3

The following code is for blinking of the on-board LED on an STM32F401RE board. I am trying to do it without using HAL. When I flash it on to the board, it blinks once and then stays on. How can I fix this? Thank you in advance.

#include "stm32f401xe.h"
#define PLL_M 4
#define PLL_N 84
#define PLL_P 0

void sys_clk_cfg(void) {
    //1. enable HSE and wait for HSE to become ready
    RCC->CR |= RCC_CR_HSEON;
    while(!(RCC->CR & RCC_CR_HSERDY));

    //2. Set the power enable clock and voltage regulator
    RCC->APB1ENR |= RCC_APB1ENR_PWREN;
    PWR->CR |= PWR_CR_VOS;

    //configure the flash access
    FLASH->ACR |= FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;

    //configure the prescaler for clocks
    //AHB PR
    RCC->CFGR |= RCC_CFGR_HPRE_DIV1;

    //APB1 PR
    RCC->CFGR |= RCC_CFGR_PPRE1_DIV2;

    //APB2 PR
    RCC->CFGR |= RCC_CFGR_PPRE2_DIV1;


    //configure the PLL 
    RCC->PLLCFGR = (PLL_M <<0) | (PLL_N << 6) | (PLL_P << 16) | (RCC_PLLCFGR_PLLSRC_HSE);
    
    //enable the PLL
    RCC->CR |= RCC_CR_PLLON;
    while(!(RCC->CR & RCC_CR_PLLRDY));


    //set the clock source
    RCC->CFGR |= RCC_CFGR_SW_PLL;
    while((RCC->CFGR & RCC_CFGR_SW) != RCC_CFGR_SW_PLL);
}

void GPIO_Config(void)
{
    //enable the gpio clock
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;

    //2. SET THE PIN PA5 AS OUTPUT
    GPIOA->MODER |= (1<<10);

    //3. CONFIGURE THE OUTPUT MODE
    GPIOA->OTYPER = 0;
    GPIOA->OSPEEDR = 0;
}

void delay(uint32_t time)
{
    while(time--);
}


int main()
{
    sys_clk_cfg();
    GPIO_Config();

    while(1) {
        GPIOA->BSRR |= (1 << 5);
        delay(100000);
        GPIOA->BSRR |= ((1 << 5) << 16); 
        delay(100000);
    }
}

I expected the LED to Blink, but it's not blinking and just stays on the whole time.

1
  • 1
    For clarity and to avoid errors I would suggest:GPIOA->BSRR |= GPIO_BSRR_BS5 ; /GPIOA->BSRR |= GPIO_BSRR_BR5 ; The GPIO_BSRR_BS5/GPIO_BSRR_BR5 macros are defined in the stm32f401xe.h you have already included and is not part of the HAL. Commented Aug 24, 2024 at 7:31

3 Answers 3

2

Your delay function is seriously flawed. You need at least:

void delay( volatile uint32_t time )

to prevent the empty loop from being optimised to nothing. It is in any case an extraordinarily crude method of implementing a delay, and your blink rate may be different under different optimisation levels, and certainly at different clock rates or different processors. The Cortex-M4 has as SYSTICK clock specifically for this sort of thing (example)

Sign up to request clarification or add additional context in comments.

Comments

1

Please first make sure that your GPIO code runs with the default clock settings. By default, 16 MHz HSI is already available/running.

Try removing RCC-> PWR-> FLASH-> codes except the one that enables the GPIO clock. You should be able to run blinky without touching these settings.

Also try adding __DSB(); after GPIO clock enable. This is mentioned in the errata document.

RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
__DSB();

Also, try adding __NOP(); into your delay code. NOP doesn't optimized away completely. But remember that it gives you inconsistent timings. If you have a scope, try probing the LED. Maybe it's blinking faster than you think.

1 Comment

I actually benchmarked NOP on STM32 (F103 which I had at hand), and NOP gives consistent and reproducible 1 cycle delay per NOP without exceptions (I had 100 NOPs in a row, and measured clock cycles using SysTick counter clocked from CPU clock, ran it multiple times). Even tho official ARM documentation says the processor may drop NOP from the pipeline at runtime, it seems STM32 always "executes" every single NOP. What I haven't tested is if M7 core can "parallelize" the NOPs. In practice, I found a read of some volatile hardware register often used for a guaranteed tiny delay.
0

Adding on to Clifford's answer.
Your compiler might be optimizing the source code and removing the delay function.
Since the while loop inside the function does not execute any code during the iteration and also keeps the CPU busy during the iteration, the compiler might view the function as unnecessary that does nothing but keep the CPU busy (blocked). Thus it might remove (optimize out) the loop, thereby causing no delay and hence the LED not to blink.
But there is a way to tell the compiler to not optimize the loop and keep it as it is. This is by specifying a "do not optimize" flag.
For GCC and Clang compilers, you can specify an attribute above the delay function that sets the optimization level to 0. See the following code snippet.

__attribute__((optimize("O0")))
void delay(uint32_t time)
{
    while(time--);
}

Explanation:

  1. __attribute__ is used to specify additional properties and behaviours for functions, variables etc.
  2. optimize("<optimization level>") specifies the level of optimization required. O0 means no optimization.

By specifying this attribute, the compiler will not optimize (remove) the code inside the delay function, and keep the loop as it is and thus enable the LED to blink.

You can lookup ways to disable optimization for other compilers as well.

References (for GCC):

  1. https://gcc.gnu.org/onlinedocs/gcc-4.6.0/gcc/Function-Attributes.html
  2. How many GCC optimization levels are there?

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.