Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What would cause peripheral register writes to be ignored on STM32?

Tags:

c

stm32

I have an STM32G491RE Nucleo board and I'm trying to make the LED blink without any libraries, following tutorials like this and this.

Here's the datasheet for STM32G4.

And here's my basic LED blink code:

#include <stdbool.h>
#include <stdint.h>

#define PERIPH_RCC 0x40021000
#define RCC_AHB2ENR ((volatile uint32_t*) PERIPH_RCC + 0x4c)

#define PERIPH_GPIOA 0x48000000
#define GPIOA_MODE  ((volatile uint32_t*) PERIPH_GPIOA + 0x00)
#define GPIOA_ODR ((volatile uint32_t*) PERIPH_GPIOA + 0x14)
#define GPIOA_BSRR ((volatile uint32_t*) PERIPH_GPIOA + 0x18)

#define LED_PIN 5

int main(void) {
    // enable clock for GPIOA
    *RCC_AHB2ENR |= (1 << 0);

    // wait two clock cycles for the peripheral clock to be enabled
    volatile uint32_t temp;
    temp = *RCC_AHB2ENR;
    temp = *RCC_AHB2ENR;

    

    *GPIOA_MODE &= ~(0b11 << (LED_PIN * 2));
    *GPIOA_MODE |= (0b01 << (LED_PIN * 2));

    bool val = false;
    for(;;) {
        // *GPIOA_ODR ^= (1 << LED_PIN);
        val = !val;
        *GPIOA_BSRR = (1 << LED_PIN) << (val ? 0 : 16);
        for(uint32_t i = 0; i < 100000; i++);
    }
}

#define SRAM_START (0x20000000U)
#define SRAM_SIZE (96U * 1024U)
#define SRAM_END (SRAM_START + SRAM_SIZE)
#define STACK_PTR_INIT (SRAM_END)

extern uint32_t _text_end, _data_start, _data_end, _bss_start, _bss_end;
void reset_handler(void) {
    // copy .data from FLASH to SRAM
    uint32_t data_size = (uint32_t)&_data_end - (uint32_t)&_data_start;
    uint8_t* flash_data = (uint8_t*)&_text_end;
    uint8_t* sram_data = (uint8_t*)&_data_start;

    for(uint32_t i = 0;  i < data_size; i++) {
        sram_data[i] = flash_data[i];
    }

    // zero-fill .bss
    uint32_t bss_size = (uint32_t)&_bss_end - (uint32_t)&_bss_start;
    uint8_t* bss = (uint8_t*) &_bss_start;
    for(uint32_t i = 0; i < bss_size; i++) {
        bss[i] = 0;
    }
    main();
}

void default_handler(void) {
    while(true);
}

// stack ptr + cortex-m4 + 71 channels for stm32g491x
#define ISR_VECTOR_LEN (1 + 15 + 71)

uint32_t isr_vector[ISR_VECTOR_LEN] __attribute__((section(".isr_vector"))) = {
    STACK_PTR_INIT,
    (uint32_t)&reset_handler,
};

I'm stepping through this in a debugger, trying to determine why the LED isn't blinking. Much to my surprise, when I run x/tw 0x4002104c in GDB to read the memory for the RCC_AHB2ENR word, it still contains the reset value even after the line that writes it is executed. All of my writes to the GPIOA peripheral registers also exhibit this behavior. What gives?

like image 966
winduptoy Avatar asked Jan 22 '26 16:01

winduptoy


1 Answers

Your error is this expression: ((volatile uint32_t*) PERIPH_RCC + 0x4c).

It casts PERIPH_RCC to a pointer to uint32_t (the volatile is not relevant here). After this it adds the index 0x4c, which results in the address 0x40021000 + 0x4c * sizeof (uint32_t) giving 0x40021130.

Casts have higher precedence than additions.

The solution is to put the addition in parentheses: ((volatile uint32_t*) (PERIPH_RCC + 0x4c)).

like image 178
the busybee Avatar answered Jan 25 '26 07:01

the busybee



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!