STM32双Bank模式下的Flash擦写并发:OTA升级可靠性的关键实现细节
👁 2 阅读 · 2026-08-27 · 嵌入式
在OTA升级中,Flash擦写操作往往阻塞CPU,导致升级过程漫长且易受干扰,尤其在掉电或通信异常时可能损坏固件。STM32的双Bank模式通过将Flash划分为两个独立区域,允许在擦写一个Bank的同时从另一个Bank执行代码,实现并发操作,大幅提升升级可靠性和效率。本文深入解析双Bank模式的原理、配置步骤及完整代码示例,并探讨实际应用中的注意事项,帮助开发者构建稳健的OTA系统。
# STM32双Bank模式下的Flash擦写并发:OTA升级可靠性的关键实现细节
## 引言
在嵌入式OTA升级中,Flash擦写操作通常需要暂停CPU执行,导致系统响应延迟,且升级过程中一旦掉电或通信中断,固件可能损坏。STM32的双Bank模式(如STM32F4、F7、H7系列)通过将Flash划分为两个独立Bank,允许在擦写一个Bank时从另一个Bank执行代码,实现并发操作。这一特性不仅提升了升级效率,还通过原子性切换增强了可靠性。本文将深入探讨双Bank模式的原理、配置步骤、代码实现及注意事项。
## 双Bank模式原理
### 什么是双Bank?
双Bank模式将Flash存储器分为两个独立的区域(Bank1和Bank2),每个Bank拥有独立的擦写控制逻辑。在单Bank模式下,Flash擦写操作会阻塞CPU,因为代码执行和擦写操作共享同一总线。而在双Bank模式下,CPU可以从一个Bank执行代码,同时擦写另一个Bank,实现真正的并行操作。
### 并发擦写的优势
- **提升升级速度**:擦写操作不再阻塞CPU,系统可在升级过程中继续处理其他任务(如通信、用户交互)。
- **增强可靠性**:若升级失败,可回滚到另一个Bank中的旧固件,避免设备变砖。
- **支持无缝切换**:通过设置标志位,在复位后引导加载程序选择正确的Bank启动。
### 硬件支持
并非所有STM32都支持双Bank模式,需查阅具体型号的参考手册。例如,STM32F4系列中,只有Flash容量大于1MB的型号(如STM32F429)支持双Bank;而STM32F7和H7系列则普遍支持。此外,双Bank模式要求Flash的扇区大小对称,且地址映射固定。
## 配置步骤
### 1. 检查芯片支持
首先确认芯片是否支持双Bank模式,可通过查看参考手册中的“Flash memory”章节。例如,STM32F429的Flash基地址为0x08000000,Bank1和Bank2各占512KB。
### 2. 配置Flash选项字节
双Bank模式通过修改选项字节启用。在STM32CubeProgrammer或代码中,设置`FLASH_OPTR`寄存器的`DBANK`位。例如,对于STM32F4:
```c
// 启用双Bank模式
FLASH->OPTR |= FLASH_OPTR_DBANK; // 假设DBANK位为1表示双Bank
FLASH->CR |= FLASH_CR_OPTSTRT; // 启动选项字节编程
while (FLASH->SR & FLASH_SR_BSY); // 等待完成
```
注意:修改选项字节后需复位才能生效。
### 3. 调整链接脚本
启用双Bank后,Flash地址映射变化。例如,STM32F429的Bank1地址为0x08000000-0x0807FFFF,Bank2为0x08080000-0x080FFFFF。链接脚本需将固件分为两个区域,分别放置于两个Bank。
```ld
MEMORY
{
FLASH_BANK1 (rx) : ORIGIN = 0x08000000, LENGTH = 512K
FLASH_BANK2 (rx) : ORIGIN = 0x08080000, LENGTH = 512K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
}
```
### 4. 编写引导加载程序
引导加载程序(Bootloader)位于Bank1起始地址,负责接收新固件并写入Bank2。升级完成后,设置标志位并复位,Bootloader根据标志位选择启动Bank。
## 代码实现示例
以下示例基于STM32F429,演示如何在Bank2中擦写固件,同时从Bank1执行代码。
### 初始化Flash
```c
void Flash_Init(void) {
// 解锁Flash
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
// 等待空闲
while (FLASH->SR & FLASH_SR_BSY);
}
void Flash_Lock(void) {
FLASH->CR |= FLASH_CR_LOCK;
}
```
### 擦除Bank2扇区
```c
void Flash_EraseBank2(void) {
// 选择Bank2(例如扇区8-15,每个16KB)
for (uint32_t sector = 8; sector <= 15; sector++) {
FLASH->CR &= ~FLASH_CR_SNB; // 清除扇区号
FLASH->CR |= (sector << 3) | FLASH_CR_SER; // 设置扇区号和擦除使能
FLASH->CR |= FLASH_CR_STRT; // 开始擦除
while (FLASH->SR & FLASH_SR_BSY); // 等待完成
}
}
```
### 写入数据到Bank2
```c
void Flash_WriteBank2(uint32_t address, uint32_t *data, uint32_t length) {
for (uint32_t i = 0; i < length; i++) {
// 等待Flash空闲
while (FLASH->SR & FLASH_SR_BSY);
// 编程双字(64位)
FLASH->CR |= FLASH_CR_PG;
*(volatile uint64_t *)address = *(uint64_t *)(data + i);
while (FLASH->SR & FLASH_SR_BSY);
address += 8; // 每次写入8字节
}
FLASH->CR &= ~FLASH_CR_PG;
}
```
### 升级流程示例
```c
void OTA_Upgrade(void) {
// 接收新固件到RAM缓冲区(省略通信代码)
uint8_t buffer[1024];
// 擦除Bank2
Flash_EraseBank2();
// 写入新固件
Flash_WriteBank2(0x08080000, (uint32_t *)buffer, sizeof(buffer)/8);
// 设置升级标志(存储在备份寄存器或Flash选项字节)
PWR->CR |= PWR_CR_DBP; // 使能备份域访问
RTC->BKP0R = 0xA5A5; // 示例:备份寄存器存储标志
// 复位
NVIC_SystemReset();
}
```
### 引导加载程序选择Bank
```c
void Bootloader_Jump(void) {
if (RTC->BKP0R == 0xA5A5) {
// 跳转到Bank2
uint32_t jump_address = *(volatile uint32_t *)(0x08080004);
void (*jump)(void) = (void (*)(void))jump_address;
__set_MSP(*(volatile uint32_t *)0x08080000);
jump();
} else {
// 跳转到Bank1
// ...
}
}
```
## 注意事项
- **擦写时序**:双Bank模式下,擦写操作仍会占用总线带宽,但CPU可继续执行,需确保中断处理不访问正在擦写的Bank。
- **电源稳定性**:擦写Flash时电流较大,需确保电源稳定,否则可能导致擦写失败。
- **标志位存储**:升级标志建议存储在备份寄存器或独立EEPROM,避免因Flash擦写失败丢失。
- **错误处理**:擦写过程中若发生错误(如电压不足),需重试或回滚。
- **地址对齐**:写入数据需按双字(8字节)对齐,否则触发硬件错误。
- **链接脚本**:确保Bank2的固件链接地址正确,否则跳转后无法执行。
## 总结
双Bank模式为STM32 OTA升级提供了高效的并发擦写能力,显著提升了升级可靠性和用户体验。通过合理配置选项字节、调整链接脚本并实现健壮的引导加载程序,开发者可以构建支持无缝升级和回滚的嵌入式系统。在实际应用中,需注意电源、时序和错误处理等细节,以充分发挥双Bank的优势。