STM32双Bank Flash在线升级不掉电变砖:架构设计与实战指南
👁 2 阅读 · 2026-08-27 · 嵌入式
在STM32的OTA升级中,擦写Flash期间掉电是导致设备变砖的头号杀手。本文深入剖析双Bank Flash的硬件特性与原子切换机制,结合IAP跳转、写保护与CRC校验,给出一个防掉电变砖的完整软件架构。通过配置步骤、代码示例和注意事项,助你构建工业级可靠的在线升级方案。
# STM32双Bank Flash在线升级:如何避免擦写期间掉电变砖
## 1. 问题根源:为什么擦写掉电会变砖?
在线升级(OTA)的核心是擦除并重写内部Flash。STM32的Flash编程(尤其是擦除)需要较高电压(如1.8V),且操作期间CPU若掉电,Flash内容可能处于**未定义状态**——既不是旧固件,也不是新固件,导致设备无法启动。
传统单Bank方案中,Bootloader和App共享同一Flash区域,升级时直接覆盖App区。一旦掉电,Bootloader可能也被破坏(若Bootloader与App相邻),或App区残留半写数据,系统无法跳转。
## 2. 双Bank Flash:硬件级的安全屏障
部分STM32系列(如STM32F7、H7、L4+)支持**双Bank Flash**。其核心特性:
- **物理隔离**:Flash分为Bank0和Bank1,可独立擦写。
- **原子切换**:通过设置选项字节(Option Bytes)中的`BOR_LEV`或`DBANK`位,可让系统在重启时从另一个Bank启动,切换过程由硬件完成,无需软件干预。
- **并行编程**:支持同时擦写一个Bank而运行另一个Bank(需注意总线仲裁)。
**关键优势**:升级时,App运行在Bank0,新固件写入Bank1。即使Bank1擦写掉电,Bank0的旧固件完好无损,设备仍可正常运行。下次升级时,再尝试从Bank1启动或回滚。
## 3. 防掉电变砖的软件架构
### 3.1 总体流程
```
Bootloader (Bank0起始) --> 检查标志 --> 跳转至Bank0或Bank1的App
App运行 --> 收到升级包 --> 写入非活动Bank --> 设置切换标志 --> 软复位
Bootloader --> 校验新Bank CRC --> 切换Bank --> 启动新App
```
### 3.2 关键模块
- **Bootloader**:固定位于Bank0起始地址(如0x08000000),负责启动选择、固件校验、跳转。
- **App**:可位于Bank0或Bank1,通过编译链接地址区分。
- **升级标志**:存储在备份寄存器(RTC Backup Register)或Flash末尾的专用区域,用于指示升级状态。
## 4. 配置步骤(以STM32H743为例)
### 4.1 硬件配置
1. **使能双Bank模式**:在CubeMX中,选择`Memory` -> `Flash`,设置`Dual Bank Mode`为`Enabled`。
2. **配置Flash等待周期**:根据主频调整`LATENCY`,确保Flash访问稳定。
3. **分配地址**:
- Bank0:0x08000000 - 0x080FFFFF(1MB)
- Bank1:0x08100000 - 0x081FFFFF(1MB)
### 4.2 软件配置
1. **Bootloader工程**:
- 链接脚本:仅包含Bank0区域,起始地址0x08000000,长度可设为0x20000(128KB)。
- 中断向量表重定位:在启动代码中设置`SCB->VTOR = 0x08000000`。
2. **App工程**(以Bank0为例):
- 链接脚本:起始地址0x08020000,长度0xE0000(896KB)。
- 在`main`函数开头重定位向量表:`SCB->VTOR = 0x08020000`。
3. **升级流程代码**(在App中):
```c
// 写入新固件到非活动Bank(假设当前运行在Bank0,写入Bank1)
void OTA_WriteFirmware(uint32_t *data, uint32_t len) {
uint32_t dst_addr = 0x08100000; // Bank1起始
// 擦除Bank1(注意:擦除前需解锁Flash)
HAL_FLASH_Unlock();
FLASH_Erase(&flash_erase_cfg); // 配置擦除Bank1
// 编程数据
for (uint32_t i = 0; i < len; i += 8) {
HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, dst_addr + i, *(uint64_t*)(data+i));
}
HAL_FLASH_Lock();
// 设置升级标志(备份寄存器)
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x5A5A); // 标志值
// 软复位
NVIC_SystemReset();
}
```
4. **Bootloader跳转逻辑**:
```c
void Bootloader_JumpToApp(void) {
uint32_t app_addr;
// 检查升级标志
if (HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) == 0x5A5A) {
// 尝试从Bank1启动
app_addr = 0x08100000;
// 校验Bank1的CRC(需提前计算并存储)
if (CRC_Check(app_addr, APP_SIZE) == PASS) {
// 切换Bank(通过设置选项字节)
FLASH_OB_Unlock();
// 设置DBANK位,使系统从Bank1启动
FLASH_OB_Program(OB_DBANK, 1);
FLASH_OB_Launch();
// 复位后硬件自动从Bank1启动
} else {
// 校验失败,回滚到Bank0
app_addr = 0x08020000;
// 清除标志
HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0);
}
} else {
app_addr = 0x08020000; // 默认Bank0
}
// 跳转
if (*(volatile uint32_t*)app_addr & 0x2FFE0000) { // 检查栈顶地址合法性
// 设置主栈指针
__set_MSP(*(volatile uint32_t*)app_addr);
// 跳转至复位向量
void (*jump)(void) = (void*)(*(volatile uint32_t*)(app_addr + 4));
jump();
}
}
```
## 5. 完整代码示例(简化版)
```c
// 升级包接收与写入(伪代码)
void OTA_Process(void) {
// 1. 接收升级包(通过UART/以太网)
uint8_t *buffer = malloc(MAX_PACKET_SIZE);
// 2. 校验包完整性(CRC32)
if (CRC32_Verify(buffer, packet_len) != PASS) {
error_handle();
return;
}
// 3. 写入非活动Bank
uint32_t active_bank = GetActiveBank(); // 读取当前运行Bank
uint32_t target_bank = (active_bank == BANK0) ? BANK1_ADDR : BANK0_ADDR;
Flash_EraseBank(target_bank);
Flash_WriteData(target_bank, buffer, packet_len);
// 4. 设置升级标志
SetUpgradeFlag(target_bank);
// 5. 软复位
NVIC_SystemReset();
}
// 在Bootloader中,启动前检查
void CheckAndJump(void) {
uint32_t flag = GetUpgradeFlag();
if (flag != 0) {
uint32_t target = (flag == BANK1_FLAG) ? BANK1_ADDR : BANK0_ADDR;
if (VerifyAppCRC(target)) {
// 切换Bank(硬件切换)
SwitchBank(target);
} else {
// 回滚到旧固件
ClearFlag();
}
}
JumpToApp(GetActiveBank());
}
```
## 6. 注意事项与最佳实践
- **Flash写保护**:在Bootloader中启用对Bank0的写保护,防止App意外擦除Bootloader。可通过选项字节设置`WRP`。
- **升级标志的可靠性**:使用备份寄存器(掉电不丢失)而非RAM,并采用双标志(如0xA5A5和0x5A5A)防止误判。
- **CRC校验**:在写入后、切换前,必须对新固件进行CRC或SHA校验,确保完整性。
- **看门狗**:在擦写过程中喂狗,防止因长时间擦除导致系统复位。
- **电源监测**:使用PVD(可编程电压检测器)监测电压,若电压低于阈值,立即停止擦写并进入安全状态。
- **测试**:在开发阶段,模拟掉电场景(如随机断电)进行压力测试,验证恢复机制。
## 7. 总结
双Bank Flash配合原子切换机制,为OTA升级提供了硬件级的安全保障。通过合理的软件设计——Bootloader引导、标志管理、CRC校验和回滚策略,即使擦写期间掉电,设备也能自动恢复至旧固件,彻底避免变砖。该方案在工业控制、智能家居等对可靠性要求高的场景中极具价值。