STM32-P405 GPIO – Enabling User Controllable LED


The STM32-P405 board has a user controllable surface mounted LED which is connected to a GPIO pin. According to the Supplier User Manual (Section 6.5 Additional hardware components) the LED is connected to GPIO pin PC12.

From the OEM Reference Manual (Section 6.3.10 RCC AHB1 peripheral clock register) a clock needs to be enabled to drive the GPIO(C) output and then pin PC12 mode is set as a general purpose output (Section 8.4.1 GPIO port mode register, Section 2.3 Memory map).

Begin by copying the files from a previous example into an appropriate working directory and make the changes highlighted in bold to the Makefile and main.c files as follows:

OBJ := vector_table.o main.o gpio.o
CFLAGS := -nostdlib -g -mcpu=cortex-m4 -mthumb -march=armv7e-m
LDFLAGS := -g -o
ASMFLAGS := -g -o
all: $(TARGET)

%.o: %.s
        $(CROSS)as -g -o $@ $<

%.o: %.c
        $(CROSS)gcc $(CFLAGS) -c $<

        $(CROSS)ld -g -o $@ -T $(LDSCRIPT) $(OBJ)

        $(RM) *.o *.elf

debug: $(TARGET)
        $(CROSS)gdb -x gdbinit

Now add the following to the file main.c:

int main()
        return 0;


Now add a file called gpio.c with the following contents:

#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_PIN_12_MODE_GPO (0x1 << 24)

/* 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;

int gpio_setup(void)
        reg_set_bits(RCC_GPIO_REG_ENABLE, RCC_GPIO_C_ENABLE);
        reg_set_bits(GPIO_C_MODER, GPIO_PIN_12_MODE_GPO);

        return 0;

In order to use the stdint.h header file you may need to install a set of header files from the newlib project as follows:

sudo apt-get install libnewlib-arm-none-eabi

then run make debug

make debug
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 (
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <>
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:
Find the GDB manual and other documentation resources online at:
For help, type "help".
Type "apropos word" to search for commands related to "word".
0x080000b8 in ?? ()
Loading section .text, size 0xb0 lma 0x8000000
Start address 0x8000000, load size 176
Transfer rate: 272 bytes/sec, 176 bytes/write.
Breakpoint 1 at 0x8000040: file vector_table.s, line 29.
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, reset_handler () at vector_table.s:29
29        bl main
r0             0x0    0
r1             0x0    0
r2             0x0    0
r3             0x0    0
r4             0x0    0
r5             0x0    0
r6             0x0    0
r7             0x0    0
r8             0x0    0
r9             0x0    0
r10            0x0    0
r11            0x0    0
r12            0x0    0
sp             0x20001000    0x20001000
lr             0xffffffff    -1
pc             0x8000040    0x8000040 <reset_handler>
xPSR           0x41000000    1090519040

Start the main() function which in turn calls gpio_setup() as follows:

(gdb) cont
Program received signal SIGINT, Interrupt.
halt () at vector_table.s:31
31        bl halt

The LED should be lit as shown in the following image:


If LED is now lit the clock has been enabled correctly as well as the correct GPIO pin.