2
votes

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.

2
Did you check the generated code? The delay might be getting optimized out. Maybe you can try declaring the loop counter(s) as volatile.th33lf
@th33lf I am sorry I did not understand what you mean. Actually the LED toggles inside the while loop ,but only in debug mode. Also it shows the clock is not enabled for PORTC.Sajil
Do you mean that if you step through, the LED toggles, but if you let the code run continuously, the LED just stays lit? If so, the delay might be getting removed by the compiler.th33lf
Yes , not lit but off. If the code is correctly compiled and uploaded, the LED should blink as soon as i power the board but that's not the case.Sajil
I still don't understand exactly what you are facing. Does it blink in debug mode and only not work when you simply flash and reset? Or does it work only when you step through line by line? If so, I would try first with the loop counters declared as volatile. Also, the port configuration seems to be a two-step process. You should configure the direction (input/output) and then what kind of output it should be. For LEDs, it is usually open drain output, but depends on your board. I am not sure if just doing (1 << 20) sets both of these correctly.th33lf

2 Answers

4
votes

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.

-1
votes

Actually the LED toggles inside the while loop ,but only in debug mode

Yes, because that's the only time the generated machine code includes those delay loops. In release mode, the LED still toggless, except you need an oscilloscope or a logic analyzer to look at the output pin's state to see that it's toggling - you won't see it with just your eyes :)

In release mode, the delay loops are removed, because you can't implement delays that way. Ideally you should use a timer, but as a quick hack this would work:

const int N = 500000;
while(1){
    for(int i = 0; i < N;i++) GPIOC->ODR |= 0x00002000;
    for(int i = 0; i < N;i++) GPIOC->ODR &= ~0x00002000;
}

It'll work since GPIOC is points to a volatile object, and the compiler can't optimize the accesses out.