STM32-P405 GPIO – Output Example

 

In this example we are going to drive a set of LEDs using the the GPIO pins on the STM32-P405 board. The LEDs are connected to the GPIO pins as follows:

Pin PC12 -> Red Led

Pin PC13 -> Green LED

Pin PC10 -> Blue LED

The LEDs have a current limiting resistor of 330 Ohms connected in series.

To drive these pins and light up the LEDS copy the files from the previous GPIO example, STM32-P405 GPIO – Enabling User Controllable LED, and change the main.c file as follows:

#define LED_DELAY (1000000)

int main() 
{
        gpio_setup();

        while(1) {
                gpio_flash_leds(LED_DELAY);
        }

        return 0;       
}

Then change gpio.c to the following:

#include <stdint.h>

#define RCC_GPIO_REG_ENABLE ((uint32_t *)0x40023830)
#define RCC_GPIO_REG_RESET ((uint32_t *)0x40023810)

#define RCC_GPIO_A_RESET (1 << 0)
#define RCC_GPIO_B_RESET (1 << 1)
#define RCC_GPIO_C_RESET (1 << 2)
#define RCC_GPIO_D_RESET (1 << 3)
#define RCC_GPIO_E_RESET (1 << 4)
#define RCC_GPIO_F_RESET (1 << 5)

#define RCC_GPIO_A_ENABLE (1 << 0)
#define RCC_GPIO_B_ENABLE (1 << 1)
#define RCC_GPIO_C_ENABLE (1 << 2)
#define RCC_GPIO_D_ENABLE (1 << 3)
#define RCC_GPIO_E_ENABLE (1 << 4)
#define RCC_GPIO_F_ENABLE (1 << 5)

#define GPIO_C_REG_BASE ((uint32_t *)0x40020800)
#define GPIO_C_REG_OUTPUT ((uint32_t *)0x40020814)

#define GPIO_C_REG_MODE GPIO_C_REG_BASE

#define GPIO_PIN_12_MODE_GPO (0x1 << 24)
#define GPIO_PIN_12_DATA_BIT (0x1 << 12)

#define GPIO_PIN_13_MODE_GPO (0x1 << 26)
#define GPIO_PIN_13_DATA_BIT (0x1 << 13)

#define GPIO_PIN_10_MODE_GPO (0x1 << 20)
#define GPIO_PIN_10_DATA_BIT (0x1 << 10)

#define LED_R GPIO_PIN_12_DATA_BIT
#define LED_G GPIO_PIN_13_DATA_BIT
#define LED_B GPIO_PIN_10_DATA_BIT

/* Write value to register address */
static inline
void reg_set(volatile uint32_t *reg_ptr, uint32_t val)
{
        *reg_ptr = val;
}

/* Copy register OR bits in and write back */
static inline
void reg_set_bits(volatile uint32_t *reg_ptr, uint32_t val)
{
        uint32_t reg;
        reg = *reg_ptr;
        reg |= val;
        *reg_ptr = reg;
}

/* Copy register OR bits in and write back */
static inline
void reg_unset_bits(volatile uint32_t *reg_ptr, uint32_t val)
{
        uint32_t reg;
        reg = *reg_ptr;
        reg &= ~val;
        *reg_ptr = reg;
}

void delay(int cycles)
{
        while(cycles) {
                cycles--;
        }

}

int gpio_setup(void)
{
        reg_set_bits(RCC_GPIO_REG_ENABLE, RCC_GPIO_C_ENABLE);

        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_12_MODE_GPO);
        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_13_MODE_GPO);
        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_10_MODE_GPO);

        return 0;
}

int gpio_flash_leds(int led_delay) {
        reg_set_bits(GPIO_C_REG_OUTPUT, LED_R);
        delay(led_delay);
        reg_set_bits(GPIO_C_REG_OUTPUT, LED_G);
        delay(led_delay);
        reg_set_bits(GPIO_C_REG_OUTPUT, LED_B);
        delay(led_delay);
        reg_set(GPIO_C_REG_OUTPUT, 0);
        delay(led_delay);
        return 0;
}

Then run make debug and start the main() function:

arm-none-eabi-as -g -o vector_table.o vector_table.s
arm-none-eabi-gcc -nostdlib -g -mcpu=cortex-m4 -mthumb -march=armv7e-m -c main.c
arm-none-eabi-gcc -nostdlib -g -mcpu=cortex-m4 -mthumb -march=armv7e-m -c gpio.c
arm-none-eabi-ld -g -o startup.elf -T startup.ld vector_table.o main.o gpio.o
arm-none-eabi-gdb -x gdbinit
GNU gdb (7.6.50.20131218-0ubuntu1+1) 7.6.50.20131218-cvs
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-linux-gnu --target=arm-none-eabi".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x08000054 in ?? ()
Loading section .text, size 0x17c lma 0x8000000
Start address 0x8000000, load size 380
Transfer rate: 576 bytes/sec, 380 bytes/write.
Breakpoint 1 at 0x8000054: file main.c, line 5.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, main () at main.c:5
5        gpio_setup();
r0             0x0    0
r1             0x0    0
r2             0x0    0
r3             0x0    0
r4             0x0    0
r5             0x0    0
r6             0x0    0
r7             0x20000ff8    536875000
r8             0x0    0
r9             0x0    0
r10            0x0    0
r11            0x0    0
r12            0x0    0
sp             0x20000ff8    0x20000ff8
lr             0x8000045    134217797
pc             0x8000054    0x8000054 <main+4>
xPSR           0x41000000    1090519040
(gdb) cont
Continuing.

