I've been meaning to do this for a long long time and finally got around to it tonight. As some of you know, I'm quite fond of low-level bit banging commands using macros like this:
DDBD = B01101010;
This produces very small and fast code but it can be difficult to modify later. However, pinMode() and digitalWrite() are too bloated and slow for me to even contemplate using so they are not an option either for anything except the most quick and dirty test code. But I like their format since it is easy to see what is going on and I find digital pin numbers easier to work than PB5 for instance. Well feast your eyes on these little beauties! They are as easy to look at as the Arduino functions they replace but they only add 2 bytes of program size and they each execute in a single clock cycle since all this looking up is done at compile time rather than run time. The only efficiency I give up is you can't change more than one pin per clock cycle. Hope this helps others, I plan to fold this back into most of my code. If anyone spots a way to improve this please let me know.
// Macros to define the ports and bitmasks for the pins on the 328p succinctly
#if defined(__AVR_ATmega328P__)
#define DirRegFromPin(pin) pin<8 ? 0x0A : pin<14 ? 0x04 : 0x07 // Register which controls input or output
#define DataRegFromPin(pin) pin<8 ? 0x0B : pin<14 ? 0x05 : 0x08 // Register which controls high or low
#define BitmaskFromPin(pin) pin<8 ? 1<<pin : pin<14 ? 1<<(pin-8) : 1<<(pin-14)
#endif
// Bit-banging macros - each adds 2 bytes of sketch size and takes 1 clock cycle to execute
#define MakeOutput(pin) _SFR_IO8(DirRegFromPin(pin)) |= BitmaskFromPin(pin)
#define MakeInput(pin) _SFR_IO8(DirRegFromPin(pin)) &= ~(BitmaskFromPin(pin))
#define PullHigh(pin) _SFR_IO8(DataRegFromPin(pin)) |= BitmaskFromPin(pin)
#define PullLow(pin) _SFR_IO8(DataRegFromPin(pin)) &= ~(BitmaskFromPin(pin))
Just in case it isn't obvious, they are used like so:
MakeOutput(9);
PullHigh(9); // LED is on now
PullLow(9); // LED is off now
MakeInput(9);
or even better like so:
#define LED 9
MakeOutput(LED);
PullHigh(LED); // LED is on now
PullLow(LED); // LED is off now
MakeInput(LED);
Great, thanks for sharing those beautiful macros with us! I'll definitely use them ;D
I added a read as well. I'm not able to edit the initial post anymore so here is the latest:
#if defined(__AVR_ATmega328P__) /* Macros to define the ports and bitmasks for the pins on the 328p succinctly */
#define InpRegFromPin(pin) pin<8 ? 0x09 : pin<14 ? 0x03 : 0x06 /* Input registers for the ports */
#define DirRegFromPin(pin) pin<8 ? 0x0A : pin<14 ? 0x04 : 0x07 /* Data direction registers for the ports */
#define DataRegFromPin(pin) pin<8 ? 0x0B : pin<14 ? 0x05 : 0x08 /* Output registers for the ports */
#define BitmaskFromPin(pin) pin<8 ? 1<<pin : pin<14 ? 1<<(pin-8) : 1<<(pin-14)
#endif
/* Bit-banging macros - each adds 2 bytes of sketch size and takes 1 clock cycle to execute */
#define MakeOutput(pin) _SFR_IO8(DirRegFromPin(pin)) |= BitmaskFromPin(pin) /* Much faster and smaller version of pinMode(Pin, OUTPUT) */
#define MakeInput(pin) _SFR_IO8(DirRegFromPin(pin)) &= ~(BitmaskFromPin(pin)) /* Much faster and smaller version of pinMode(Pin, INPUT) */
#define PullHigh(pin) _SFR_IO8(DataRegFromPin(pin)) |= BitmaskFromPin(pin) /* Much faster and smaller version of digitalWrite(Pin, HIGH) */
#define PullLow(pin) _SFR_IO8(DataRegFromPin(pin)) &= ~(BitmaskFromPin(pin)) /* Much faster and smaller version of digitalWrite(Pin, LOW) */
#define ReadPin(pin) _SFR_IO8(InpRegFromPin(pin)) & (BitmaskFromPin(pin)) ? 1 : 0 /* One line if else statement using the format [test ? true return : false return] */
Just to drive the point of smaller sketch sizes home here is a comparison of blinks.
Arduino 1.8.3 Blink - 928 bytes of hex and 9 bytes of SRAM
/* the setup function runs once when you press reset or power the board */
void setup() {
/* initialize digital pin LED_BUILTIN as an output. */
pinMode(LED_BUILTIN, OUTPUT);
}
/* the loop function runs over and over again forever */
void loop() {
digitalWrite(LED_BUILTIN, HIGH); /* turn the LED on (HIGH is the voltage level) */
delay(1000); /* wait for a second */
digitalWrite(LED_BUILTIN, LOW); /* turn the LED off by making the voltage LOW */
delay(1000); /* wait for a second */
}
My Blink - 176 bytes of hex and 0 bytes of SRAM
int main(void) { /* =========== the setup function runs once when you press reset or power the board =========== */
MakeOutput(9);
for(;;) { /* =========== the loop function runs over and over again forever =========== */
PullHigh(9);
_delay_ms(1000);
PullLow(9);
_delay_ms(1000);
} /* =========== the loop function runs over and over again forever =========== */
} /* End main */