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.

Control Suite GPIO_WritePin functions

Other Parts Discussed in Thread: TMS320F28075

In migrating from earlier Piccolo mcus to the TMS320F28075, I've noticed a number of changes in the how the chip is used in the Control Suite source files.  One example of this is that there are now dedicated functions for writing to and reading GPIO pins (namely GPIO_ReadPin and GPIO_WritePin in "F2807x_Gpio.c").  This actually seems like a much better way of accessing GPIOs than the previous method of having a number of GPIO structures for groups of GPIOs (A, B, C, etc.).  However, when inspecting the source code, it seems like a lot of computation for simply setting a GPIO (see write routine below).

volatile Uint32 *gpioDataReg;
Uint32 pinMask;

gpioDataReg = (volatile Uint32 *)&GpioDataRegs + (pin/32)*GPY_DATA_OFFSET;
pinMask = 1UL << (pin % 32);

if (outVal == 0)
gpioDataReg[GPYCLEAR] = pinMask;
else
gpioDataReg[GPYSET] = pinMask;

I rewrote the write and read routines as macros so that most of the routine could be handled by the preprocessor.  Some timing tests using an oscilloscope to set and immediately clear a GPIO show that the write macro sets and clears in 16.8ns (vs. 324ns with the function calls) with an optimization setting of 2.  Is there any reason why TI didn't opt for macros instead of functions for this?

