STM32双Bank切换实现OTA零停机:基于Flash双区映射的在线升级实战
👁 1 阅读 · 2026-08-27 · 嵌入式
在物联网设备中,OTA升级常因Flash擦写导致系统短暂停机,影响用户体验。本文深入剖析STM32双Bank架构原理,通过Flash双区映射实现无缝切换,让固件更新在后台完成,应用零停机运行。文章涵盖硬件基础、配置步骤、完整代码示例及关键注意事项,助你打造高可靠的在线升级方案。
# STM32双Bank切换实现OTA零停机:基于Flash双区映射的在线升级实战
## 1. 为什么需要双Bank?
传统OTA升级流程:下载固件到临时区 → 擦除主区 → 写入新固件 → 重启。此过程擦写期间应用无法运行,造成数秒甚至更长的停机。对于工业控制、医疗设备或边缘网关,这种中断不可接受。
STM32(如F4、L4、H7系列)支持双Bank Flash架构,将Flash划分为两个独立Bank(如Bank1和Bank2),每个Bank可独立擦写。通过硬件映射,CPU可从任一Bank启动,实现“后台下载+切换启动”的零停机升级。
## 2. 双Bank原理与映射机制
### 2.1 硬件架构
- 以STM32F4为例,Flash容量1MB时,Bank1地址0x08000000~0x0807FFFF,Bank2地址0x08080000~0x080FFFFF。
- 每个Bank拥有独立擦写控制器,可并行操作(部分型号支持)。
- 启动映射由选项字节(Option Bytes)中的BOOT_SW位控制:置1时,CPU从Bank2启动,且Bank1映射到0x08000000,Bank2映射到0x08080000;置0时默认从Bank1启动。
### 2.2 切换流程
1. 应用运行在Bank1,新固件下载到Bank2(通过Bootloader或应用自身)。
2. 校验新固件CRC/签名。
3. 设置BOOT_SW位,触发软复位。
4. 复位后CPU从Bank2启动,应用无缝切换,停机时间仅复位周期(微秒级)。
## 3. 工程配置步骤
### 3.1 链接脚本调整
将Flash划分为两个区域,分别对应Bank1和Bank2。以STM32F407VG(1MB Flash)为例,修改链接脚本(.ld):
```c
MEMORY
{
FLASH_BANK1 (rx) : ORIGIN = 0x08000000, LENGTH = 512K
FLASH_BANK2 (rx) : ORIGIN = 0x08080000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
```
编译时,为Bank1和Bank2分别生成固件,或使用同一固件通过宏控制起始地址。
### 3.2 选项字节设置
使用STM32CubeProgrammer或代码设置BOOT_SW位。代码方式:
```c
// 解锁选项字节
FLASH_OB_Unlock();
// 设置BOOT_SW为1(从Bank2启动)
FLASH_OB_UserConfig(OB_BOOT_SW_SET);
// 保存选项字节
FLASH_OB_Launch();
```
注意:设置后需复位生效,且选项字节写入会触发一次Flash擦写,但时间极短(毫秒级)。
## 4. 完整代码示例
以下示例实现:应用运行在Bank1,接收新固件写入Bank2,校验后切换。
### 4.1 固件写入Bank2
```c
#define BANK2_BASE 0x08080000
#define PAGE_SIZE 2048 // 根据芯片调整
void Write_Firmware_To_Bank2(uint32_t *data, uint32_t size)
{
uint32_t addr = BANK2_BASE;
FLASH_Unlock();
// 擦除Bank2(按页擦除)
for (uint32_t offset = 0; offset < size; offset += PAGE_SIZE) {
FLASH_ErasePage(addr + offset);
}
// 写入数据
for (uint32_t i = 0; i < size / 4; i++) {
FLASH_ProgramWord(addr + i * 4, data[i]);
}
FLASH_Lock();
}
```
### 4.2 校验固件完整性
```c
uint8_t Verify_Firmware_CRC(uint32_t expected_crc)
{
uint32_t crc = 0;
// 使用硬件CRC或软件CRC计算Bank2区域
crc = HAL_CRC_Calculate(&hcrc, (uint32_t *)BANK2_BASE, FIRMWARE_SIZE / 4);
return (crc == expected_crc) ? 1 : 0;
}
```
### 4.3 切换Bank
```c
void Switch_To_Bank2(void)
{
// 设置BOOT_SW位
FLASH_OB_Unlock();
FLASH_OB_UserConfig(OB_BOOT_SW_SET);
FLASH_OB_Launch();
// 软复位
NVIC_SystemReset();
}
```
### 4.4 主流程
```c
int main(void)
{
// 初始化...
// 检查是否有新固件待安装
if (New_Firmware_Available()) {
// 接收固件到RAM缓冲区(或直接流式写入)
Receive_Firmware();
// 写入Bank2
Write_Firmware_To_Bank2(fw_buffer, fw_size);
// 校验
if (Verify_Firmware_CRC(expected_crc)) {
Switch_To_Bank2();
} else {
// 校验失败,继续运行旧固件
}
}
// 正常运行应用...
while (1) {
// 应用逻辑
}
}
```
## 5. 注意事项与优化建议
- **地址映射**:切换后,Bank2映射到0x08000000,但中断向量表仍指向原地址。需在启动文件或SystemInit中重设VTOR(向量表偏移寄存器)。
```c
SCB->VTOR = 0x08000000; // 切换后Bank2基址
```
- **固件大小**:确保新固件不超过Bank容量(本例512KB)。
- **擦写时间**:虽然双Bank可并行,但写入Bank2时应用仍运行,需确保Flash控制器不冲突(部分型号支持同时读写不同Bank)。
- **回滚机制**:若新固件启动失败,可设置看门狗或标志位,在Bootloader中回滚到旧Bank。
- **选项字节保护**:写入选项字节前需解锁,且操作后重新锁定,防止误改。
- **性能考量**:双Bank切换仅复位,但若应用有状态需保存,需在切换前持久化关键数据。
## 6. 总结
利用STM32双Bank架构,OTA升级从“停机数秒”优化为“零停机”,极大提升设备可用性。本文从原理到代码,展示了完整实现路径。实际项目中,还需结合安全启动、加密校验等机制,打造更健壮的升级系统。希望本文能为你提供实战参考,助你在嵌入式领域更上一层楼。