I'm working in C on an Arduino device, where the pins are labeled differently. I'm using PLAIN C though, not the Arduino "language".
Each pin is defined by it's port (eg. PORTB
) and pin (bit) in the port (eg. PB0
).
I'd like to concisely alias pins, so I could make macros or functions somewhat like what the Arduino uses:
pin_direction(D2, 1); // D2 as output
set_pin(D2, 0); // write zero to D2
pin_direction(D3, 0); // D3 as input
enable_pullup(D3, 1); // enable D3 pullup
Instead (atm) I have to use something ugly like this:
#define D0 0
#define D1 1
#define D2 2
#define D3 3
...
#define D10 2
#define D11 3
#define PORT_D0 PORTD
#define PORT_D1 PORTD
#define PORT_D2 PORTD
#define PORT_D3 PORTD
...
#define PORT_D10 PORTB
#define PORT_D11 PORTB
// the same for PIN_xx and DDR_xx
And then I can use macros to do the work:
#define sbi(port, bit) (port) |= _BV(bit)
#define cbi(port, bit) (port) &= ~ _BV(bit)
sbi(DDR_D2, D2); // D2 to output
cbi(PORT_D2, D2); // D2 to output
sbi(DDR_D3, D3); // D3 as input
sbi(PORT_D3, D3); // D3 pullup enable
Now this works, but it's very messy. Any idea how to - without a monster overhead of something like huge switch
- do this better - more like my first example? Somehow enumerate all bits and then resolve the right register on the fly?
I'm using avr-gcc with avr-libc.
Here's the solution I use.
In my util.h (common to all my AVR projects):
#define DDR_REG(port) DDR ## port
#define PORT_REG(port) PORT ## port
#define PIN_REG(port) PIN ## port
#define SET_BIT(port, bit) do { (port) |= (1 << (bit)); } while(0)
#define CLR_BIT(port, bit) do { (port) &= ~(1 << (bit)); } while(0)
#define BIT_IS_SET(port, bit) ((((uint8_t)(port)) >> ((uint8_t)(bit))) & 0x1)
#define IO_SET_INPUT_AUX(port, bit) CLR_BIT(DDR_REG(port), bit)
#define IO_SET_AS_INPUT(io) IO_SET_INPUT_AUX(io)
#define IO_SET_OUTPUT_AUX(port, bit) SET_BIT(DDR_REG(port), bit)
#define IO_SET_AS_OUTPUT(io) IO_SET_OUTPUT_AUX(io)
#define IO_OUTPUT_0_AUX(port, bit) CLR_BIT(PORT_REG(port), bit)
#define IO_OUTPUT_0(io) IO_OUTPUT_0_AUX(io)
#define IO_OUTPUT_1_AUX(port, bit) SET_BIT(PORT_REG(port), bit)
#define IO_OUTPUT_1(io) IO_OUTPUT_1_AUX(io)
#define IO_GET_INPUT_AUX(port, bit) BIT_IS_SET(PIN_REG(port), bit)
#define IO_GET_INPUT(io) IO_GET_INPUT_AUX(io)
In my pin mappings file:
#define UPBTN_IO B,7
#define DOWNBTN_IO D,0
#define ENTERBTN_IO D,1
(etc)
In code:
IO_SET_AS_INPUT(UPBTN_IO);
This relies on some fun preprocessor bits, like there only being one round of macro expansion on macro parameters.