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.

SW-EK-TM4C123GXL: GPIOPinWrite: Does HWREG need to be passed a uint32_t?

Part Number: SW-EK-TM4C123GXL

I've an interrupt clocked at 100kHz - so need it to be as efficient (e.g. minimum clocks) as possible.

So I changing the calls to libraries, to direct writes to the memory mapped registers. This basically just removes the checking in the library that the parameters are correct.

One thing confuses me. In GPIOPinWrite we have:

    HWREG(ui32Port + (GPIO_O_DATA + (ui8Pins << 2))) = ui8Val;

but in hw_types.h HWREG is defined as:

#define HWREG(x)                                                              \
        (*((volatile uint32_t *)(x)))

So doesn't this say that its a pointer to a uint32_t; so why is GPIOPinWrite putting a uint8_t into memory address? So should HWREGB be used - or am I misunderstanding the code?

  • OK - read the CPU data sheet carefully last when I could sleep. Think I understand whats going on.

    The GPIO_DATA starts at ui32Port and is 0x400 bytes long, but actually thats 0x100 32 bit words. Each word, the 24 highest bits are reserved, and you can only set the 8 least significant bits. Now for the code the writes it - expand the above code to get

    (*((volatile uint32_t *)(ui32Port + (GPIO_O_DATA + (ui8Pins << 2))))) = (uint32_t) ui8Val;

    And what I've done is explicitly written the cast to a uint32_t, its implied in the original but not show. So the cast sets the highest 24 bits to zero, and these set the reserved bits, the ui8Val goes in the lowest 8 bits. the <<2 just means as we cycle through pins, we jump forward 4 bytes (equals one 32bit word).

    But then maybe the code  should be,

    (*((volatile uint8_t *)(ui32Port + (GPIO_O_DATA + (ui8Pins << 2))))) = (uint8_t) ui8Val;

    That just sets the lowest 8 bits, and doesn't touch the reserved bits. But it assumes that the 32bit number is little endian, rather than letting the compiler decide.

    Now it gets more tricky when I pass the ui8Val = -1; e.g. just set all GPIO pins that are named in ui8Pins. When that goes via a uint8_t it just becomes 0xff - and thats good. But if I get cast by the 32 bit number it becomes 0xffffffff - and puts 1s in the reserved bits. And am I allowed to do that?

    So I'm unclear which is the clearest coding (going via the HWREG defines in either case ...

  • Hi David,

      For GPIOPinWrite(), TivaWare is trying to implement the bit set capability of individual pins. Please see below description or refer to the same section in your respective device datasheet. 

  • Yes that was the document I read, that said the data you had to write was only 8 bits wide, as can only write to 8 pins on each Port. So if only need to write 8 bits, should HWREGB ([e.g. (*((volatile uint8_t *)(x))) ] be used? But the library driver uses HWREG ...

  • Hi David,

      The upper 24bits are  reserved and not physically implemented. Writing to the upper 24bits have no effect as it will write nowhere. Therefore, writing only 8bits to the address or the entire 32bits will be the same. Is there a reason why you want to modify TivaWare HWREG?

  • I wasn't sure the implications of writing to reserved bits, especially as in this case the pins couldn't refer to the reserved bits. That I couldn't find in the documentation. For me reason to ask, is I take the code from inside GPIOPinWrite and remove the assert error testing - and that saves clocks in a very tight loop.

    But if both codings give the same result, in the same number of clocks, then I guess its swings and roundabouts ...

  • Hi,

      When I look at the disassembly code for GPIOPinWrite() it is already optimized to a one-cycle instruction. 

  • Interesting - I'll check when I get home. Wonder how the assert gets resolved, maybe it its always of statics, it can be resolved during compilation? But how can GPIOPinWrite know what arguments it receives before linking? Anyway I'll check when I get home. If thats the case I only gain by the inlining of the code.

  • By default, the driverlib is not built with DEBUG support. Therefore, ASSERT will not be checked. If you want to use the assert, you need to rebuild driverlib. See below gpio.c source code. If you don't want to rebuild the library, you can copy the gpio.c file to your CCS project. Try the hello project. In the CCS predefine symbols add the DEBUG symbol. 

    #ifdef DEBUG
    static bool
    _GPIOBaseValid(uint32_t ui32Port)
    {
    return((ui32Port == GPIO_PORTA_BASE) ||
    (ui32Port == GPIO_PORTA_AHB_BASE) ||
    (ui32Port == GPIO_PORTB_BASE) ||
    (ui32Port == GPIO_PORTB_AHB_BASE) ||
    (ui32Port == GPIO_PORTC_BASE) ||
    (ui32Port == GPIO_PORTC_AHB_BASE) ||
    (ui32Port == GPIO_PORTD_BASE) ||
    (ui32Port == GPIO_PORTD_AHB_BASE) ||
    (ui32Port == GPIO_PORTE_BASE) ||
    (ui32Port == GPIO_PORTE_AHB_BASE) ||
    (ui32Port == GPIO_PORTF_BASE) ||
    (ui32Port == GPIO_PORTF_AHB_BASE) ||
    (ui32Port == GPIO_PORTG_BASE) ||
    (ui32Port == GPIO_PORTG_AHB_BASE) ||
    (ui32Port == GPIO_PORTH_BASE) ||
    (ui32Port == GPIO_PORTH_AHB_BASE) ||
    (ui32Port == GPIO_PORTJ_BASE) ||
    (ui32Port == GPIO_PORTJ_AHB_BASE) ||
    (ui32Port == GPIO_PORTK_BASE) ||
    (ui32Port == GPIO_PORTL_BASE) ||
    (ui32Port == GPIO_PORTM_BASE) ||
    (ui32Port == GPIO_PORTN_BASE) ||
    (ui32Port == GPIO_PORTP_BASE) ||
    (ui32Port == GPIO_PORTQ_BASE) ||
    (ui32Port == GPIO_PORTR_BASE) ||
    (ui32Port == GPIO_PORTS_BASE) ||
    (ui32Port == GPIO_PORTT_BASE));
    }
    #endif

  • ta - yes that explains it.