Jump to content
chicken

Basic MSP430 GPIO Macros

Recommended Posts

Interesting stuff. I was thinking about this only yesterday, primarily because I have a project that I switch between a F5529LP dev board and a F5510 final PCB. That means it's helpful to be able to switch pins and ports with some sections like this

 

#if defined (__MSP430F5510__)
#define LED_RED_PORT	P2OUT
#define LED_RED_PIN 	BIT1
#endif

#if defined (__MSP430F5529__)
#define LED_RED_PORT P1OUT
#define LED_RED_PIN BIT0
#endif

Usage:
  LED_RED_PORT |= LED_RED_PIN;
This simple stuff is all fine, but gets messy when using interrupts and needing to define multiple PxDIR, PxIN, PxOUT, PxSELn, PxIE, etc.

 

Then I started doing this sort of thing, but it started to feel a bit wrong

#define IRQ_ON P2IE |= IRQ_PIN

Usage:
  if (something)
    IRQ_ON;
I might just use the parameterized macros with ## such as GPIO_SEL(x) so I only have to define the port once. I knew I'd seen something similar once, but couldn't remember where or when. Thanks for reminding me.

Share this post


Link to post
Share on other sites

Anyone know what I'm doing wrong here?

#define LED_PORT    1
#define PxOUT(port) P##port##OUT

PxOUT(LED_PORT) ^= 0x01;
I get a compilation error - identifier "PLED_PORTOUT" is undefined.

 

I can see that the value that I pass to the parameterized macro is taken literally rather than being itself defined as 1. How do I tell the preprocessor to make the substitution?

 

My code looks similar to that above. I've tried changing the ordering, whitespace, throwing in brackets. I can't seem to get it to work.

Share this post


Link to post
Share on other sites

Ah - Just after I posted I managed to find an answer myself although it's not elegant. It seems that to expand again you need another level of redirection.

http://stackoverflow.com/questions/12630548/c-expand-macro-with-token-pasting

 

This works because of the first rather pointless looking line:

#define PxOUT(port)   PxOUT_(port)
#define PxOUT_(port)  P##port##OUT
#define LED_PORT      1

PxOUT ( LED_PORT ) ^= 0x01;
If anyone knows a slightly neater solution then let me know, but this will do.

Share this post


Link to post
Share on other sites

 

Welcome to the preprocessor. I don't know of any more elegant solution as parameters are literal, though there may be one. The second level forces the parameter to be seen as a token itself and be substituted. The initial version did not let the token be seen on its own.

 

I learned the power, and limitations, or macro expansion with asmh on MVS, writing a 12 liner to expand to the twelve days of xmas for a class. The C/C++ preprocessor is more capable, but is still not doing full featured regeps.

 

 

If anyone knows a slightly neater solution then let me know, but this will do.

Share this post


Link to post
Share on other sites

As all gio registers start with port and then OUT, IN, IE etc, example: P1OUT, with some macro word scrambling you can do this:

#define read &           // words for common gpio interaction
#define clear &= ~
#define set |=
#define toggle ^=

#define Button1(y,x)    (P1 ##x y BIT0)       // PORT and PIN (ignore the ##x y)
#define BlueLED(y,x)    (P2 ##x y BIT5) 

To use them:
BlueLED(set,DIR);                                 
BlueLED(clear,OUT);

Button1(clear,DIR);                       // a input
Button1(set,OUT);                         // pull-up resistor
Button1(set,REN);                          
Button1(set,IES);                         // falling edge
button1(clear,IFG);
Button1(set,IE);                            

Share this post


Link to post
Share on other sites

This is how I do my GPIO initialization. It doesn't handle any later GPIO accesses, but it's a nice table, and smaller and faster than configuring the bits one by one.

/* use 0 or 1 to set the output level */
#define OUTPUT        0x00    /* default */
#define INPUT         0x02
#define PULL_DOWN     0x04
#define PULL_UP       0x05
#define REDUCED_DRIVE 0x00    /* default */
#define FULL_DRIVE    0x08
#define GPIO          0x00    /* default */
#define PERIPHERAL    0x10

struct digital_io_init_f5529 {
    u8 P1[8];
    u8 P2[8];
    u8 P3[8];
    u8 P4[8];
    u8 P5[8];
    u8 P6[8];
    u8 P7[8];
    u8 P8[3];
    u8 PJ[4];
};

static void init_port(const u8 *init, unsigned int count, uint16_t int base_address)
{
    uint16_t out = 0, dir = 0, ren = 0, ds = 0, sel = 0;
    uint16_t bit;

    for (bit = 1; count > 0; init++, bit <<= 1, count--) {
        if (*init & 1)          out |= bit;
        if (!(*init & INPUT))   dir |= bit;
        if (*init & PULL_DOWN)  ren |= bit;
        if (*init & FULL_DRIVE) ds  |= bit;
        if (*init & PERIPHERAL) sel |= bit;
    }
    HWREG16(base_address + OFS_PAOUT) = out;
    HWREG16(base_address + OFS_PADIR) = dir;
    HWREG16(base_address + OFS_PAREN) = ren;
    HWREG16(base_address + OFS_PADS ) = ds;
    HWREG16(base_address + OFS_PASEL) = sel;
}

static void init_ports(const struct digital_io_init_f5529 *init)
{
    init_port(init->P1, 8 + 8, PA_BASE);
    init_port(init->P3, 8 + 8, PB_BASE);
    init_port(init->P5, 8 + 8, PC_BASE);
    init_port(init->P7, 8 + 3, PD_BASE);
    init_port(init->PJ, 4,     PJ_BASE);
}

void init()
{
    init_ports(&(const struct digital_io_init_f5529){
           .P4[4] = PERIPHERAL | OUTPUT,
           .P4[5] = PERIPHERAL | INPUT,
           .P7[0] = GPIO | INPUT | PULL_UP,
           .P7[1] = GPIO | OUTPUT | FULL_DRIVE,
           /* ... */
    });
}

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×