┬ÁTasker Forum

┬ÁTasker Forum => STTM STM32 and STR91XF => Topic started by: FAQ on May 28, 2009, 11:31:26 AM

Title: GPIO use on STR91
Post by: FAQ on May 28, 2009, 11:31:26 AM
How are GPIOs best configured for input/output use?

Also, is it possible to make use of thir bit masking functionality?

Title: Re: GPIO use on STR91
Post by: mark on May 28, 2009, 11:37:52 AM

The configuration of GPIOs is a bit complicated, but here are the general rules:

1) The individual ports are not clocked out of reset and they are also held in reset state. This means that for each used port, first clocks must be applied to the port and then the port needs to be taken out of reset.
As example, for the GPIOs 4, 6, 7 and 8, the following will do this - it is called only once.

        SCU_PCGR1 |= (GPIO4 | GPIO6 | GPIO7 |GPIO8);              // clock the ports
        SCU_PRR1  |= (GPIO4 | GPIO6 | GPIO7 |GPIO8);              // remove internal resets

2) The GPIOs have input and output configuration registers plus the conventional direction register (DDR). To ensure input operation it is best to clear the output configuration, set the input configuration and clear the DDR. This is often not necessary due to default states out of reset but it is best to be consistent to avoid any surprises (eg. when the code is used somewhere else and the port had already been configured). The configuration also removes any peripheral use.

The following example configured port 4 bits 0 and 1 as inputs:

        SCU_GPIOOUT4 &= ~((MASK_FUNCTION << (2*0)) | (MASK_FUNCTION << (2*1)));                // mask peripheral function select
        SCU_GPIOIN4  &= ~(PORT4_BIT0 | PORT4_BIT1);                                            // ensure input not connected to a peripheral
        GPIO_DIR_4   &= ~(PORT4_BIT0 | PORT4_BIT1);                                            // ensure inputs

3) Outputs are a bit special too since they don't drive the data register state to the line when made output. Instead they drive the original input state. This means that it is important to first set the DDR to output and then set the value (setting first the value and then changing the DDR will not necessarily give the state that you expect).

The following shows how to set various outputs and drive them with all '1'.

        SCU_GPIOOUT6 &= ~((MASK_FUNCTION << (2*0)));                                           // mask peripheral function select
        SCU_GPIOIN6  &= ~(PORT6_BIT0);                                                         // ensure input not connected to a peripheral
        GPIO_DIR_6   |= (PORT6_BIT0);                                                          // prepare outputs
        GPIO_DATA_6  |= (PORT6_BIT0);                                                          // set output '1'

        SCU_GPIOOUT7 &= ~((MASK_FUNCTION << (2*0)) | (MASK_FUNCTION << (2*1)));                // mask peripheral function select
        SCU_GPIOIN7  &= ~(PORT7_BIT0 | PORT7_BIT1);                                            // ensure input not connected to a peripheral
        GPIO_DIR_7   |= (PORT7_BIT0 | PORT7_BIT1);                                             // prepare outputs
        GPIO_DATA_7  |= (PORT7_BIT0 | PORT7_BIT1);                                             // set output '1'

        SCU_GPIOOUT8 &= ~((MASK_FUNCTION << (2*1)) | (MASK_FUNCTION << (2*2)) | (MASK_FUNCTION << (2*3)) | (MASK_FUNCTION << (2*4))); // mask peripheral function select
        SCU_GPIOIN8  &= ~(PORT8_BIT1 | PORT8_BIT2 | PORT8_BIT3 | PORT8_BIT4);                  // ensure input not connected to a peripheral
        GPIO_DIR_8   |= (PORT8_BIT1 | PORT8_BIT2 | PORT8_BIT3 | PORT8_BIT4);                   // prepare outputs
        GPIO_DATA_8  |= (PORT8_BIT1 | PORT8_BIT2 | PORT8_BIT3 | PORT8_BIT4);                   // set output '1'

I think that the pattern is very clear and so it can also be easily extended to other ports if necessary

4) Beware that, after enabling the clock and removing reset that the ports may take an instruction interval (or two) before it is actually prepared to accept commands. Therefore it is a good idea to first enable all ports (clocks/reset) and then perform the detailed configuration - this ensures that writes to the port registers themselves is actually delayed slightly and avoids potential problems.

The (present) project uses GPIO_DATA_X to read and write port data. This is the simplest method but not necessarily the fastest.
The ports also support a masking function (controlled over the port address - in a block of 256 long addresses).
Since this operation is in fact the same as the Luminary GPIOs - where this has been extended - the following macros shoudl be able to be used to allow reading and writing masked bits within a port byte (avoiding the constructions PORT |= BITS; and PORT &= ~BITS). This is discussed further here: http://www.utasker.com/forum/index.php?topic=539.msg2384#msg2384

I think that these macros should work with the STR91:

    #define _WRITE_PORT_MASK(ref, value, mask)    *(unsigned long *)(GPIO_PORT##ref##_BLOCK + 4*mask) = value
    #define _READ_PORT_MASK(ref, mask)            *(unsigned long *)(GPIO_PORT##ref##_BLOCK + 4*mask)

As example, rather than using
        GPIO_DATA_8  |= (PORT8_BIT1 | PORT8_BIT2 | PORT8_BIT3 | PORT8_BIT4);                   // set output '1's
It would allow

In the read case, instead of doing something like

if ((GPIO_DATA_4 & (PORT4_BIT0 | PORT4_BIT1)) == PORT4_BIT0) {}                     // check for 01 input state

the following could be used

if (_READ_PORT_MASK(4, (PORT4_BIT0 | PORT4_BIT1)) == PORT4_BIT0) {}

The result is slightly faster code, making use of this feature.