STM32H7双Bank模式下的固件升级失败自动回滚机制实现
👁 2 阅读 · 2026-08-27 · 嵌入式
在嵌入式设备中,固件升级失败可能导致设备变砖,而STM32H7的双Bank闪存架构为安全升级提供了硬件基础。本文深入讲解如何利用双Bank特性实现升级失败后的自动回滚,包括原理分析、配置步骤、完整代码示例及注意事项,帮助开发者构建高可靠的OTA升级方案。
# STM32H7双Bank模式下的固件升级失败自动回滚机制实现
## 1. 为什么需要自动回滚?
在OTA(空中升级)或现场升级中,固件写入过程可能因断电、通信错误或校验失败而中断。若新固件损坏,设备将无法启动。传统单Bank方案只能依赖外部备份或人工干预,而STM32H7的双Bank闪存允许同时存储两个固件副本,配合硬件支持的切换机制,可实现无缝回滚,极大提升系统可靠性。
## 2. 双Bank模式原理
STM32H7系列(如H743、H750)内置双Bank闪存,每个Bank容量相同(如1MB时每个512KB)。通过配置选项字节(Option Bytes)中的`DBANK`位,可切换为双Bank模式。此时,闪存地址空间被划分为两个独立区域:
- **Bank 1**:通常存放当前运行固件(Active)
- **Bank 2**:存放新固件(Pending)
系统复位后,硬件根据`BOOT_ADD0`和`BOOT_ADD1`选项字节决定从哪个Bank启动。若启动失败(如看门狗复位),可通过软件切换启动地址,实现回滚。
## 3. 实现方案设计
### 3.1 整体流程
1. 设备运行在Bank 1,接收新固件并写入Bank 2。
2. 写入完成后,设置标志位(如备份寄存器或Flash特定区域),标记新固件待验证。
3. 切换启动地址到Bank 2并软复位。
4. 若Bank 2固件运行正常(如应用层上报心跳),则清除标志位,完成升级。
5. 若Bank 2启动失败(如看门狗超时),系统自动回滚到Bank 1。
### 3.2 关键配置
- **选项字节设置**:使用STM32CubeProgrammer或代码配置`DBANK=1`,并设置`BOOT_ADD0`和`BOOT_ADD1`。
- **看门狗**:启用独立看门狗(IWDG),在启动早期喂狗,若固件异常则触发复位。
- **标志位存储**:使用备份寄存器(BKP)或Flash末尾扇区存储升级状态。
## 4. 代码实现
以下代码基于STM32H743,使用HAL库,演示核心逻辑。
### 4.1 配置双Bank模式(初始化代码)
```c
/* 启用双Bank模式 */
void Flash_EnableDualBank(void)
{
FLASH_OBProgramInitTypeDef ob;
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
ob.OptionType = OPTIONBYTE_BANK;
ob.Bank = FLASH_BANK_1; // 配置Bank1选项
ob.BankConfig = FLASH_BANK_DUAL; // 双Bank模式
HAL_FLASH_OBProgram(&ob);
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
/* 需要重新上电生效 */
HAL_NVIC_SystemReset();
}
```
### 4.2 固件写入Bank 2
```c
/* 将新固件写入Bank2,地址从0x08100000开始(假设Bank2基址) */
void Write_FirmwareToBank2(uint32_t *data, uint32_t len)
{
uint32_t addr = 0x08100000;
HAL_FLASH_Unlock();
for (uint32_t i = 0; i < len; i += 4) {
if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, *(uint64_t*)(data+i)) != HAL_OK) {
// 错误处理
}
addr += 8; // 双字写入
}
HAL_FLASH_Lock();
}
```
### 4.3 切换启动Bank并复位
```c
/* 设置启动地址为Bank2,并软复位 */
void SwitchToBank2AndReset(void)
{
/* 设置标志位,表示新固件待验证 */
Set_UpdateFlag(0xAA55); // 写入备份寄存器
/* 修改选项字节BOOT_ADD1为Bank2起始地址 */
FLASH_OBProgramInitTypeDef ob;
HAL_FLASH_Unlock();
HAL_FLASH_OB_Unlock();
ob.OptionType = OPTIONBYTE_USER;
ob.UserType = OB_USER_BOOT_ADD1;
ob.BOOT_ADD1 = 0x08100000; // Bank2地址
HAL_FLASH_OBProgram(&ob);
HAL_FLASH_OB_Lock();
HAL_FLASH_Lock();
HAL_NVIC_SystemReset();
}
```
### 4.4 启动时检查回滚条件
```c
void Check_And_Rollback(void)
{
uint16_t flag = Get_UpdateFlag();
if (flag == 0xAA55) { // 有未验证的更新
/* 检查当前运行Bank */
if (__HAL_FLASH_GET_BANK() == FLASH_BANK_2) {
/* 运行在Bank2,说明新固件已启动,但需验证 */
if (System_Check() == PASS) {
/* 验证通过,清除标志 */
Clear_UpdateFlag();
// 正常继续运行
} else {
/* 验证失败,回滚到Bank1 */
SwitchToBank1();
}
} else {
/* 运行在Bank1,说明Bank2启动失败,自动回滚 */
Clear_UpdateFlag();
// 继续运行Bank1固件
}
}
}
void SwitchToBank1(void)
{
/* 设置BOOT_ADD0为Bank1地址 */
// 类似SwitchToBank2,但地址改为0x08000000
HAL_NVIC_SystemReset();
}
```
### 4.5 看门狗配合
```c
/* 初始化IWDG,超时1秒 */
void IWDG_Init(void)
{
IWDG_HandleTypeDef hiwdg;
hiwdg.Instance = IWDG;
hiwdg.Init.Prescaler = IWDG_PRESCALER_64;
hiwdg.Init.Reload = 4095; // 约1秒
HAL_IWDG_Init(&hiwdg);
}
/* 在主循环中喂狗 */
while (1) {
HAL_IWDG_Refresh(&hiwdg);
// 应用逻辑
}
```
## 5. 注意事项
- **地址映射**:确认具体型号的Bank基址(H743的Bank1为0x08000000,Bank2为0x08100000)。
- **选项字节修改**:修改后需复位生效,且注意保护其他选项字节。
- **固件大小**:确保固件不超过Bank容量,否则需压缩或分块。
- **验证机制**:建议在应用层增加CRC校验或版本检查,避免误判。
- **看门狗**:在启动早期喂狗,防止正常启动被误复位。
- **中断向量表**:若固件从Bank2启动,需重定位中断向量表(SCB->VTOR)。
## 6. 总结
通过STM32H7的双Bank模式,我们实现了升级失败后的自动回滚,无需外部硬件,仅靠内部闪存和看门狗即可保证设备始终可运行。该方案适用于对可靠性要求高的工业、汽车等嵌入式场景。开发者可根据实际需求调整验证逻辑和回滚策略,构建更健壮的OTA系统。