This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to use the register definition (i.e., SYSCTL or UCSCTL3) to initialize a variable at compile time.

Other Parts Discussed in Thread: MSP430F5438

Hello,

  I am trying to figure out how to use the register definitions that are found in the MSP430xxxx header files to initialize data at compile time.  Right now, I am having to hard code the actual addresses like the following:

#define GPIO_REG(reg)        ((volatile unsigned char*)(reg))

typedef struct
{
    volatile unsigned char  *In,
                            *Out,
                            *Dir,
                            *Sel;

} GpioRegSet_t;

/* -- Dummy Register -- */
uint8_t GpioDummyReg;

GpioRegSet_t GpioRegSet[] =
{
    /*       *IN               *OUT              *DIR              *SEL  */
    {  &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg    },  // Port 0
    {  GPIO_REG(0x0200), GPIO_REG(0x0202), GPIO_REG(0x0204), GPIO_REG(0x020A) },  // Port 1
    {  GPIO_REG(0x0201), GPIO_REG(0x0203), GPIO_REG(0x0205), GPIO_REG(0x020B) },  // Port 2
    {  GPIO_REG(0x0220), GPIO_REG(0x0222), GPIO_REG(0x0224), GPIO_REG(0x022A) },  // Port 3
    {  GPIO_REG(0x0220), GPIO_REG(0x0223), GPIO_REG(0x0225), GPIO_REG(0x022B) }   // Port 4
};

Ideally, I would love just to do the following:

GpioRegSet_t GpioRegSet[] =
{
    /*       *IN               *OUT              *DIR              *SEL  */
    {  &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg    },  // Port 0
    {  P1IN,             P1OUT,            P1DIR,            P1SEL            },  // Port 1
    {  P2IN,             P2OUT,            P2DIR,            P2SEL            },  // Port 1
    {  P3IN,             P3OUT,            P3DIR,            P3SEL            },  // Port 1
    {  P4IN,             P4OUT,            P4DIR,            P4SEL            },  // Port 1
};

But this will not work as I will get a compiler error of "#28 expression must have a constant value".  For one example, P1IN is defined as:

#define P1IN                   (PAIN_L)       /* Port 1 Input */

Which is defined as:

SFR_8BIT(PAIN_L);                             /* Port A Input */

where SFR_8BIT(x) is defined as

#define SFR_8BIT(address)   extern volatile unsigned char address

I image the tool chain is filling in the addresses based upon the particular processor which is probably happening a link time (????), but I am not sure.  Is there a way I can use these definition to initialize variable and arrays, if so how?

Oh, almost forgot, I am using Code Composer Studio v6.0.1

Thanks,

