Skip to main content
Visitor II
August 22, 2025
Solved

HAL_Delay hangs after jumping from bootloader to FreeRTOS app (using TIM14 as tick source)

  • August 22, 2025
  • 3 replies
  • 706 views

Hello,

I am working on an STM32F446RE with a custom bootloader and FreeRTOS application.

  • Bootloader address: 0x08000000

  • Application address: 0x0800C000

  • The bootloader successfully jumps to the application by setting MSP, PC, and SCB->VTOR.

  • In my application, I configured FreeRTOS to use a hardware timer (TIM14) instead of SysTick as the OS tick source.

Problem:
When I flash the application directly, it runs fine.
But when I run through the bootloader, the application stops working as soon as it calls HAL_Delay(). If I comment out HAL_Delay(), the app continues normally. After the RTOS kernel starts, I get a HardFault on the first interrupt.

I suspect this is related to SysTick still being dirty from the bootloader, while my application is configured to use TIMx as the tick source.

My questions:

  1. What is the recommended way to cleanly reinitialize the HAL tick and interrupt system after jumping from a bootloader to an application that uses TIMx instead of SysTick?

  2. Should I override HAL_InitTick/HAL_GetTick in the application so that HAL_Delay() works with FreeRTOS (using TIMx ticks)?

  3. If HAL_Delay() must work before FreeRTOS scheduler starts, should I explicitly reconfigure SysTick in the application even though FreeRTOS uses TIM14?

  4. Is there a recommended sequence of peripheral deinitialization in the bootloader to avoid SysTick/interrupt leftovers?

Extra info:

  • Toolchain: GCC (CubeIDE, no optimizations)

  • Bootloader disables SysTick (SysTick->CTRL=0; SysTick->LOAD=0; SysTick->VAL=0;) before jump.

  • Application runs fine standalone, fails only after bootloader jump when HAL_Delay() is called.

Thanks in advance for guidance.

    This topic has been closed for replies.
    Best answer by SaiKumar1

    enabling the interrupts in application after HAL_Init in application solved the problem

    3 replies

    Graduate II
    August 22, 2025

    Isn't it recommended that you leave SysTick to FreeRTOS & designate a separate TIM for HAL use?

    SaiKumar1Author
    Visitor II
    August 22, 2025

    Yes, I am aware that FreeRTOS normally takes over SysTick, and the recommendation is to keep SysTick for FreeRTOS while designating a separate hardware timer for HAL timebase.In my case, I configured FreeRTOS to use a hardware TIM (not SysTick) for the RTOS tick source. That’s why HAL_Delay() is failing after bootloader jump — because Cube HAL by default still expects SysTick to be running for delays, unless HAL_InitTick is overridden.

    Super User
    August 22, 2025

    > When I flash the application directly, it runs fine.
    > But when I run through the bootloader, the application stops working as soon as it calls HAL_Delay().

    Ensure SCB->VTOR is being set correctly by the application. When it hard fault, see what interrupt it thinks it's in (VECTACTIVE bits in SCB) and address the problem appropriately.

    SaiKumar1Author
    Visitor II
    September 5, 2025
    I am working with a custom bootloader on STM32F446RE.
    The bootloader successfully jumps to my application at 0x0800C000.
    The application runs, prints "Application Running", but as soon as it calls HAL_Delay(), the code stops executing.
     
    If I flash the application directly (without bootloader), it runs completely fine.
     
    In my jump code, I de-initialize peripherals, disable SysTick, clear pending interrupts, reset NVIC, and then relocate the vector table before jumping. Example:
     
    void jump_to_application(void)
    {
        uint32_t appStack        = *(volatile uint32_t*)APP_START_ADDR;
        uint32_t appResetHandler = *(volatile uint32_t*)(APP_START_ADDR + 4);
        typedef void (*pFunction)(void);
        pFunction JumpToApp = (pFunction)appResetHandler;
     
        __disable_irq();
     
        SysTick->CTRL = 0;
        SysTick->LOAD = 0;
        SysTick->VAL  = 0;
      
        for (uint32_t i = 0; i < 8; i++) {
            NVIC->ICER[i] = 0xFFFFFFFF;
            NVIC->ICPR[i] = 0xFFFFFFFF;
        }
     
        HAL_UART_DeInit(&huart2);
        HAL_DeInit();
        HAL_RCC_DeInit();
     
        SCB->VTOR = APP_START_ADDR;
        __set_MSP(appStack);
        __enable_irq();
        JumpToApp();
    }
     
    The code will jump from bootloader to app on pressing the button PC13 configured as EXTI 
     
    void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
    {
    if(GPIO_Pin==KEY_Pin)
    {
    jump_to_application();
    }
    }
    and my app code is very simple just to print a small text on uart2,
      while (1)
      {
      Log_Msg("Application Running\r\n");
      HAL_Delay(1000);
        /* USER CODE END WHILE */
     
        /* USER CODE BEGIN 3 */
      }
     
    Super User
    September 15, 2025

    Don't jump to the bootloader within an interrupt context (HAL_GPIO_EXTI_Callback).

    Set a flag and make the jump within the main thread.

    Visitor II
    September 15, 2025

    I used FreeRTOS in bootloader and after jump first hal_delay / systick interrupt caused hard fault. Problem was that the bootloader thread mode uses PSP. After jump to simple led blink application PSP is still used. MSP points to start of RAM. Now when interrupt occurs, registers are pushed where MSP points. This causes stack corruption?

    __set_CONTROL(__get_CONTROL() & ~0x2); // clear SPSEL → use MSP
    __ISB();
    

    Before jump we need to set thread mode to use MSP. This worked for me.


    I think you also need to use inline asm to jump to the app, so compiler don`t make anything extra stuff.

    __asm__ volatile(
    			"bx %0 \n"
    			:
    			: "r"(appResetHandler)
    			:
    );
    

    Hope this helps you.