port I/O abstraction macros



Everyone,

Earlier I posted some macros that allow you to easily define binary
constants in C (see http://cprog.tomsweb.net/binconst.txt). Playing
similar tricks with the preprocessor, I now created some macros that
allow you to make easy abstraction of port I/O's. It's written for the
MSP430 but the concept can be applied to other embedded architectures.

You simply define your signal location like:
#define LED P1,0
and then use them like:
SETDIR(LED);
CLR(LED);
TOGGLE(LED);

The obvious advantage, besides readability, is that in the next
hardware design or layout iteration - when most of the signals have
shifted to different pins on different ports - all you need to change
is one simple and readable #define in a central location.

Header file (or http://cprog.tomsweb.net/portio.txt):

/* Port I/O abstraction macros - by Tom Torfs, 2006
* MSP430 implementation
* Donated to the public domain. No warranties whatsoever.
*
* These macros allow you to make easy abstraction of I/O port & pin
numbers.
* You simply define your signal location like:
* #define LED P1,0
* and then use them like:
* SETDIR(LED);
* CLR(LED);
* TOGGLE(LED);
* ...
*
* The obvious advantage, besides readability, is that in the next
* hardware design or layout iteration - when most of the signals have
* shifted to different pins on different ports - all you need to
change
* is one simple and readable #define in a central location
*
* In addition, nothing stops you from porting this header to other
* microcontroller families. The specifics such as direction and
interrupt
* registers may be different but the basic concepts hold.
*
* Overview of user macros:
* - Reading from port registers: (these are meant to be used in
expressions)
* x = IN(signal), OUTBUF(signal), DIR(signal), SEL(signal),
IE(signal), IES(signal), IFG(signal)
* - Writing to port registers: (these are meant to be used as
statements)
* output bit manipulation: SET(signal); CLR(signal); TOGGLE(signal);
* direction bit manipulation: SETDIR(signal); CLRDIR(signal);
* special function bit manipulation: SETSEL(signal); CLRSEL(signal);
* interrupt bits manipulation: SETIE(signal); CLRIE(signal);
SETIES(signal); CLRIES(signal);
* interrupt flag manipulation: SETIFG(signal); CLRIFG(signal);
* copy 1 or 0 into output bit: CPYBIT(signal,bitvalue)
* - Getting the port name (string) or bit number (integer literal)
corresponding to a signal:
* const char s[] = PORTNAME(name);
* x = BITNUM(name);
* - Interrupt vector for port corresponding to a signal: (requires
compiler-specific mapping)
* INTVECTOR(name)
*/

/********** HELPER MACROS **********/

#define POUT_(Pn) Pn##OUT
#define PIN_(Pn) Pn##IN
#define PDIR_(Pn) Pn##DIR
#define PSEL_(Pn) Pn##SEL
#define PIE_(Pn) Pn##IE
#define PIES_(Pn) Pn##IES
#define PIFG_(Pn) Pn##IFG

#define IN_(p,b) (PIN_(p)&(1<<b))
#define OUTBUF_(p,b) (POUT_(p)&(1<<b))
#define DIR_(p,b) (PDIR_(p)&(1<<b))
#define SEL_(p,b) (PSEL_(p)&(1<<b))
#define IE_(p,b) (PIE_(p)&(1<<b))
#define IES_(p,b) (PIES_(p)&(1<<b))
#define IFG_(p,b) (PIFG_(p)&(1<<b))

#define SET_(p,b) POUT_(p)|=(1<<(b))
#define CLR_(p,b) POUT_(p)&=~(1<<(b))
#define TOGGLE_(p,b) POUT_(p)^=(1<<(b))
#define SETDIR_(p,b) PDIR_(p)|=(1<<(b))
#define SETSEL_(p,b) PSEL_(p)|=(1<<(b))
#define SETIE_(p,b) PIE_(p)|=(1<<(b))
#define SETIES_(p,b) PIES_(p)|=(1<<(b))
#define SETIFG_(p,b) PIFG_(p)|=(1<<(b))
#define CLRDIR_(p,b) PDIR_(p)&=~(1<<(b))
#define CLRSEL_(p,b) PSEL_(p)&=~(1<<(b))
#define CLRIE_(p,b) PIE_(p)&=~(1<<(b))
#define CLRIES_(p,b) PIES_(p)&=~(1<<(b))
#define CLRIFG_(p,b) PIFG_(p)&=~(1<<(b))

#define PORTNAME_(p,b) #p
#define BITNUM_(p,b) b

#define INTVECTOR_(p,b) INTVECTOR_##p

/********** USER MACROS **********/

/* for reading from port registers
note: these return 0 or nonzero, not 0 or 1
if necessary, use as: x = IN(signal) ? 1 : 0;
not necessary for normal use in if() etc. */
#define IN(name) IN_(name)
#define OUTBUF(name) OUTBUF_(name)
#define DIR(name) DIR_(name)
#define SEL(name) SEL_(name)
#define IE(name) IE_(name)
#define IES(name) IES_(name)
#define IFG(name) IFG_(name)

/* for writing to port registers */
#define SET(name) SET_(name)
#define CLR(name) CLR_(name)
#define TOGGLE(name) TOGGLE_(name)
#define SETDIR(name) SETDIR_(name)
#define SETSEL(name) SETSEL_(name)
#define SETIE(name) SETIE_(name)
#define SETIES(name) SETIES_(name)
#define SETIFG(name) SETIFG_(name)
#define CLRDIR(name) CLRDIR_(name)
#define CLRSEL(name) CLRSEL_(name)
#define CLRIE(name) CLRIE_(name)
#define CLRIES(name) CLRIES_(name)
#define CLRIFG(name) CLRIFG_(name)

#define CPYBIT(name,bit) ((bit) ? (SET_(name)) : (CLR_(name)))

/* if you want to know the port (string) or the bit number (integer
literal) */
#define PORTNAME(name) PORTNAME_(name)
#define BITNUM(name) BITNUM_(name)

/* interrupt vector for port */
#define INTVECTOR(name) INTVECTOR_(name)

/* mapping to compiler-specific interrupt vector names/numbers
(this is for mspgcc, adapt for your compiler) */
#define INTVECTOR_P1 PORT1_VECTOR
#define INTVECTOR_P2 PORT2_VECTOR

/* USAGE EXAMPLE:

- define your port I/O's as follows: (note you MUST use a #define,
not an enum)
#define LED P1,0
#define INPUTSIGNAL P2,1
#define ANALOGSIGNAL P6,3
#define INTSIGNAL P2,2
#define SIGBIT0 P3,7
#define SIGBIT1 P3,6
#define SIGBIT2 P3,5
#define UNUSED1 P1,5
#define UNUSED2 P1,6
#define UNUSED3 P1,7

- examples of usage:
SETDIR(LED); CLR(LED); TOGGLE(LED);
CLRDIR(INPUTSIGNAL); x = IN(INPUTSIGNAL);
SETSEL(ANALOGSIGNAL);
SETIE(INTSIGNAL); CLRIE(INTSIGNAL);
CPYBIT(SIGBIT0,x&1); CPYBIT(SIGBIT1,x&2); CPYBIT(SIGBIT2,x&4);
SETDIR(UNUSED1); SETDIR(UNUSED2); SETDIR(UNUSED3); // set to output to
save power
printf("The LED is on port %s, bit %d.\n", PORTNAME(LED),
BITNUM(LED));
ADC12MCTL0 = SREF_0 | BITNUM(ANALOGSIGNAL);

- example interrupt routine (compiler-specific syntax)
interrupt(INTVECTOR(INTSIGNAL)) int_func(void)
{
if (IFG(INTSIGNAL))
{
// do something...
CLRIFG(INTSIGNAL); // clear interrupt flag
}
}
*/

.



Relevant Pages

  • Re: Macro Substitution Help Request
    ... arguments" when compiling with Imagecraft ICCAVR. ... I have written a set of macros that can be used to logically set or reset a specified port bit. ... Other code that you might want to include in your macro is saving output bytes/words in a "shadow" array, so that if you cannot read the output port directly, it will combine the new output bitwith the current value of other bits in the output port. ...
    (comp.lang.c)
  • Re: macros that build macros
    ... The macros produce code in support of type-safe inter-process message ... There were for digital I/O ports, ... port, PIN for reading input, PORT for writing output, and DDR (Data ... If the hardware changes, and the status LED moved to port B, pin 3, ...
    (comp.lang.c)
  • Re: Are _T() and TEXT() macros equivalent?
    ... but I'm guessing it wouldn't matter. ... You could easily define these macros in any port you did and there are so many other things that would cause you grief doing a port that this would likely not be top on your pain list. ...
    (microsoft.public.vc.mfc)
  • Re: Gimp port broke?
    ... Don't mean to steal your spot light but here's the errors I got this ... always use macros to include FreeType header files." ... > The log of the port build is 1.1MB and will be sent upon request. ...
    (freebsd-questions)
  • Re: EPROM to RS232
    ... If you can deal with a parallel port, ... provides signals at 5V. ... One problem is that using a 555 for the clock is going to be tricky, ... the counters to address the EPROM. ...
    (sci.electronics.basics)