Mark

  • I do not understand what you are asking.
    "How to use the register definition (i.e., SYSCTL or UCSCTL3) to initialize a variable at compile time."

    The addresses of registers are known via the definitions at compile time. But their contents are not.
    The addresses or stack positions are assigned at compile time. But their initialization are not.
    SYSCTL or UCSCTL3 do not seem to have anything to do with what you have shown.
  • There was a typo in my previous reply. It is corrected here.

    I do not understand what you are asking.

    "How to use the register definition (i.e., SYSCTL or UCSCTL3) to initialize a variable at compile time."

    The addresses of registers are known via the definitions at compile time. But their contents are not.

    The addresses or stack positions of variables are assigned at compile time. But their initialization are not.

    SYSCTL or UCSCTL3 do not seem to have anything to do with what you have shown.

  • Accessing a Port via a variable address pointer can be handy sometimes.

    I’m using therefore the following code, maybe not exactly what you want but maybe it helps you and this is without error’s; 

    typedef struct VarPortBase {
    	unsigned char IN_;
    	unsigned char OUT_;
    	unsigned char DIR_;
    	unsigned char IFG_;
    	unsigned char IES_;
    	unsigned char IE_;
    	unsigned char SEL_;
    	unsigned char REN_;
    } VarPortBase;
    
    
    void main (void)
    {
    	WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    
    	VarPortBase* pPort;
    
    	pPort = (VarPortBase*)&P1IN;
    
    	pPort->DIR_ = BIT1;
    	pPort->OUT_ = BIT1;
    
    }
    

  • Mark Hicks said:
    I image the tool chain is filling in the addresses based upon the particular processor which is probably happening a link time (????), but I am not sure.  Is there a way I can use these definition to initialize variable and arrays, if so how?

    The peripheral registers are defined as external volatile variables to the compiler, which the linker script places at the fixed address for the peripheral with a device specific file included at the end of the linker command file. E.g. at the end of the linker command file for a MSP430F5438:

    /****************************************************************************/
    /* Include peripherals memory map                                           */
    /****************************************************************************/
    
    -l msp430f5438.cmd
    

    Therefore, to initialize a structure with the address of a peripheral register, you just need to take the address of the peripheral register. E.g. the following simple LED flashing program was created for a MSP430F5438:

    #include <msp430.h> 
    #include <stdint.h>
    
    typedef struct
    {
        volatile unsigned char  *In,
                                *Out,
                                *Dir,
                                *Sel;
    
    } GpioRegSet_t;
    
    /* -- Dummy Register -- */
    uint8_t GpioDummyReg;
    
    GpioRegSet_t GpioRegSet[] =
    {
        /*       *IN               *OUT              *DIR              *SEL  */
        {  &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg,    &GpioDummyReg    },  // Port 0
        {  &P1IN,            &P1OUT,           &P1DIR,           &P1SEL           },  // Port 1
        {  &P2IN,            &P2OUT,           &P2DIR,           &P2SEL           },  // Port 2
        {  &P3IN,            &P3OUT,           &P3DIR,           &P3SEL           },  // Port 3
        {  &P4IN,            &P4OUT,           &P4DIR,           &P4SEL           },  // Port 4
        {  &P5IN,            &P5OUT,           &P5DIR,           &P5SEL           },  // Port 5
        {  &P6IN,            &P6OUT,           &P6DIR,           &P6SEL           },  // Port 6
        {  &P7IN,            &P7OUT,           &P7DIR,           &P7SEL           },  // Port 7
        {  &P8IN,            &P8OUT,           &P8DIR,           &P8SEL           },  // Port 8
        {  &P9IN,            &P9OUT,           &P9DIR,           &P9SEL           },  // Port 9
        {  &P10IN,           &P10OUT,          &P10DIR,          &P10SEL          },  // Port 10
    };
    
    /*
     * main.c
     */
    int main(void) {
        WDTCTL = WDTPW | WDTHOLD;	// Stop watchdog timer
    
        /* Toggle LED1 on P10.7 and LED2 on P10.6 */
        *GpioRegSet[10].Dir = BIT6 | BIT7;
        *GpioRegSet[10].Out = BIT6;
        for (;;)
        {
            (*GpioRegSet[10].Out) ^= BIT6 | BIT7;
            __delay_cycles (400000);
        }
    	
    	return 0;
    }
    

  • Chester,

    Thanks, your answer makes perfect sense. In the past I had the need to use "elements" within the sections area of a linker script (Green Hills). For example, the sections might define the starting and ending of RAM which I would use the elements to be passed to a function that performs some RAM testing. In order to use the elements in the section I also needed to take the address of the element.

    Thanks!

    regards,
    Mark
  • The original question sounds as if the purpose is to define a hardware configuration that is automatically loaded (initialized) at program startup.
    This is not possible. Hardware registers are memory.-mapped but their content won't survive a power cycle. So the init value needs to be copied to them after power-up. The C startup code copies a block of init values (for global vars) to RAM but isn't designed to copy sets of init values to arbitrary memory locations (the hardware registers).
    You need to do it manually at begin of main. From constant structs.
    If you'd locate these structs at the position of the hardware registers and provide initializers, then the linker ill create binary output for these locations. And on program download, the debugger will load these values to the hardware registers. And on next power cycle, all is gone. You need to put them in flash (the 'const' keyword does it for you) and copy them over at beginning of main.
    GRACE does it the same way (but with automatically generated code) for the register settings you make in the GRACE IDE.

**Attention** This is a public forum