#define GPIOREG(PIN) ((volatile Uint32 *)&GpioDataRegs + ((PIN / 32) * GPY_DATA_OFFSET))
#define PINMASK(PIN) (1UL << ((PIN) % 32))
#define GPIO_WRITE(PIN, VAL) if(VAL == 0) GPIOREG(PIN)[GPYCLEAR] = PINMASK(PIN); \
                                                 else GPIOREG(PIN)[GPYSET] = PINMASK(PIN)

  • Hi Benjamin,

    I don't think there's any specific reason TI was trying to avoid using macros for this. However, functions are a little easier to read and debug, so perhaps for demonstration/teaching purposes, macros weren't the preferred option.

    There's no doubt that writing it as you have is much faster. Thanks for sharing it with the forum.

    Whitney

  • Hi Whitney, Benjamin,

    please excuse me if my question seems inadequate, but I am trying to set/clear some GPIO channels inside a function, getting the GPASET.bit names from an array in a loop.

    I found an example in:
    HRPWM_PrdUpDown_SFO_V8.c (F2837xD Device HRPWM SFO V8 High-Resolution Period)
    which make this, getting the different EPwmxRegs via a for{} -loop:

    #define PWM_CH 9 // # of PWM channels
    //
    // Array of pointers to EPwm register structures:
    // *ePWM[0] is defined as dummy value not used in the example
    //
    /*###############################################################################################
    Please notice: There is a mistake in this structure: &EPwm1Regs is defined twice, instead of EPwm0Regs
    ###############################################################################################*/

    volatile struct EPWM_REGS *ePWM[PWM_CH] =
    { &EPwm1Regs, &EPwm1Regs, &EPwm2Regs, &EPwm3Regs, &EPwm4Regs, &EPwm5Regs, &EPwm6Regs, &EPwm7Regs, &EPwm8Regs};

    void HRPWM_Config(period)
    {
    Uint16 j;

    for(j=1; j<PWM_CH; j++)
    {
    (*ePWM[j]).TBCTL.bit.PRDLD = TB_SHADOW; // set Shadow load
    // etc
    }
    }

    I wonder if I can use the following code to set (or clear) the GPASET.bits like this:

    #define GPIO_CH 8 // # of GPIO pins
    #define SETIT 1
    #define CLEARIT 0

    volatile struct GPIO_PINS *GPIO[GPIO_CH] =
    { &GPIO0, &GPIO3, &GPIO8, &GPIO23, &GPIO24, &GPIO35, &GPIO66, &GPIO76 };

    void GPIO_Config()
    {
    Uint16 j;

    for(j=1; j<GPIO_CH; j++)
    {
    GpioDataRegs.GPASET.bit.(*GPIO[j]) = SETIT;
    // etc
    }
    }

    best regards from Germany!
  • Gustavo,

    Which device are you using?

    I don't think that is going to work because the compiler isn't going to resolve "GPIOx" to an address. If you were using the driverlib functions then you could have an array of integers that you pass to the GPIO_WritePin function for example.

    Regards,
    Kris
  • Hi Kris,
    thanks for the quick answer!
    I am using the F28379D. Not using the driverlib functions, because my application is very time sensitive.
    But, are you saying, I should stick to the routine described by Benjamin in the start of this thread?

    What I do not get is why this example above works resolving for "EPwmxRegs" (in my case "GpioDataRegs") and my modification for "GPIOx pin" should not.
    What is the substantial difference for the compiler between resolving for either addresses?
    somehow is my knowledge not yet sufficient to deal with this....
    :(
  • As Kris mentioned, "GPIOx" is actually a single bit in a register, not a memory location with a unique address. In your case GpioDataRegs.GPASET is actually a 32-bit register. You can access this register by dereferencing its associated address. The GPIOs, however, are the 32-bits that make up the register itself. So the reason you cannot dereference them individually is because they all share the same memory address. If you are trying to set the GPIOs quickly, you can set any combination of GPIOs in the same register simultaneously (e.g. GpioDataRegs.GPASET.all = 0xA34F9384). If you need this to execute in a for loop with an index, then I would suggest using macros.

  • Exactly what Benjamin said. When you do a ".bit.GPIOx" write what happens under the hood is the compiler turns this into a Read Modify Write instruction at the GPASET address (for example). This involves generating a mask for the bit you are trying to write ( based on the bit position ) and writing it back to the whole GPASET register. The most efficient method is to write the whole GPASET register as Benjamin demonstrated. The difficulty with this in your application is that the GPIOs are in different ports (GPASET, GPBSET, etc) so you would need to maintain a value for each of these. It's certainly possible- just not trivial. I suggested the driver method just for simplicity, but if it's time critical it is probably better to develop a more complex routine.

    Just thinking through this for a few minutes, one thing you could probably do is an array of GPxSET registers. I promise this syntax isn't correct, but just for the general idea:

    my_array[] {&GpioDataRegs.GPASET, &GpioDataRegs.GPBSET, &GpioDataRegs.GPCSET, etc....}

    Then in your code you could divide your GPIO number by 32 to figure out the port (0 -> A, 1->B, 2->C) and get the array index. Then you could use the remainder of the GPIO number divided by 32 (this will tell you the bit position of your desired GPIO, so shift a '1' that number of places) to calculate a value to write to the my_array[index].

    That sounds complicated, but it's a pretty small number of instructions. *Disclaimer, this is just a theory :)

    Regards.

    Kris

  • Hi Kris, Hi Benjamin,

    you are great, thanks!

    Both your posts helped me further to understand the information Ti provides (which is excellent, if you can see the things you are looking for).

    Since I am new in this task of programming micro-controllers, every step is a kind of space trip :)

    Following your remarks, I will for the time being simplify my use of the ports. In this way, I could solve the issue by creating an array with the pin numbers in use as hex values and talking to the GPIOSET.all setting  the pins as needed, or clearing them with GPIOCLEAR.all. The experiment you suggested, Kris, I will sleep long nights over it, and will come to you later to tell you how it went.

    Just to make sure, my code would do this, using pins GPIO0 to GPIO5, GPIO16 and GPIO24, all in the A section:

    Uint16 myGPIOpins[8] = {0x1,0x2,0x4,0x8,0x10,0x20,0x10000,0x1000000};

    // or like this, might be ?
    // Uint16 myGPIOpins[8] = {1b,10b,100b,1000b,10000b,100000b,10000000000000000b,1000000000000000000000000b};

    void SetAllPins()   // to set the pins, and the same in another function for GPACLEAR
    {

        Uint16 j;

        for(j=0; j<8; j++)

        {

            GpioDataRegs.GPASET.all = myGPIOpins[j];

        }

    }

    I know, this is far from inventing the Wheel, but my father always told me not to do it.

    Many, many thanks and a very nice Sunday for you!
    Gustavo

  • Sorry Kris, Benjamin,
    I realize now, you might be thinking I do not read your posts properly.
    It is for me clear that I can write to several pins at once by choosing the appropriate number.
    Actually, the loop is a way for me to figure out how to write to the pins in a secuence.
    In "my" real world there will be other steps in the loop and this is why I use it to write only to one pin at the time.
    Best regards from a very, very rainy Germany