1. Introduction
A bootloader is a critical program responsible for booting and updating embedded devices. In automotive ECUs, during OTA upgrades, the bootloader is responsible for downloading, verifying, and writing new firmware, ensuring that in case of an anomaly, it can revert to the old version to guarantee driving safety. Mobile system updates rely on the bootloader to flash the system, verify signatures, and start the system kernel. Similarly, the BIOS or UEFI in computers is a type of bootloader used to initialize hardware and load the operating system. Through a unified download interface and security mechanisms, the bootloader allows devices to be remotely maintained, updated online, and prevents malicious tampering, thereby reducing production and maintenance costs while ensuring system stability and security throughout its lifecycle.

Ultimately, the bootloader is also a project, similar in nature to an application.
The simple bootloader implemented by the author mainly serves the following functions:
- Erase the existingapp
- Receive newappfirmware
- Write toflashmemory
- Jump to the newapp
2. Development Approach
The author developed based on the STM32F103CBT6, which has a flash size of 128KB compared to the more common STM32F103C8T6.


In the flash startup mode of the STM32 microcontroller, the startup address is always0x8000000, which is also the starting address of the bootloader.
The bootloader size is12616bytes, requiring12pages plus a little bit (0.32 pages)
Therefore, the judgment flag is placed at the beginning of page12, which is at address0x08003400
The starting address of the app is0x08003800, (the beginning of page 13)
Memory distribution is as follows:

The boot detection flag signal being true indicates that an upgrade is needed, erasing the app and writing new firmware; otherwise, it indicates no upgrade is needed, and it directly jumps to the app.

3. Practical Development
The app project needs to set the starting address. If usingKeil, the settings are as follows:

Set the vector table offset address in the system_stm32f1xx.c file.

In the app, only a blinkingLED is run as a demonstration.

The starting address of the boot project is0x8000000, so no offset is needed.
Set the address macros and upgrade request flag.
#define APP_START_ADDRESS 0x08003800
#define APP_REQUEST_ADDRESS 0x08003400
updateReq = *(uint8_t*)(APP_REQUEST_ADDRESS);
After powering on or resetting, the bootloader checks whether an upgrade is needed.
if (updateReq == 1 || updateReq == 0xFF){
state = STATE_INIT;
HAL_FLASH_Unlock();
HAL_FLASHEx_Erase(&req, &error);
}else{
state = STATE_JUMP;
}
Switch states in the main loop.
The author created a matching upper computer.

When the upper computer presses boot, it sends an upgrade request composed of a frame header0xAA + 0x22222222 + CRC, which the app detects and sets the flag and resets.

When the upper computer presses flash, it begins the transmission of the new firmware, receiving and writing to flash in the bootloader.
case STATE_FLASH:{
/**
* flash code
*/
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_START_ADDRESS + flashPackCnt * 4, *(uint32_t*) (prx + 1));
flashPackCnt++;
/* flash finish, notify PC by sending 0x77 */
tx[0] = 0x77;
HAL_UART_Transmit_IT(&huart1, tx, 1);
/* go to wait state for getting next data from PC*/
HAL_UART_Receive_IT(&huart1, rx, 6);
state = STATE_WAIT;
if ((flashPackCnt) * 4 == dataSize) {
state = STATE_END;
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, APP_REQUEST_ADDRESS, 0);
}
break;
}
The upper computer sends0x20 + a four-byte firmware packet + CRC, and the microcontroller responds with0x77 as an acknowledgment signal.

After the writing is complete, jump to the new app.
case STATE_JUMP:{
deinitEverything();
uint32_t stacktop = *((__IO uint32_t *)APP_START_ADDRESS);
__set_MSP(stacktop);
app_func_t app_func = (app_func_t)(*((__IO uint32_t *)(APP_START_ADDRESS + 4)));
app_func();
}
After the upgrade, debugging reads the memory, and the content at0x08003800 matches the compiled app’s hex content.

Since the upgrade request has been erased, the next reset will directly jump to the app.