一、双Bank架构与OTA零停机原理
STM32H7系列(如H743/H750)内置双Bank闪存,每个Bank容量相等(如1MB时各512KB)。通过配置选项字节DBANK,可将闪存划分为两个独立区域,支持并行编程/擦除与无缝切换。
零停机核心机制:
- 系统从Bank1运行,同时可对Bank2进行擦写(无需暂停应用)。
- 升级完成后,通过置位
FLASH_CR的SWAP_BANK位,硬件自动交换Bank映射,复位后CPU从新固件启动。 - 若新固件自检失败,可再次切换回旧Bank,实现秒级回滚。
关键优势:
- 升级过程不中断实时控制(如电机、通信)。
- 回滚无需外部备份,仅依赖闪存切换。
- 支持多版本固件驻留,便于A/B测试。
二、系统总体设计
2.1 内存布局
| 区域 | 地址范围 | 用途 | |------|----------|------| | Bootloader | 0x08000000 - 0x0800FFFF | 启动引导、FDCAN接收、校验逻辑 | | App_Bank1 | 0x08010000 - 0x0807FFFF | 当前运行固件 | | App_Bank2 | 0x08080000 - 0x080FFFFF | 待升级固件 |
2.2 升级流程
- Bootloader通过FDCAN接收固件包,写入Bank2(非活动区)。
- 接收完成后,计算CRC32并与发送端校验值比对。
- 校验通过,置位
SWAP_BANK并软复位。 - 新固件启动后,运行自检(如心跳、外设测试),若失败则调用回滚API。
三、FDCAN通信协议设计
采用经典CAN 2.0B帧,ID分段:
-
0x100:握手请求 -
0x101:固件数据(每帧最多8字节) -
0x102:结束帧(含CRC) -
0x103:应答帧(ACK/NACK)
数据帧格式:[包序号(2字节)][数据(6字节)],包序号用于重组。
四、关键代码实现
4.1 双Bank切换配置
void Flash_SwapBank(void) {
FLASH_OBProgramInitTypeDef ob;
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
// 读取当前DBANK配置,确保为双Bank模式
ob.OptionType = OPTIONBYTE_BYTE;
ob.BYTE = 0; // 实际需根据芯片调整
HAL_FLASH_OB_GetConfig(&ob);
// 置位SWAP_BANK位(需先设置FLASH_CR的SWAP_BANK)
FLASH->CR |= FLASH_CR_SWAP_BANK;
while (FLASH->SR & FLASH_SR_BSY);
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
// 软复位生效
NVIC_SystemReset();
}
4.2 固件写入Bank2(非活动区)
void Write_Firmware_To_Bank2(uint32_t addr, uint8_t *data, uint32_t len) {
HAL_FLASH_Unlock();
FLASH_EraseInitTypeDef erase;
uint32_t error;
// 擦除Bank2(地址0x08080000起,共512KB)
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
erase.Banks = FLASH_BANK_2;
erase.Sector = 0;
erase.NbSectors = 8; // 根据扇区大小调整
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASHEx_Erase(&erase, &error);
// 写入数据(按32位编程)
for (uint32_t i = 0; i < len; i += 8) {
uint64_t val = *(uint64_t *)(data + i);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, addr + i, val);
}
HAL_FLASH_Lock();
}
4.3 回滚机制实现
void Rollback_Firmware(void) {
// 检查当前运行Bank,若为Bank2则切换回Bank1
if (IS_ACTIVE_BANK2()) {
Flash_SwapBank(); // 切换后复位,自动运行Bank1
}
// 若为Bank1,则无需操作(旧固件仍有效)
}
// 在应用启动时调用自检,失败则回滚
void App_Self_Test(void) {
if (Check_CRC() != PASS) {
Rollback_Firmware();
}
}
五、Bootloader跳转逻辑
void Jump_To_App(uint32_t app_addr) {
// 检查栈顶地址合法性
if (((*(volatile uint32_t *)app_addr) & 0x2FFE0000) != 0x20000000) {
return;
}
// 设置MSP并跳转
__set_MSP(*(volatile uint32_t *)app_addr);
void (*app_reset)(void) = (void (*)(void))(*(volatile uint32_t *)(app_addr + 4));
app_reset();
}
六、注意事项与优化建议
-
中断向量表重定向:应用固件需在启动时设置
SCB->VTOR = 0x08010000(或当前Bank基址)。 - FDCAN缓冲区管理:使用DMA接收,避免CPU占用,确保高速传输不丢帧。
- CRC校验:建议使用硬件CRC单元(如CRC32),加速计算。
- 看门狗:在升级过程中喂狗,防止意外复位导致半写入状态。
- 双Bank切换时序:切换期间闪存不可访问,需确保无中断干扰。
- 测试覆盖:模拟断电、通信中断等异常,验证回滚可靠性。
七、总结
本文基于STM32H7双Bank特性,结合FDCAN实现了一套零停机OTA方案。通过硬件级Bank切换,避免了传统双区拷贝的时间开销,回滚机制保障了升级失败时的系统可用性。开发者可根据实际需求调整帧格式与校验策略,该架构同样适用于其他支持双Bank的MCU(如STM32L4+)。