STM32H7双Bank切换实现OTA零停机:基于FDCAN的固件回滚机制设计
👁 1 阅读 · 2026-08-27 · 嵌入式
在工业与车载应用中,OTA升级的可靠性直接决定系统可用性。STM32H7凭借双Bank闪存架构,可实现无缝固件切换与即时回滚。本文深入解析双Bank原理,结合FDCAN通信,设计一套零停机OTA流程,涵盖地址映射、跳转逻辑、校验策略及完整代码示例,助你构建高鲁棒性的嵌入式升级方案。
# 一、双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 升级流程
1. Bootloader通过FDCAN接收固件包,写入Bank2(非活动区)。
2. 接收完成后,计算CRC32并与发送端校验值比对。
3. 校验通过,置位`SWAP_BANK`并软复位。
4. 新固件启动后,运行自检(如心跳、外设测试),若失败则调用回滚API。
# 三、FDCAN通信协议设计
采用经典CAN 2.0B帧,ID分段:
- `0x100`:握手请求
- `0x101`:固件数据(每帧最多8字节)
- `0x102`:结束帧(含CRC)
- `0x103`:应答帧(ACK/NACK)
数据帧格式:`[包序号(2字节)][数据(6字节)]`,包序号用于重组。
# 四、关键代码实现
## 4.1 双Bank切换配置
```c
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(非活动区)
```c
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 回滚机制实现
```c
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跳转逻辑
```c
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+)。