Skip to main content
EThom.3
Senior II
July 9, 2026
Question

Application with home-made bootloader: Application won't run automatically after upload

  • July 9, 2026
  • 2 replies
  • 36 views

This is really a minor one. More an inconvenience than an actual problem. But it would be nice to get it solved, and to gain some understanding of what actually happens.

I have a few different applications for various STM32 families, with a homemade bootloader added to it. The bootloaders are placed from address 0x08000000, and the applications begin at a higher address. Typically 0x08010000. The bootloaders and applications run fine. Bootloaders can start the applications, and applications can invoke the bootloaders. No problem there.

The only issue I have is when uploading a new application from STM32CubeProgrammer, through the ST-LINK V3. I typically do this when debugging. But when I do it, even if the “Run after programming” checkbox is checked, the microcontroller isn’t started correctly after upload. When I debug this issue, the microcontroller seems to be stuck in an endless loop in the application. (Not the bootloader.)

In the code I’m currently working on, this is the place where it gets stuck:

0801c444 <HAL_GetTick>:
* implementations in user file.
* @retval tick value
*/
__weak uint32_t HAL_GetTick(void)
{
return uwTick;
801c444: 4b01 ldr r3, [pc, #4] @ (801c44c <HAL_GetTick+0x8>)
801c446: 6818 ldr r0, [r3, #0]
}
801c448: 4770 bx lr
801c44a: bf00 nop
801c44c: 200013f0 .word 0x200013f0

0801c450 <HAL_Delay>:
* implementations in user file.
* @param Delay specifies the delay time length, in milliseconds.
* @retval None
*/
__weak void HAL_Delay(uint32_t Delay)
{
801c450: b538 push {r3, r4, r5, lr}
801c452: 4604 mov r4, r0
uint32_t tickstart = HAL_GetTick();
801c454: f7ff fff6 bl 801c444 <HAL_GetTick>
uint32_t wait = Delay;

/* Add a period to guaranty minimum wait */
if (wait < HAL_MAX_DELAY)
801c458: 1c63 adds r3, r4, #1
{
wait += (uint32_t)uwTickFreq;
801c45a: bf1c itt ne
801c45c: 4b05 ldrne r3, [pc, #20] @ (801c474 <HAL_Delay+0x24>)
801c45e: 781b ldrbne r3, [r3, #0]
uint32_t tickstart = HAL_GetTick();
801c460: 4605 mov r5, r0
wait += (uint32_t)uwTickFreq;
801c462: bf18 it ne
801c464: 18e4 addne r4, r4, r3
}

while ((HAL_GetTick() - tickstart) < wait)
801c466: f7ff ffed bl 801c444 <HAL_GetTick>
801c46a: 1b43 subs r3, r0, r5
801c46c: 42a3 cmp r3, r4
801c46e: d3fa bcc.n 801c466 <HAL_Delay+0x16>
{
}
}

It keeps repeating the while loop (instructions 0x0801C466 - 0x0801C46E), branching to HAL_GetTick along the way. I have no idea why it does this.

If I have STM32CubeProgrammer reset and run the microcontroller (CPU tab → Hardware Reset + Run), it starts up as expected, exactly as after power-on. So after uploading the application I need to close all the pop-up windows, switch to the CPU tap, and then click Hardware Reset and Run. Yeah, I know it is a minor thing, but I still find it inconvenient.

I remember seeing something that might be a solution, but now I cannot find it. If I recall correctly, it had something to do with USER_VECT_TAB_ADDRESS found in system_stm32xxx.c, but I am not sure.

Does anyone recognise this, and know what to do? And perhaps why?

2 replies

EThom.3
EThom.3Author
Senior II
July 9, 2026

I may have found a solution. Sort of. However, I don’t know if it is a bad one, that may create problems elsewhere.

In the application file “system_stm32l4xx.c”, I uncommented #define USER_VECT_TAB_ADDRESS. Then I changed VECT_TAB_BASE_ADDRESS from FLASH_BASE to 0x08010000, which is the application start address.

Now, when STM32CubeProgrammer has finished uploading an application, the microcontroller starts the application. Not the bootloader, which is normally the first piece of code to be run.

So while it is a solution, it is clearly not the perfect solution.

Pavel A.
July 9, 2026

> So while it is a solution, it is clearly not the perfect solution.

Having to modify a “library” file is not perfect. If you look at this file a bit longer, intent of its authors would be clear.

https://github.com/STMicroelectronics/cmsis-device-l4/blob/ca0bfa2b8b68dc2994b27fba0a10dfd28d086ee1/Source/Templates/system_stm32l4xx.c#L121

All the needed macros/symbols can be defined in the project preprocessor settings instead of modifying the .c file.

USER_VECT_TAB_ADDRESS  1

VECT_TAB_OFFSET  0x10000

Then this line should compile properly:

https://github.com/STMicroelectronics/cmsis-device-l4/blob/ca0bfa2b8b68dc2994b27fba0a10dfd28d086ee1/Source/Templates/system_stm32l4xx.c#L203

 

>  Not the bootloader, which is normally the first piece of code to be run.

If the app depends on the bootloader to prepare clocks, memories and so on, but the debugger starts the app directly via its vector address - the app can detect this and call NVIC_SystemReset, to ensure going thru the bootloader. Or, the debugger can be configured to always start from the fixed bootloader address.

Anyway, the problem is solved and all is good now.

EThom.3
EThom.3Author
Senior II
July 9, 2026

Thanks, Pavel A.

Interesting. Will have a close look at that.

mƎALLEm
ST Technical Moderator
July 9, 2026

​Hello @EThom.3 ,

Custom bootloader related questions need to be posted in “STM32 MCUs Embedded software” forum board.

ST bootloader related questions need to be posted in “STM32 MCUs Products” forum board.

I move the thread then.

To give better visibility on the answered topics, please click "Best answer" on the reply which solved your issue or answered your question.