3. 存储分区规划:Bootloader分区、App分区、配置参数分区、OTA备份分区设计
好,咱们今天聊聊存储分区。说实话,很多刚入行的朋友觉得这玩意儿就是画几个框、分几块地址,没什么技术含量。嗯,我以前也这么想,直到有一次在量产前发现分区不够用,差点把项目搞砸了。从那以后,我对分区规划就特别上心。
你想想看,药盒这种设备,固件升级要是出问题,那可是要命的事。所以分区规划,说白了就是给系统留好退路。咱们今天把四个核心分区掰开揉碎了讲清楚。
3.1 Bootloader分区——系统的守门员
Bootloader分区,我习惯叫它「启动引导区」。它负责两件事:一是上电后检查App分区是否完好,二是决定要不要进入升级模式。
这个分区一般放在Flash的最开头,地址从0x08000000开始。为什么?因为MCU上电后默认从这里取指令。我见过有人把Bootloader放在后面,结果每次启动都要跳转,麻烦得很。
典型大小:32KB ~ 64KB
存放内容:启动代码、Flash驱动、升级协议栈、CRC校验函数
这里有个坑,我曾经踩过——Bootloader里千万别放太多功能。它只需要能擦写Flash、能接收固件包就够了。有人非要把LCD驱动、按键扫描都塞进去,结果分区不够用,还得重新布局。
注意:Bootloader分区一旦写坏,设备就变砖了。所以这个分区我一般会做写保护,或者至少留一个硬件恢复引脚。
3.2 App分区——主程序的家
App分区就是咱们的主程序运行区。这个分区的大小,取决于你的固件有多大。我一般建议留出30%的余量,因为你永远不知道后面会加什么功能。
举个例子,我做过一个药盒项目,最初固件只有80KB,我觉得128KB的分区绰绰有余。结果后来加了语音播报、蓝牙配网,固件直接飙到150KB。嗯,最后只能压缩代码,折腾了好几天。
| 固件类型 | 建议App分区大小 | 备注 |
|---|---|---|
| 简单药盒(无屏) | 128KB | 仅基础提醒功能 |
| 中端药盒(有屏) | 256KB | 含UI、蓝牙 |
| 高端药盒(联网) | 512KB | 含OTA、语音、Wi-Fi |
App分区的起始地址,紧跟在Bootloader后面。比如Bootloader占64KB,那App就从0x08010000开始。这个地址要在链接脚本里写死,千万别搞错了。
3.3 配置参数分区——不能丢的小数据
这个分区很多人会忽略,觉得参数存EEPROM里就行了。但EEPROM容量小、寿命有限,而且有些MCU根本没有内部EEPROM。
我个人的做法是,在Flash里划出一块区域专门存配置参数。比如Wi-Fi密码、服药时间表、设备ID这些。为什么单独分一个区?因为App升级时会擦写整个App分区,如果参数和代码混在一起,升级一次参数就丢了。
小技巧:配置参数分区我一般用两个备份。写参数时先写备份区,成功后再更新主区。这样即使写一半断电,也不会丢数据。这个习惯是我在一次现场升级断电后养成的。
配置分区的大小不用太大,4KB到8KB就够用了。但要注意,Flash的擦除单位是扇区(通常是4KB),所以分区大小最好是扇区的整数倍。
3.4 OTA备份分区——最后的救命稻草
这个分区是OTA升级的核心。它的作用很简单:下载新固件时,先存到这里,等下载完了再搬到App分区。
你可能会问:「为什么不直接下载到App分区?」嗯,这个问题问得好。如果直接覆盖,下载过程中断电了怎么办?App分区被擦了一半,Bootloader检查发现校验失败,设备就变砖了。
所以OTA备份分区的大小,必须和App分区一样大。比如App分区是256KB,那备份分区也得是256KB。这样新固件下载完成后,Bootloader可以完整地校验一遍,确认没问题再搬过去。
升级流程:
- 下载新固件到OTA备份分区
- 校验CRC或哈希值
- 擦除App分区
- 从备份分区拷贝到App分区
- 再次校验App分区
- 重启进入新固件
我曾经遇到过一个问题:Flash容量不够,没法给备份分区留出和App一样大的空间。怎么办?后来我用了「差分升级」的方案,只下载变化的部分,备份分区只需要存差分包,大小可以压缩到App分区的30%左右。但这个方案实现起来比较复杂,适合有经验的团队。
3.5 完整分区布局示例
好了,咱们把四个分区串起来,看一个完整的例子。假设MCU的Flash是1MB(0x08000000 ~ 0x080FFFFF):
// 分区地址分配(以STM32为例)
#define BOOTLOADER_ADDR 0x08000000 // 64KB
#define APP_ADDR 0x08010000 // 384KB
#define CONFIG_ADDR 0x08070000 // 8KB
#define OTA_BACKUP_ADDR 0x08072000 // 384KB
// 剩余空间:0x080D2000 ~ 0x080FFFFF 约184KB,留作扩展
这个布局里,Bootloader占64KB,App和备份各占384KB,配置参数占8KB。剩下的空间我一般留着,万一以后要加功能呢?
重要提醒:分区地址一旦确定,就不要轻易改动。因为Bootloader里写死了App的起始地址,如果改了,旧版Bootloader就找不到新版App了。我见过有人升级时改了分区大小,结果旧设备全部变砖,只能返厂维修。
3.6 分区规划的几个原则
最后,我总结几条经验,供你参考:
- Bootloader越小越好——只放必要代码,其他功能交给App
- App分区留余量——至少多留30%,别卡着极限设计
- 配置参数独立——别和代码混在一起,升级时容易丢
- 备份分区和App等大——除非你用差分升级,否则别省这个空间
- 地址对齐——分区起始地址要按扇区对齐,否则擦写会出问题
嗯,分区规划就讲到这里。说白了,这就是给固件升级铺路。路铺好了,后面的事就顺了。下一节咱们聊聊Bootloader的具体实现,到时候我会分享一个我实际用过的代码框架。