STM32双Bank Flash在线升级失败后自动回滚的完整实现与边界条件分析
👁 3 阅读 · 2026-08-27 · 嵌入式
在嵌入式设备OTA升级中,升级失败可能导致设备变砖。STM32的双Bank Flash架构为安全升级提供了硬件基础,通过合理的软件设计,可以实现升级失败后的自动回滚。本文深入剖析双Bank Flash的工作原理,给出完整的实现代码,并详细分析回滚机制中的边界条件,帮助开发者构建高可靠的升级方案。
# STM32双Bank Flash在线升级失败后自动回滚的完整实现与边界条件分析
## 1. 为什么需要双Bank Flash回滚机制?
在OTA升级场景中,固件写入Flash的过程可能因断电、通信错误或校验失败而中断。若直接覆盖当前运行的程序,设备将无法启动。STM32(如F7、H7系列)的双Bank Flash允许将Flash划分为两个独立的Bank,一个运行当前固件,另一个写入新固件。升级完成后,通过切换启动Bank实现无缝切换。若新固件无法运行,系统可自动回退到旧Bank,确保设备永不砖化。
## 2. 双Bank Flash原理
- **硬件结构**:STM32的Flash被分为两个对称的Bank(如H743的2MB Flash分为Bank1和Bank2各1MB)。每个Bank可独立擦写,且支持同时操作(双Bank模式)。
- **启动配置**:通过选项字节(Option Bytes)中的`nBOOT1`和`BOOT0`引脚,或软件配置`FLASH_OPTCR`寄存器,可设置从Bank1或Bank2启动。
- **切换机制**:在运行时,通过修改`FLASH_OPTCR`的`BOR_LEV`和`SWAP_BANK`位,可动态切换启动Bank,无需硬件跳线。
## 3. 系统架构设计
- **Bootloader**:位于固定区域(如Bank1起始地址),负责启动应用、升级管理和回滚决策。
- **App1**:运行在Bank1(地址0x08000000),当前版本。
- **App2**:运行在Bank2(地址0x08040000),新版本。
- **升级流程**:
1. Bootloader接收新固件,写入Bank2。
2. 校验通过后,设置标志位(如备份在Flash的`upgrade_flag`),并切换启动Bank到Bank2,复位。
3. App2启动后,若运行正常(如心跳检测),则清除标志位,完成升级。
4. 若App2启动失败(如看门狗超时),Bootloader检测到标志位未清除,自动回滚到Bank1。
## 4. 关键实现步骤
### 4.1 配置Flash双Bank模式
在系统初始化时,确保Flash工作在双Bank模式。以STM32H743为例:
```c
void Flash_EnableDualBank(void) {
FLASH_OBProgramInitTypeDef ob_init;
HAL_FLASHEx_OBGetConfig(&ob_init);
if (ob_init.DualBank == 0) {
ob_init.DualBank = 1;
HAL_FLASHEx_OBProgram(&ob_init);
HAL_FLASH_OB_Launch(); // 重启生效
}
}
```
### 4.2 升级标志管理
在Flash的固定区域(如最后一个扇区)存储升级状态:
```c
#define UPGRADE_FLAG_ADDR 0x080FF000 // 示例地址
#define FLAG_UPGRADING 0xA5A5A5A5
#define FLAG_UPGRADE_OK 0x5A5A5A5A
#define FLAG_ROLLBACK 0xDEADBEEF
void WriteFlag(uint32_t addr, uint32_t val);
uint32_t ReadFlag(uint32_t addr);
```
### 4.3 Bootloader启动逻辑
Bootloader在启动时检查标志位:
```c
void Bootloader_Start(void) {
uint32_t flag = ReadFlag(UPGRADE_FLAG_ADDR);
if (flag == FLAG_UPGRADING) {
// 上次升级未完成,尝试启动App2,若失败则回滚
if (App2_CheckValid() && App2_Start()) {
// App2运行成功,等待其清除标志
} else {
// 回滚到App1
WriteFlag(UPGRADE_FLAG_ADDR, FLAG_ROLLBACK);
SwitchBank(BANK1);
NVIC_SystemReset();
}
} else {
// 正常启动App1
SwitchBank(BANK1);
// 跳转App1
}
}
```
### 4.4 App2运行成功确认
App2启动后,若初始化成功,应清除升级标志:
```c
void App2_Init(void) {
// 初始化外设、自检等
if (SelfTestPassed()) {
WriteFlag(UPGRADE_FLAG_ADDR, FLAG_UPGRADE_OK);
// 继续运行
} else {
// 触发看门狗复位,让Bootloader回滚
while(1);
}
}
```
### 4.5 看门狗保护
在Bootloader和App中启用独立看门狗(IWDG),确保异常时复位:
```c
void IWDG_Init(void) {
IWDG_HandleTypeDef hiwdg;
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 4095;
HAL_IWDG_Init(&hiwdg);
}
```
## 5. 边界条件分析
- **升级过程中断电**:若在写入Bank2时断电,Bank2可能损坏。但Bank1未受影响,Bootloader检测到标志为`FLAG_UPGRADING`,尝试启动Bank2失败后回滚。
- **App2启动但自检失败**:App2主动清除标志前,若自检失败,应进入死循环触发看门狗,Bootloader回滚。
- **标志位写入失败**:Flash写入可能因电压不稳失败。建议使用双备份标志,或使用Flash的ECC校验。
- **Bank切换失败**:若`SWAP_BANK`操作未生效,需检查选项字节是否被锁定。可在Bootloader中先解锁再操作。
- **回滚后App1数据丢失**:若App1在升级期间修改了共享数据,回滚后可能不一致。建议在升级前保存关键数据到备份区域。
- **多次回滚**:若新固件始终无法运行,系统会反复回滚。可增加回滚计数,超过阈值后停止升级,等待用户干预。
## 6. 完整代码示例(简化)
```c
// main.c - Bootloader
#include "main.h"
#define APP1_ADDR 0x08000000
#define APP2_ADDR 0x08040000
void SwitchBank(uint8_t bank) {
FLASH_OBProgramInitTypeDef ob;
HAL_FLASHEx_OBGetConfig(&ob);
ob.SWAP_BANK = (bank == BANK2) ? 1 : 0;
HAL_FLASHEx_OBProgram(&ob);
HAL_FLASH_OB_Launch();
}
int main(void) {
HAL_Init();
IWDG_Init();
Flash_EnableDualBank();
uint32_t flag = ReadFlag(UPGRADE_FLAG_ADDR);
if (flag == FLAG_UPGRADING) {
// 尝试启动App2
if (App2_CheckValid()) {
SwitchBank(BANK2);
NVIC_SystemReset();
} else {
WriteFlag(UPGRADE_FLAG_ADDR, FLAG_ROLLBACK);
SwitchBank(BANK1);
NVIC_SystemReset();
}
} else {
SwitchBank(BANK1);
// 跳转App1
void (*app1_entry)(void) = (void (*)(void)) (APP1_ADDR + 4);
__set_MSP(*(uint32_t*)APP1_ADDR);
app1_entry();
}
while(1);
}
// app2.c - 新固件
void App2_Main(void) {
if (SelfTest()) {
WriteFlag(UPGRADE_FLAG_ADDR, FLAG_UPGRADE_OK);
// 正常运行
} else {
while(1); // 触发看门狗
}
}
```
## 7. 注意事项
- **地址对齐**:确保两个Bank的起始地址正确,且向量表偏移已设置(`SCB->VTOR`)。
- **Flash擦写时间**:擦写Bank2时,应关闭中断或使用Flash操作中断,避免干扰。
- **电源稳定性**:升级期间应确保供电稳定,建议使用外部看门狗或电压监测。
- **测试覆盖**:模拟断电、校验失败、App异常等场景,验证回滚可靠性。
## 8. 总结
双Bank Flash回滚机制是嵌入式OTA升级的安全基石。通过合理的标志位管理和看门狗配合,能有效应对升级失败。但需注意边界条件,如断电时机、标志位可靠性等。本文提供的实现可作为参考,实际项目中需根据具体芯片和需求调整。