The LEDs should now light in sequence.

led

The example above sets the appropiate bit in the GPIO output register. This requires a read-modify-write operation. I.e read the the output register into variable, OR a bit into this value, then write the value back into the register.

There are some issues with the approach, firstly it increases the amount of CPU cycles in order to set the appropriate bit which can slow down the rate of GPIO operations, secondly if more than one thread wishes to set or unset a pin on the output register there is the potential for race conditions.

For example, a thread could read the register and set the appropriate bit, but before it can write the value back to the register the thread pre-empted by another thread, this second thread also reads the register sets its’ appropriate bit and then writes this back to the register. The first thread now resumes execution and writes its’ value back to the register, which has the effect of unsetting the bit the second thread set.

Using a locking scheme would of course prevent this, but also entails the use of many CPU cycles to synchronise access to the register.

In order to avoid this another register is available, the BSRR, which allows a thread to set or unset a GPIO pin on an atomic basis.

From the OEM Reference Manual (Section 8.4.7 GPIO port bit set/reset register (GPIOx_BSRR)), it can be seen that the 32 bit register is split into two 16 bit sections, the first 16 bit region maps a GPIO pin to a “set”function, i.e. setting bit 12 would set pin PC12 to 5v

The second 16 bit region does the opposite, i.e. setting bit 24 would set the voltage on pin PC12 to ground.

To see this in action replace gpio.c with the following:

#include <stdint.h>

#define RCC_GPIO_REG_ENABLE ((uint32_t *)0x40023830)
#define RCC_GPIO_REG_RESET ((uint32_t *)0x40023810)

#define RCC_GPIO_A_RESET (1 << 0)
#define RCC_GPIO_B_RESET (1 << 1)
#define RCC_GPIO_C_RESET (1 << 2)
#define RCC_GPIO_D_RESET (1 << 3)
#define RCC_GPIO_E_RESET (1 << 4)
#define RCC_GPIO_F_RESET (1 << 5)

#define RCC_GPIO_A_ENABLE (1 << 0)
#define RCC_GPIO_B_ENABLE (1 << 1)
#define RCC_GPIO_C_ENABLE (1 << 2)
#define RCC_GPIO_D_ENABLE (1 << 3)
#define RCC_GPIO_E_ENABLE (1 << 4)
#define RCC_GPIO_F_ENABLE (1 << 5)

#define GPIO_C_REG_BASE ((uint32_t *)0x40020800)
#define GPIO_C_REG_OUTPUT ((uint32_t *)0x40020814)

#define GPIO_C_REG_BSRR ((uint32_t *)0x40020818)

#define GPIO_C_REG_MODE GPIO_C_REG_BASE

#define GPIO_PIN_12_MODE_GPO (0x1 << 24)
#define GPIO_PIN_12_DATA_BIT (0x1 << 12)
#define GPIO_PIN_12_RESET_BIT (0x1 << 28)

#define GPIO_PIN_13_MODE_GPO (0x1 << 26)
#define GPIO_PIN_13_DATA_BIT (0x1 << 13)

#define GPIO_PIN_10_MODE_GPO (0x1 << 20)
#define GPIO_PIN_10_DATA_BIT (0x1 << 10)

#define LED_R GPIO_PIN_12_DATA_BIT
#define LED_G GPIO_PIN_13_DATA_BIT
#define LED_B GPIO_PIN_10_DATA_BIT

/* Write value to register address */
static inline
void reg_set(volatile uint32_t *reg_ptr, uint32_t val)
{
        *reg_ptr = val;
}

/* Copy register OR bits in and write back */
static inline
void reg_set_bits(volatile uint32_t *reg_ptr, uint32_t val)
{
        uint32_t reg;
        reg = *reg_ptr;
        reg |= val;
        *reg_ptr = reg;
}

/* Copy register OR bits in and write back */
static inline
void reg_unset_bits(volatile uint32_t *reg_ptr, uint32_t val)
{
        uint32_t reg;
        reg = *reg_ptr;
        reg &= ~val;
        *reg_ptr = reg;
}

void delay(int cycles)
{
        while(cycles) {
                cycles--;
        }

}

int gpio_setup(void)
{
        reg_set_bits(RCC_GPIO_REG_ENABLE, RCC_GPIO_C_ENABLE);

        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_12_MODE_GPO);
        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_13_MODE_GPO);
        reg_set_bits(GPIO_C_REG_MODE, GPIO_PIN_10_MODE_GPO);

        return 0;
}

int gpio_flash_leds(int led_delay) {
        reg_set(GPIO_C_REG_BSRR, LED_R);
        delay(led_delay);
        reg_set(GPIO_C_REG_BSRR, GPIO_PIN_12_RESET_BIT);
        delay(led_delay);
        return 0;
}

Running this example now results in the red LED flashing on and off.

 

 

 

Advertisements