# 一、双Bank闪存与ART加速原理 STM32F4系列(如STM32F407/427)内置1MB闪存,分为两个独立的Bank(Bank0和Bank1),每个Bank大小512KB。双Bank的核心优势在于:**当一个Bank正在擦除或编程时,CPU仍可从另一个Bank正常取指执行**,因为两个Bank拥有独立的控制逻辑和总线接口。 ART(Adaptive Real-Time Accelerator)是ST专为闪存设计的加速器,它通过指令缓存和预取缓冲,将闪存访问延迟从几十个周期降至接近零。在双Bank模式下,ART可同时为两个Bank提供独立的缓存路径,确保切换时无性能抖动。 **关键点:** - 双Bank支持“读-写-读”并行操作,但同一Bank内不能同时读写。 - 通过设置选项字节(Option Bytes)中的nDBANK位,可将闪存配置为单Bank或双Bank模式。 - OTA时,固件A运行于Bank0,新固件写入Bank1,完成后切换启动地址,实现原子性更新。 # 二、硬件与软件准备 - 硬件:STM32F407开发板(或其他F4系列),外部Flash可选,但本例使用内部双Bank。 - 软件:STM32CubeIDE(或Keil),HAL库,STM32F4xx HAL驱动。 - 注意:需确认芯片型号支持双Bank(如F407/427/437等),且Flash大小≥1MB。 # 三、配置步骤 ## 1. 启用双Bank模式 双Bank模式通过修改选项字节实现。在CubeMX中,可进入`Option Bytes`配置,将`DBANK`设置为`Dual Bank`。若手动操作,使用以下代码: ```c /* 解锁Flash */ HAL_FLASH_Unlock(); /* 设置选项字节:双Bank模式 */ FLASH_OBProgramInitTypeDef ob; HAL_FLASH_OBGetConfig(&ob); ob.OptionType = OPTIONBYTE_BYTE; ob.WRPState = OB_WRPSTATE_DISABLE; ob.USERType = OB_USER_BANK; ob.USERConfig = OB_BANK_DUAL; HAL_FLASH_OBProgram(&ob); /* 重新加载选项字节 */ HAL_FLASH_OBLaunch(); HAL_FLASH_Lock(); ``` 配置后需复位生效。 ## 2. 规划内存布局 - Bank0:起始地址0x08000000,存放当前运行固件(App0)。 - Bank1:起始地址0x08080000,存放待升级固件(App1)。 - 向量表偏移:App0使用默认偏移,App1需设置`SCB->VTOR = 0x08080000`。 ## 3. 编写并行编程驱动 核心思路:在Bank0运行时,对Bank1进行擦写。由于双Bank独立,CPU可继续执行Bank0代码,但需注意中断向量表位于Bank0,因此擦写Bank1不会影响中断响应。 # 四、完整代码示例 以下示例演示如何从Bank0向Bank1写入新固件,并在完成后切换启动。 ```c #include "stm32f4xx_hal.h" #define BANK1_BASE 0x08080000 #define APP_SIZE 0x40000 // 256KB,根据实际调整 void Flash_Write_Bank1(uint32_t *data, uint32_t size) { HAL_FLASH_Unlock(); __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_ALL_ERRORS); uint32_t addr = BANK1_BASE; for (uint32_t i = 0; i < size; i += 8) { // 擦除页(每页16KB) if ((i % 0x4000) == 0) { FLASH_EraseInitTypeDef erase; erase.TypeErase = FLASH_TYPEERASE_PAGES; erase.PageAddress = addr; erase.NbPages = 1; uint32_t pageError = 0; HAL_FLASHEx_Erase(&erase, &pageError); } // 编程64位(双字) uint64_t val = ((uint64_t)data[i+1] << 32) | data[i]; HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, addr, val); addr += 8; } HAL_FLASH_Lock(); } void Jump_To_Bank1(void) { // 设置向量表偏移 SCB->VTOR = BANK1_BASE; // 获取新固件复位向量和栈顶 uint32_t stack_top = *(volatile uint32_t*)BANK1_BASE; uint32_t reset_handler = *(volatile uint32_t*)(BANK1_BASE + 4); // 关闭中断,跳转 __disable_irq(); typedef void (*pFunction)(void); pFunction jump = (pFunction)reset_handler; __set_MSP(stack_top); jump(); } // 调用示例:从串口接收固件数据并写入 void OTA_Update(uint8_t *fw_data, uint32_t fw_size) { // 确保数据对齐 Flash_Write_Bank1((uint32_t*)fw_data, fw_size); // 验证(可选) if (memcmp((void*)BANK1_BASE, fw_data, fw_size) == 0) { Jump_To_Bank1(); } } ``` # 五、注意事项 - **擦除时间**:擦除一个16KB页约需1秒,期间Bank1不可访问,但Bank0运行不受影响。 - **中断处理**:编程时若中断触发,HAL库会等待操作完成,导致短暂阻塞。建议在编程期间屏蔽可屏蔽中断,或使用DMA传输数据。 - **电源稳定性**:擦写时电流较大,确保供电稳定,否则可能导致编程失败。 - **双Bank切换**:切换后需重新配置时钟和所有外设,因为新固件会重新初始化。 - **ART缓存**:切换后ART缓存可能残留旧数据,需执行`SCB_InvalidateICache()`和`SCB_InvalidateDCache()`。 - **固件签名**:生产环境应增加CRC或签名校验,防止损坏固件被加载。 # 六、总结 利用STM32F4双Bank闪存和ART加速,OTA升级不再需要停机等待。通过并行编程,系统可在运行中完成固件更新,并在瞬间切换,极大提升用户体验和系统可用性。本文提供的代码框架可直接应用于实际项目,但需根据具体硬件调整地址和大小。掌握这一技术,你的嵌入式设备将具备工业级OTA能力。