0
\$\begingroup\$

My Environment:

1- Mac OS

2- STM32 Bluepill flashed with Blackmagic Prob firmware and acts as a Black magic probe

3- Stm32 Bluepill as a development board

4- CMSIS firmware

5- Using Vscode with PlatformIO

My Problem: I am trying to program my STM32 blue pill and debugging it using a Blackmagic probe (a blue pill with BMP firmware)

I wrote the following code and when debugging it using BMP it starts getting executed but when reaching the delay1() function line of code, control just passes it and doesn't execute it therefore the LED connected to the pin PB11 is always ON but dimmed because it gets switched ON and Off so fast and the delay function is not executed!

My question is: What is going on? Why would control just jump and not execute a line of code?


/** 
 * ? LED BLINKIND SKETCH, PINOUT FOR STM32F103XX 
 * ! Green LED -> PB11
 * * Blue LED  -> PB10
 * * Red LED   -> PB1
 * * Pot.      -> PA0
 * * Push Btn. -> PB9
*/

//? ========= INCLUEDS =+=========
#include "STM32F1xx.h"

//? ========= PROTOTYPES ===========
void portsInit(void);
void delay1(void);


int main(void)
{
    portsInit();

    while(1)
    {
        //GPIOB->ODR ^= GPIO_ODR_ODR11_Msk;
        GPIOB->BSRR = GPIO_BSRR_BS11;
        delay1();
        GPIOB->BSRR = GPIO_BSRR_BR11;
        delay1();
    }
}


void portsInit(void)
{
    //* Enable clock to port "A" and "B" (Default SysCLK = 8Mhz ext. OSC):
    RCC->APB2ENR |= ((1<<2) | (1<<3));

    //* Set Pin Modes and Pin Configuration. (Reset Value = 0x4444 4444)
    //! Setting Mode for Pin PB11 MODE[1:0], "Bit1" of the MODE register = 1 and "Bit0" = 1
    GPIOB->CRH |= GPIO_CRH_MODE11_Msk;           // Set PB11 MODE as Output @ 50Mhz (max).  
    //! Setting Configuration for Pin PB11 CNF[1:0]
    GPIOB->CRH &= ~(GPIO_CRH_CNF11_Msk);
    /*
        * Final GPIOB->CRH register must equal 0b 0011 0100 0100 0100 or 0x3444,
        * to set pb11 pin as an Output MODE @ 50Mhz configured as General purpose
        * output push-pull configuration.
    */
}// End portsInit() Function.

void delay1(void)
{
    for(uint32_t i = 0; i < 10000000; i++)
    {
        //! LOOP DOING NOTHING WASTING TIME (PSUDO-DELAY)
    }
}



/**     ============= Registers Can be used as well such as: ==================    
    
    //RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;       // Enable PortA Clock
    //RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;       // Enable PortB Clock
    Set pin "PB11" as output push-pull (Green LED)
    GPIOB->CRH |= ((1<<12) | (1<<13));        //Set Pin to Output 50Mhz max speed
    GPIOB->CRH &= ~((1<<14) | (1<<15));       //Configure Pin as Push-Pull

*/

This is my platformIO.ini:

[env:bluepill_f103c8]
platform = ststm32
board = bluepill_f103c8
framework = cmsis
board_build.mcu = stm32f103c8t6                 ; change microcontroller (override)
board_build.f_cpu = 8000000L                    ; change MCU frequency   (override)
upload_protocol = blackmagic                    ; SWD interface
;upload_protocol = blackmagic-jtag               ; JTAG interface
debug_tool = blackmagic
debug_port = /dev/tty.usbmodem7BBB54AD1
upload_port = /dev/tty.usbmodem7BBB54AD1        ; There are 2 Ports, UART and SWD, Check ports in terminal% ls /dev/tty.*

PS. I am using an extension for commenting!

\$\endgroup\$
3
\$\begingroup\$

That is perfectly normal. The compiler has optimizations on, and it sees you have an empty for loop which does nothing so it optimizes it away. Since there is now an empty subroutine it can also be optimized away and it does not need to be called.

\$\endgroup\$
3
  • \$\begingroup\$ @The_M_Code You should be able to turn off compiler optimizations somewhere in your project/compiler settings. Then rebuild and the debugger should step more like you expect. \$\endgroup\$ – kkrambo Dec 16 '20 at 14:51
  • \$\begingroup\$ I will try to figure out where are the optimization settings. Anyways, what are better practices for adding a loop \$\endgroup\$ – The_M_Code Dec 16 '20 at 16:17
  • 1
    \$\begingroup\$ @The_M_Code that is completely another question. What if a loop is not a good practice at all? And if you still want a for loop, there are plenty of examples to make the loop variable volatile and using inline assembly to add a volatile "nop" opcode to execute, even in STM32 example applications. \$\endgroup\$ – Justme Dec 16 '20 at 16:23
1
\$\begingroup\$

As previously explained, your delay loop has no apparent computational function or side effects, and so ends up removed by an optimizing compiler.

To have a delay loop which survived, it would have to either contain an operation which accomplished something, or one which the compiler was specifically informed was needed despite the lack of any "visible" result.

For example, here's how ARM's basic core support CMSIS library creates a C language __NOP() which inserts a non-removable "do nothing" opcode when using a GCC-style toolchain:

__attribute__((always_inline)) __STATIC_INLINE void __NOP(void)
{
  __ASM volatile ("nop");
}

If you are linking CMSIS, you can probably just use __NOP() in your code, otherwise you could use the __ASM volatile ("nop"); directly. If you are using a different compiler you'd need to figure out the particular mechanism for that; looking in the CMSIS sources wouldn't be a bad approach to doing so.

Speaking more generally, delay loops have a reputation for being over-used by those who gained their formative programming experience on classic home computers with single threaded operating systems, and on early generation MCUs with few timer peripherals. Things like "turbo-XT" and early AT PC's exposed the fallacy of this, as a number of games then became unplayably fast.

Often it is considered preferable to use a timer-based delay routine, and most modern MCU environments make some millisecond delay function available. A large advantage there is consistency. On the flip side, a timer-based delay routine is often a place where a project with incomplete chip setup will fail - there was a question here that traced back to that very issue this week.

Finally it's worth noting that any sort of blocking wait prevents anything else from happening. Many more sophisticated firmware projects operate as state machines, where an operation is kicked off, and other things are done until enough time has elapsed for the long running operation to be complete. Others use an RTOS with distinct tasks, such that other useful work can be done while one tasks is blocking on a delay (hopefully implemented using an RTOS-appropriate mechanism!)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.