Search code examples
carmembeddedstm32keil

How to do bare-metal LED blink on STM32F103C8T6?


I have just started exploring STM32 MCUs. I want to blink the LED on the BluePill(having STM32F103C8T6 MCU) board. I suspect I have been mislead by something. As per the Reference Manual of F1 series, There are 3 main steps:

  • Enable Clock for the PORT (here PORTC)
  • Configure The CNF/MODE registers
  • Configure ODR register as required i.e. HIGH/LOW on the pin.

I have written the code in KEIL MDK as per the manual but after it is loaded, The code do not run, I press the reset button and then LED turns ON, even though I have changed Settings to RESET & RUN in KEIL.

Here is the code and The parts of reference manual.

#include<stm32f10x.h>

int main(){
    
    RCC->APB2ENR |= 1<<4; //PORTC is on APB2 bus
    GPIOC->CRH |= (1<<20);
    
    while(1){
        GPIOC->ODR |= 0x00002000;
        for(int i = 0; i < 500000;i++); //dummy delay
        GPIOC->ODR &= ~0x00002000;
        for(int i = 0; i < 500000;i++); // dummy delay

    }

}

Reference manual: ODR register APB2 bus enable CRH register

When I am using the Debug mode, I noticed one thing that the clock is not enabled for PORTC after execution of RCC->APB2ENR |= (1<<4). GPIOC debug

The LED does not blink. I am unable to find the error in this whole process.


Solution

  • When you write a dummy delay loop, a smart compiler will usually figure out that there is nothing worthwhile happening in this piece of code and optimize the whole thing away.

    If you want to know this has happened, the best way is to take a look at the disassembly of the generated binary.

    Thankfully, C provides the volatile keyword to get around exactly this sort of problem. It tells the compiler explicitly to not optimize memory accesses to variables declared with this qualifier.

    Here you can see some sample code that shows the difference between generated assembly code with and without volatile keyword using the excellent godbolt tool. Without volatile, the for loop gets optimized to nothing and no incrementing or checking of i ever happens.

    Hence the loops should have been written as:

    for(volatile int i = 0; i < 500000; i++); //dummy delay
    

    You may run into this kind of issue on embedded systems also in other instances such as when you have a variable being accessed from multiple contexts/threads.