Bootloader关键代码实现
以下是基于STM32F4的Bootloader核心代码(使用HAL库):
c
#include "stm32f4xx_hal.h"
#include "string.h"
// 分区地址定义(根据实际芯片调整)
#define BOOT_ADDR 0x08000000
#define APP1_ADDR 0x08008000
#define APP2_ADDR 0x08028000
#define PARAM_ADDR 0x08048000
#define APP_SIZE 0x20000 // 128KB(App1和App2大小)
// 参数区结构体(存放OTA状态)
typedef struct {
uint16_t ota_flag; // OTA标志:0xAA55表示需要升级
uint32_t app1_crc; // App1区固件CRC
uint32_t app2_crc; // App2区固件CRC
uint8_t app1_version[8];// App1版本号
uint8_t app2_version[8];// App2版本号
} ParamTypeDef;
ParamTypeDef param;
// 读取参数区数据
void param_read(ParamTypeDef *p) {
memcpy(p, (void*)PARAM_ADDR, sizeof(ParamTypeDef));
}
// 计算CRC32(用于固件校验)
uint32_t crc32_calc(uint32_t start_addr, uint32_t size) {
uint32_t crc = 0xFFFFFFFF;
uint8_t *data = (uint8_t*)start_addr;
for (uint32_t i = 0; i < size; i++) {
crc ^= data[i];
for (int j = 0; j < 8; j++) {
crc = (crc >> 1) ^ ((crc & 1) ? 0xEDB88320 : 0);
}
}
return ~crc;
}
// 擦除Flash扇区(需根据芯片扇区表调整)
void flash_erase(uint32_t addr, uint32_t size) {
HAL_FLASH_Unlock();
FLASH_EraseInitTypeDef erase;
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
erase.Sector = get_sector(addr); // 自定义函数:根据地址获取扇区号
erase.NbSectors = get_sector_count(addr, size); // 计算需要擦除的扇区数
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
uint32_t page_error;
HAL_FLASHEx_Erase(&erase, &page_error);
HAL_FLASH_Lock();
}
// 写入数据到Flash
void flash_write(uint32_t addr, uint8_t *data, uint32_t len) {
HAL_FLASH_Unlock();
for (uint32_t i = 0; i < len; i += 4) {
uint32_t word = *(uint32_t*)(data + i);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, addr + i, word);
}
HAL_FLASH_Lock();
}
// 跳转到App程序
void jump_to_app(uint32_t app_addr) {
// 检查App地址是否合法(栈顶地址需在RAM范围内)
uint32_t stack_top = *(volatile uint32_t*)app_addr;
if (stack_top < 0x20000000 || stack_top > 0x20000000 + RAM_SIZE) {
return;
}
// 关闭中断,避免干扰App
__disable_irq();
// 跳转到App的复位向量(app_addr + 4是复位函数地址)
void (*app_reset_handler)(void) = (void*)*(volatile uint32_t*)(app_addr + 4);
// 设置主栈指针(MSP)为App的栈顶
__set_MSP(stack_top);
// 跳转执行
app_reset_handler();
}
int main(void) {
HAL_Init();
SystemClock_Config(); // 配置系统时钟(如168MHz)
uart_init(115200); // 初始化UART(用于调试或接收固件)
// 1. 读取参数区
param_read(¤tparam);
// 2. 检查是否需要升级(ota_flag == 0xAA55)
if (param.ota_flag == 0xAA55) {
// 3. 校验App2区新固件的CRC
uint32_t calc_crc = crc32_calc(APP2_ADDR, APP_SIZE);
if (calc_crc == param.app2_crc) {
// 4. 擦除App1区,将App2区的新固件复制到App1区
flash_erase(APP1_ADDR, APP_SIZE);
flash_write(APP1_ADDR, (uint8_t*)APP2_ADDR, APP_SIZE);
// 5. 更新参数区(清除OTA标志,更新App1版本和CRC)
param.ota_flag = 0x0000;
memcpy(param.app1_version, param.app2_version, 8);
param.app1_crc = param.app2_crc;
flash_erase(PARAM_ADDR, sizeof(ParamTypeDef));
flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));
} else {
// 校验失败,保留旧固件
param.ota_flag = 0x0000;
flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));
}
}
// 6. 跳转到App1区运行
jump_to_app(APP1_ADDR);
// 若跳转失败,进入死循环
while (1) {}
}
App关键代码示例(接收固件并设置标志)
c
// App中接收新固件并写入App2区
void ota_receive_firmware() {
uint8_t firmware_buffer[1024]; // 接收缓冲区
uint32_t firmware_size = 0; // 已接收固件大小
uint32_t app2_addr = APP2_ADDR;
// 1. 擦除App2区
flash_erase(APP2_ADDR, APP_SIZE);
// 2. 循环接收固件数据(通过UART/ETH等)
while (firmware_size < APP_SIZE) {
uint16_t recv_len = uart_receive(firmware_buffer, 1024); // 实际项目需实现超时处理
if (recv_len == 0) break;
// 写入Flash
flash_write(app2_addr, firmware_buffer, recv_len);
app2_addr += recv_len;
firmware_size += recv_len;
}
// 3. 计算新固件CRC并更新参数区
ParamTypeDef param;
param_read(¤tparam);
param.app2_crc = crc32_calc(APP2_ADDR, firmware_size);
memcpy(param.app2_version, "V2.0.0", 8); // 新固件版本号
param.ota_flag = 0xAA55; // 设置OTA标志
flash_erase(PARAM_ADDR, sizeof(ParamTypeDef));
flash_write(PARAM_ADDR, (uint8_t*)¤tparam, sizeof(ParamTypeDef));
// 4. 重启系统,触发Bootloader升级
NVIC_SystemReset();
}