2. 硬件架构设计:双区Flash布局
做固件升级,第一件事不是写代码,而是先把Flash怎么分想清楚。我见过太多项目,代码写了一半才发现Flash不够用,或者升级失败后设备变砖——说白了,都是布局没规划好。
这一章,我们就来聊聊双区备份架构下的Flash分区方案。我会结合自己踩过的坑,把Bootloader区、App A区、App B区、参数区这四块怎么划分、怎么对齐、怎么预留,一次讲透。
2.1 为什么需要双区备份?
先问一个问题:如果你的设备在升级过程中突然断电了,会发生什么?
答案是——程序被擦了一半,新的没写完,旧的也没了。设备直接变砖。
双区备份就是为了解决这个问题的。说白了,就是准备两份App空间:一份在跑,一份用来接收新固件。升级时只写备份区,主区不动。等新固件校验通过,再切换过去。
我记得2018年做一个工业控制器项目,客户要求OTA升级成功率99.9%以上。当时我用了单区方案,结果现场升级失败率高达5%。后来改成双区备份,失败率直接降到0.1%以下。从那以后,但凡涉及远程升级的项目,我必用双区。
2.2 四区划分方案
一个典型的双区Flash布局,分为四个区域:
| 区域名称 | 起始地址 | 大小 | 作用 |
|---|---|---|---|
| Bootloader区 | 0x08000000 | 64 KB | 启动引导、升级入口、故障检测 |
| App A区 | 0x08010000 | 384 KB | 主运行区,当前固件 |
| App B区 | 0x08070000 | 384 KB | 备份区,接收新固件 |
| 参数区 | 0x080D0000 | 64 KB | 升级标志、版本号、配置参数 |
这个布局基于STM32F4系列,Flash总容量1MB。实际项目中,你需要根据芯片容量和应用大小灵活调整。
2.3 Bootloader区设计要点
Bootloader是系统的看门人。它负责三件事:
- 上电检测:检查参数区的升级标志,决定启动哪个App
- 固件接收:通过CAN或USB接收新固件,写入备份区
- 故障回滚:如果App启动失败,自动切回旧版本
我个人习惯把Bootloader控制在64KB以内。为什么?因为Bootloader很少改动,写大了浪费空间。而且Bootloader越精简,出bug的概率越低。
2.4 App A区和App B区的对称设计
App A和App B的大小必须完全一致。这不是随便定的,有两个原因:
- 地址映射方便:切换时只需要修改向量表偏移,代码不用重定位
- 擦写时间可控:两个区大小一样,升级耗时可以预估
我曾经在一个项目中偷懒,把App B区设得比App A区小。结果新固件功能增加,体积超标,写不进备份区。最后只能重新分区,所有设备返厂升级——教训深刻。
另外,两个区的起始地址必须按Flash扇区对齐。以STM32F4为例,扇区大小是16KB、64KB、128KB不等。App A从0x08010000开始,正好是第4个扇区(64KB对齐),这样擦写操作不会越界。
2.5 参数区的关键作用
参数区虽然只有64KB,但它是整个升级系统的决策中心。里面存着:
- 升级标志:0xAA55表示需要升级,0x55AA表示升级完成
- 当前运行区:0表示App A,1表示App B
- 固件版本号:主版本号+次版本号+修订号,共3字节
- 校验值:整个App区的CRC32或SHA256
- 回滚计数:记录回滚次数,防止反复回滚
我建议参数区至少预留2个备份页。为什么?因为Flash写入有风险,万一写参数时断电,至少还有一个备份可用。我曾经在量产时遇到过参数区写坏的情况,就是因为只用了1页,没有冗余。
2.6 整体架构图
下面这张图展示了完整的Flash布局和升级流程。你可以看到,Bootloader在中间协调,App A和App B对称分布,参数区独立存放。
2.7 分区对齐的硬规则
分区时,有几个硬性规则必须遵守:
- 扇区对齐:每个区的起始地址必须是Flash扇区大小的整数倍
- 大小对齐:App A和App B大小必须一致,且是扇区大小的整数倍
- 地址连续:四个区在地址空间上连续,不要留空洞
- 参数区独立:参数区必须独占一个或多个扇区,不能和其他区共用
2.8 不同芯片的适配建议
不同厂商的Flash扇区大小不一样,分区方案也要跟着调整:
| 芯片系列 | Flash总容量 | 扇区大小 | 推荐分区方案 |
|---|---|---|---|
| STM32F103 | 512KB | 1KB/2KB | Bootloader 32KB + App A 224KB + App B 224KB + 参数 32KB |
| STM32F407 | 1MB | 16KB/64KB/128KB | Bootloader 64KB + App A 384KB + App B 384KB + 参数 64KB |
| GD32F450 | 2MB | 32KB/128KB | Bootloader 128KB + App A 896KB + App B 896KB + 参数 64KB |
| NXP LPC54606 | 512KB | 32KB | Bootloader 32KB + App A 224KB + App B 224KB + 参数 32KB |
嗯,这里要注意:扇区越大,擦写时间越长。128KB的扇区擦一次可能要几百毫秒。如果你的升级流程对时间敏感,建议选扇区小的芯片,或者用双Bank架构的芯片。
2.9 本章小结
双区Flash布局是固件升级的基石。Bootloader管启动,App A和App B互为备份,参数区存决策信息。四者配合,才能实现可靠的升级和回滚。
我个人习惯在项目初期就把Flash布局画出来,贴在白板上。每次改代码前先看一眼,确保不会越界。你想想看,如果连Flash怎么分都没想清楚,后面写再多代码也是白搭。