STM32F4双Bank闪存并行读写:在线升级不掉电的Bank切换实战
👁 2 阅读 · 2026-08-27 · 嵌入式
在工业设备与物联网终端中,在线升级(OTA)的可靠性至关重要。STM32F4系列的部分型号(如STM32F407/427)支持双Bank闪存架构,允许在运行一个Bank的同时擦写另一个Bank,从而实现无缝升级。本文将深入剖析双Bank闪存的工作原理,手把手教你配置Bank切换、实现并行读写,并提供完整的代码示例与避坑指南,助你打造不掉电、不中断的固件升级方案。
# STM32F4双Bank闪存并行读写:在线升级不掉电的Bank切换实战
## 一、为什么需要双Bank闪存?
传统单Bank闪存升级流程:
1. 擦除整个应用区 → 2. 写入新固件 → 3. 跳转重启。
此过程存在致命缺陷:**升级期间设备完全停机**,且一旦写入失败(如断电),设备变砖。
双Bank架构将闪存划分为两个独立的存储区(Bank1和Bank2),支持**并行编程**:当CPU从Bank1执行代码时,可以同时对Bank2进行擦写操作。升级完成后,通过修改标志位并复位,Bootloader即可跳转到新Bank运行。整个过程无需断电,且旧固件始终保留,极大提升了可靠性。
## 二、STM32F4双Bank硬件原理
以STM32F407为例,其内置1MB闪存,默认配置为单Bank(Bank1=512KB,Bank2=512KB,但合并为连续地址0x08000000-0x080FFFFF)。启用双Bank模式后,地址映射发生改变:
- **Bank1**:0x08000000 - 0x0807FFFF(512KB)
- **Bank2**:0x08080000 - 0x080FFFFF(512KB)
关键寄存器:
- `FLASH_OPTCR`:选项字节控制寄存器,其中`DBANK`位(bit22)用于切换单/双Bank模式。
- `FLASH_CR`:闪存控制寄存器,`PG`(编程)、`PER`(页擦除)、`MER`(全擦除)等位。
**注意**:切换Bank模式需要修改选项字节,并触发系统复位才能生效。
## 三、双Bank并行读写的核心优势
- **无缝升级**:运行在Bank1时,擦写Bank2,完成后复位跳转,中断时间仅为一个复位周期(毫秒级)。
- **回滚保护**:新固件异常时,Bootloader可回退到旧Bank。
- **双固件冗余**:适合A/B分区升级策略。
## 四、实战配置步骤
### 1. 启用双Bank模式
通过修改选项字节使能双Bank。可使用STM32CubeProgrammer或代码方式。以下为代码方式:
```c
void Flash_Enable_DualBank(void) {
FLASH_OB_Unlock();
// 设置DBANK位为1(双Bank模式)
FLASH_OB_Program(OB_DBANK_MODE, 1);
FLASH_OB_Launch(); // 触发选项字节重载
FLASH_OB_Lock();
NVIC_SystemReset(); // 必须复位生效
}
```
### 2. 链接脚本调整
将固件分为两个区域,例如:
```ld
MEMORY
{
BANK1 (rx) : ORIGIN = 0x08000000, LENGTH = 512K
BANK2 (rx) : ORIGIN = 0x08080000, LENGTH = 512K
}
```
### 3. 并行读写实现
在Bank1运行时,对Bank2进行擦写。核心代码:
```c
// 擦除Bank2的指定页
void Flash_Erase_Bank2_Page(uint32_t page_addr) {
FLASH_Unlock();
while (FLASH_GetStatus() == BUSY);
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
FLASH_ErasePage(page_addr); // 页地址需在Bank2范围内
FLASH_Lock();
}
// 写入数据到Bank2
void Flash_Write_Bank2(uint32_t addr, uint32_t *data, uint32_t len) {
FLASH_Unlock();
for (uint32_t i = 0; i < len; i++) {
while (FLASH_GetStatus() == BUSY);
FLASH_ProgramWord(addr + i*4, data[i]);
}
FLASH_Lock();
}
```
### 4. 升级流程与跳转
升级完成后,写入升级标志(如备份在备份寄存器),然后复位。Bootloader根据标志决定跳转地址:
```c
void Jump_To_App(uint32_t app_addr) {
// 检查栈顶地址合法性
if (((*(volatile uint32_t*)app_addr) & 0x2FFE0000) == 0x20000000) {
uint32_t jump_addr = *(volatile uint32_t*)(app_addr + 4);
pFunction jump = (pFunction)jump_addr;
__set_MSP(*(volatile uint32_t*)app_addr);
jump();
}
}
```
## 五、完整示例:在线升级Demo
假设Bootloader位于Bank1,新固件下载到Bank2。以下为简化流程:
```c
int main(void) {
// 初始化外设...
if (Check_Update_Flag()) {
// 1. 擦除Bank2
for (uint32_t page = 0x08080000; page < 0x080FFFFF; page += 2048) {
Flash_Erase_Bank2_Page(page);
}
// 2. 从外部存储(如SD卡)读取固件并写入Bank2
uint32_t buffer[128];
uint32_t write_addr = 0x08080000;
while (Read_Firmware_Block(buffer, 128)) {
Flash_Write_Bank2(write_addr, buffer, 128);
write_addr += 512;
}
// 3. 设置跳转标志并复位
Set_Boot_Flag(BANK2);
NVIC_SystemReset();
} else {
// 正常启动,根据标志跳转
if (Get_Boot_Flag() == BANK2) {
Jump_To_App(0x08080000);
} else {
Jump_To_App(0x08000000);
}
}
}
```
## 六、注意事项与避坑指南
- **选项字节修改不可逆**:一旦启用双Bank,地址映射改变,旧固件无法直接运行,需重新烧录。建议在量产前配置好。
- **中断向量表重定位**:Bank2固件需在启动时重定位向量表:`SCB->VTOR = 0x08080000;`
- **擦写期间禁止中断**:虽然双Bank支持并行,但擦写操作会阻塞CPU,若需实时性,可考虑使用DMA或RTOS任务调度。
- **电源稳定性**:升级期间掉电可能导致Bank2写入不完整,但Bank1仍可启动,因此务必在Bootloader中增加完整性校验(如CRC)。
- **使用官方库函数**:STM32F4标准外设库或HAL库均支持双Bank操作,但需注意版本差异。
## 七、总结
双Bank闪存为STM32F4提供了强大的在线升级能力,通过合理的Bank切换和并行读写,可实现近乎零停机的固件更新。本文从原理到代码,完整演示了双Bank模式的配置与使用。在实际项目中,建议结合Bootloader设计、固件签名和回滚机制,构建高可靠的OTA系统。