In my project, I use a few basic macros for GPIO. The goal is, that I can easily redefine pin assignment in a central location without compromising performance or code size.
The macros (gpiomacros.h):
// MSP430 gpio macros
#define GPIO_SEL(port) P ## port ## SEL
#define GPIO_DIR(port) P ## port ## DIR
#define GPIO_OUT(port) P ## port ## OUT
#define GPIO_IN(port) P ## port ## IN
#define GPIO_IS_INPUT(port,pin) { GPIO_SEL(port) &= ~(pin); GPIO_DIR(port) &= ~(pin); }
#define GPIO_IS_OUTPUT(port,pin) { GPIO_SEL(port) &= ~(pin); GPIO_DIR(port) |= (pin); }
#define GPIO_IS_PERIPHERAL_IN(port,pin) { GPIO_SEL(port) |= (pin); GPIO_DIR(port) &= ~(pin); }
#define GPIO_IS_PERIPHERAL_OUT(port,pin) { GPIO_SEL(port) |= pin; GPIO_DIR(port) |= (pin); }
#define GPIO_SET(port,pin) { GPIO_OUT(port) |= (pin); }
#define GPIO_CLEAR(port,pin) { GPIO_OUT(port) &= ~pin; }
#define GPIO_READ(port,pin) ( GPIO_IN(port) & (pin) )
In a central configuration file (e.g. hardware.h) I assign pins like this:
// Pin assignment #define LED1_PIN BIT1 #define LED1_PORT 6 #define LED2_PIN BIT0 #define LED2_PORT 1
And then in the code I interact with GPIO like this:
// Setup LEDs GPIO_IS_OUTPUT(LED1_PORT, LED1_PIN); GPIO_IS_OUTPUT(LED2_PORT, LED2_PIN); // Turn off LEDs GPIO_CLEAR(LED1_PORT, LED1_PIN); GPIO_CLEAR(LED2_PORT, LED2_PIN);
The macros are resolved in two steps:
1. Higher level "functions" define the commands. E.g. GPIO_SET(), GPIO_IS_OUTPUT(), ..
2. Lower level macros used within those functions translate port, pin to a register. E.g. GPIO_IN(), GPIO_SEL(), ..
The end result is code like you would write when directly working with the GPIO registers. E.g. P2OUT &= ~BIT0; Note that this translation is done by the C pre-processor before the code is compiled.
This all works fine and dandy, with the exception of port J. Port J doesn't have a SEL register, which breaks the 1st half of the GPIO_IS_OUTPUT and GPIO_IS_INPUT macros. I currently work around this by adding special GPIO_IS_OUTPUT/INPUT_J macros, but then the main code needs to include some logic to invoke the proper macro.
#if (LED2_PORT == J) GPIO_IS_OUTPUT_J(LED2_PORT, LED2_PIN); #else GPIO_IS_OUTPUT(LED2_PORT, LED2_PIN); #endif
Any ideas, how I could include a condition inside macros, that checks whether the port is J, and if so excludes the GPIO_SEL command?
And yes, I could probably use C++ templates with identical results and an easy workaround for port J, but I'd like to avoid migrating my plain old C project.