STM32H7双Bank闪存并行编程:利用ART加速实现OTA零等待切换
👁 1 阅读 · 2026-08-27 · 嵌入式
STM32H7系列凭借双Bank闪存架构与ART加速器,为OTA升级提供了革命性方案。本文深入解析双Bank并行编程原理,结合ART缓存一致性策略,演示如何实现固件无缝切换与零等待运行。通过配置Flash控制器、管理Bank切换时序及优化ART预取,开发者可构建高可靠、低延迟的空中升级系统,彻底告别停机更新。
# STM32H7双Bank闪存并行编程:利用ART加速实现OTA零等待切换
## 1. 双Bank架构与ART加速原理
STM32H7系列(如STM32H743/750)内置高达2MB的双Bank闪存,每个Bank独立可擦写。传统单Bank方案在OTA时需暂停应用,而双Bank允许在Bank1运行旧固件的同时,对Bank2写入新固件。关键在于**ART(Adaptive Real-Time)加速器**,它通过指令缓存和预取机制,消除CPU访问闪存的等待状态,使代码执行速度接近零等待。
ART的核心优势:
- **指令缓存**:缓存最近执行的指令,减少闪存访问次数。
- **预取策略**:智能预取分支目标,提高缓存命中率。
- **双Bank支持**:当切换Bank时,ART自动刷新缓存,确保新代码一致性。
## 2. 双Bank并行编程硬件机制
STM32H7的闪存接口支持**RWW(Read-While-Write)**特性,即一个Bank在编程/擦除时,另一个Bank可正常读取。这为OTA提供了硬件基础:
- **Bank1**:运行当前固件(旧版本)。
- **Bank2**:接收新固件写入。
并行编程的时序要求:
- 编程操作需通过FLASH控制器寄存器触发,且**不能中断**,但CPU可继续从另一个Bank取指。
- 擦除操作按扇区进行,H7的扇区大小可配置(8KB/16KB/32KB/128KB),需合理规划。
## 3. 配置步骤:从零到双Bank OTA
### 3.1 内存布局规划
在链接脚本中定义两个Bank的起始地址:
```c
/* STM32H743 2MB Flash */
#define BANK1_BASE 0x08000000
#define BANK2_BASE 0x08100000
#define BANK1_SIZE 0x100000 // 1MB
#define BANK2_SIZE 0x100000
```
### 3.2 初始化Flash控制器
```c
void Flash_Init(void) {
// 解锁Flash
HAL_FLASH_Unlock();
// 设置编程延迟(根据主频调整)
FLASH_ACR_LATENCY = FLASH_LATENCY_7; // 480MHz时需7个等待周期
// 使能ART加速器
FLASH_ACR_ARTEN = 1;
// 使能预取
FLASH_ACR_PRFTEN = 1;
HAL_FLASH_Lock();
}
```
### 3.3 双Bank并行编程函数
```c
void Flash_WriteBank2(uint32_t addr, uint32_t *data, uint32_t len) {
HAL_FLASH_Unlock();
// 确保目标地址在Bank2范围
if (addr < BANK2_BASE || addr >= (BANK2_BASE + BANK2_SIZE)) return;
// 擦除目标扇区(以128KB扇区为例)
FLASH_EraseInitTypeDef erase = {0};
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
erase.Banks = FLASH_BANK_2;
erase.Sector = (addr - BANK2_BASE) / 128KB;
erase.NbSectors = 1;
uint32_t error = 0;
HAL_FLASHEx_Erase(&erase, &error);
// 编程数据(每次64位)
for (uint32_t i = 0; i < len; i += 2) {
uint64_t val = data[i] | ((uint64_t)data[i+1] << 32);
HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, addr + i, val);
}
HAL_FLASH_Lock();
}
```
### 3.4 Bank切换与ART缓存刷新
```c
void SwitchToBank2(void) {
// 禁用全局中断,防止切换期间干扰
__disable_irq();
// 设置启动Bank为Bank2
FLASH_OPTCR_BOR_LEV = 0; // 清除选项字节
FLASH_OPTCR_BOR_LEV = 0x2; // 设置BOR级别(示例)
// 关键:切换Bank后,ART缓存失效,需重置
SCB_InvalidateICache();
// 重新定位向量表
SCB->VTOR = BANK2_BASE;
// 跳转到新固件复位向量
uint32_t msp = *(uint32_t*)BANK2_BASE;
uint32_t reset = *(uint32_t*)(BANK2_BASE + 4);
__set_MSP(msp);
((void(*)(void))reset)();
}
```
## 4. 完整示例:OTA升级流程
```c
int main(void) {
HAL_Init();
Flash_Init();
// 检查是否有新固件(例如通过UART接收)
if (OTA_CheckNewFirmware()) {
uint32_t new_fw[1024]; // 假设固件大小4KB
OTA_ReceiveFirmware(new_fw, sizeof(new_fw));
// 写入Bank2
Flash_WriteBank2(BANK2_BASE, new_fw, sizeof(new_fw)/4);
// 验证校验和
if (OTA_VerifyCRC(BANK2_BASE, sizeof(new_fw))) {
// 切换Bank
SwitchToBank2();
}
}
// 正常运行旧固件
while(1) {
// 应用逻辑
}
}
```
## 5. 注意事项与优化技巧
- **ART缓存一致性**:切换Bank后,必须刷新指令缓存(`SCB_InvalidateICache()`),否则可能执行旧指令。
- **中断处理**:在编程Bank2时,确保中断服务函数位于Bank1且不访问Bank2,否则可能触发总线错误。
- **电源稳定性**:并行编程时电流需求增大,需确保供电充足,否则可能导致编程失败。
- **擦除时间**:H7擦除128KB扇区约需1秒,期间可继续运行,但需避免长时间阻塞。
- **使用DMA**:可通过DMA从外设(如UART/SPI)直接写入Flash,减少CPU负载。
- **回滚机制**:若新固件启动失败,可通过看门狗或外部信号回滚到Bank1。
## 6. 总结
STM32H7的双Bank闪存与ART加速器为OTA提供了硬件级支持,通过合理配置,开发者能实现固件无缝升级,且运行性能不受影响。本文的代码示例可直接应用于实际项目,但需根据具体硬件调整参数。掌握这些技术,将显著提升产品的可维护性与用户体验。