avrutil.h

freely define pin function and location independent of grouping into ports

I wrote this code because I often found myself in situations where I needed to code a general routine that would work on any AVR microcontroller with any configuration of external signals. I could define which pins my signals woud be assigned to, but then they could change port on another hardware design. So I had to also define a port. But then if I had to toggle several signals simultaneously, I couldn't group them into one port update because I didn't know if they belonged on the same port on every design. Of course, I could toggle them one at a time, but because PORTn are defined as *((volatile u08*)(0xValue)), this couldn't be optimized into just one register access where possible. This wastes code space and CPU cycles. So I wrote some macros which use temporary local variables that hold which port registers need to be modified and where. These variables should be optimized away by the compiler and shouldn't occupy any memory space. Usage is something like:

BEGIN_PORT_UPDATE;
SPBN(PORTB, 6) //can use the old style
CPBN(PORT_SOME_CHIP_SELECT, PIN_SOME_CHIP_SELECT) //or alias it
SPB(SOME_OTHER_CHIP_SELECT) //but use this
EPB(SOME_READ_ENABLE)
//or this, which accounts for polarity
DPB(SOME_WRITE_ENABLE)
END_PORT_UPDATE;

The macros stand for set/clear port bit n, set/clear port bit (function) enable/disable port bit (function). For the last two, PORT_foo, PIN_foo and POL_foo need to be #defined, meaning the port, pin number and polarity of the signal named foo.
Curiously enough, it optimizes well even if the bits to be set/cleared depend on a runtime value (all praise gcc):

a = some_value_calculated_at_runtime;
BEGIN_PORT_UPDATE;
if (a) {
SPBN(PORTB, 1)
SPBN(PORTC, 2)
EPB(SOME_OUTPUT)
DPB(OTHER_OUTPUT)
}
else {
CPBN(PORTB, 1)
CPBN(PORTB, 3)
SPBN(PORTD, PD6)
DPB(SOME_OUTPUT)
}
EPB(BLA)
END_PORT_UPDATE;

The optimization appears to screw up if compiled with -freorder-blocks (it's off by default). Always verify asm output. Tip: if asm output is too long, precede relevant code with foo(); then search. E.g. edit avrutil.h and insert foo(); in BEGIN_PORT_UPDATE.

For now it only works with ports A-F if available, but could be extended to work with any SFR / memory location. If some port is not implemented on a certain microcontroller, it's defined as null. This may be bad (no warning if accidentally accessing an inexistant port now), but makes the code more clear and a lot more #ifdef-free. Because PORTs are defined as dereferenced volatile pointers so you can write PORTA = 69, this provides an interesting example of when you actually need to define a macro that dereferences a null pointer. Maybe I'll fix it in a future release, or (you can) write a script that checks asm output for null accesses if at all possible. Anyway, this is a non-issue, because if using this header, there shouldn't be a single PORT, DDR or PIN in your program, only PORT_bla, PIN_bla and POL_bla definitions in a global header, so now you have a clean separation between the hardware configuration and the software functionality.

Later edit:
* added macros to enable or disable hi-Z (set pins as in or out). Explicitly enable or disable pullups with SPB/CPB, please.
* added same macros with a _NOW suffix, which perform the bit operation immediatly and don't need to be enclosed in begin and end tags. There's also a RPB_NOW (absolute value) and QPB_NOW (polarity-aware) for reading pins. See source.

So here is the code, released under GPLv2.