2. STM32存储器架构基础
在STM32双Bank固件升级工程中,所有升级逻辑最终都要落到“把哪些字节写到哪个地址”这一层。如果对Flash的组织方式、Bank划分、扇区边界和地址映射没有清晰认识,就很容易出现擦除越界、跳转失败、升级后程序跑飞等问题。本节从存储器物理结构出发,建立一套可用于容量规划和地址计算的完整认知。
一、核心原理:从Flash物理阵列到软件可见地址
STM32内部Flash并不是一整块可以任意字节擦写的存储介质,而是由若干扇区(Sector)组成。擦除操作的最小单位是扇区,写入操作的最小单位通常是双字(Double Word,64位)或字(Word,32位),读取则可以按字节进行。这种“读细、写中、擦粗”的不对称特性,是固件升级设计必须尊重的物理约束。
在双Bank器件中,Flash被划分为两个相互独立的Bank。每个Bank拥有自己的擦除与编程控制逻辑,因此可以做到:Bank1运行应用程序,Bank2接收新固件,两者互不干扰。这正是“边运行边升级”的硬件基础。地址映射上,Bank1通常从0x0800 0000开始,Bank2紧随其后,但具体边界取决于芯片型号与容量。
二、Bank划分与扇区结构
以常见的STM32F4/F7/H7系列为例,不同容量对应的扇区大小并不一致。小容量器件扇区较小,大容量器件扇区可达128KB甚至更大。双Bank模式下,每个Bank内部又按扇区编号独立排列。下面这张图展示了双Bank Flash的典型组织关系:地址自低向高延伸,Bank1与Bank2并列,每个Bank内部由若干扇区构成,扇区是擦除的最小单元。
需要特别注意的是,扇区编号在双Bank器件中通常是跨Bank连续编号的。例如Bank1包含Sector 0~3,Bank2从Sector 4开始。这意味着在调用HAL库擦除函数时,传入的扇区号必须与目标Bank对应,否则会擦错区域。
三、地址映射与容量规划方法
做双Bank升级,第一步就是确定“运行区”和“升级区”的地址边界。规划时建议遵循以下流程:
- 查阅芯片参考手册,确认Flash总容量、Bank数量、每个Bank的起始地址与扇区划分。
- 确定应用程序的最大体积,并向上对齐到扇区边界,避免跨扇区截断。
- 为Bootloader预留独立区域,通常放在Bank1起始处或独立扇区。
- 将升级区放在另一个Bank,确保其容量不小于应用程序最大体积。
- 在链接脚本中分别设置运行区与升级区的ROM地址,生成两个独立的可执行映像。
下面是一段典型的链接脚本片段,用于把应用程序定位到Bank1,而升级固件定位到Bank2:
/* 应用程序链接脚本 app.ld */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08008000, LENGTH = 224K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
/* 升级固件链接脚本 update.ld */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08040000, LENGTH = 256K
RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K
}
这里把应用程序起始地址设为0x08008000,是为了给前面的Bootloader留出32KB空间。升级固件则直接放在Bank2起始地址0x08040000。两个映像互不重叠,升级时只需擦除Bank2对应扇区并写入新固件即可。
四、案例与场景:一次擦除越界引发的升级失败
某项目使用STM32F767双Bank器件,应用程序约180KB,规划时把升级区放在Bank2起始处。开发者在擦除时直接调用HAL_FLASHEx_Erase(),扇区号从0开始连续擦除8个扇区,意图“清空整个Flash再写入”。结果Bank1中的Bootloader和应用程序被一并擦除,设备重启后无法启动。
复盘发现两个问题:第一,扇区号没有区分Bank,误擦了运行区;第二,擦除范围没有按实际固件体积计算,而是凭感觉“多擦一点更安全”。正确做法是:只擦除升级区所在扇区,且擦除数量由固件体积向上取整到扇区边界决定。
五、容量规划对照清单
下表汇总了双Bank升级中常见的容量与地址规划要点,可作为项目启动时的检查清单:
| 规划项 | 说明 | 典型取值/示例 |
|---|---|---|
| Bootloader区 | 存放启动与升级逻辑,独立扇区 | 0x08000000,32KB |
| 应用程序区 | 正常运行固件,位于Bank1 | 0x08008000,224KB |
| 升级固件区 | 接收新固件,位于Bank2 | 0x08040000,256KB |
| 扇区对齐 | 固件体积向上取整到扇区边界 | 180KB → 192KB(12个16KB扇区) |
| 写入粒度 | 按双字编程,不足补0xFF | 64位对齐 |
| 擦除粒度 | 按扇区擦除,不可字节擦除 | 16KB/64KB/128KB |
六、常见误区与复盘要点
- 误区一:认为Flash可以按字节擦除。实际上擦除只能按扇区进行,写入前必须先擦除,且擦除后整扇区变为0xFF。
- 误区二:忽略扇区编号跨Bank连续。双Bank器件中Bank2的扇区号并不从0重新开始,擦除时容易误操作。
- 误区三:容量规划不留余量。应用程序增长后可能超出预留扇区,导致升级区与运行区重叠。
- 误区四:链接脚本与升级地址不一致。编译出的固件地址与写入地址不匹配,跳转后必然失败。
建议在项目初期就建立一张“地址-扇区-用途”对照表,并在每次修改分区后同步更新链接脚本、升级工具配置和Bootloader中的跳转地址。只有三者一致,双Bank升级才能稳定运行。