2. 固件存储架构:Bootloader与Application分区设计、Flash布局策略

好,咱们接着聊固件存储架构。这一块,说白了就是决定你的程序代码在Flash里怎么放、怎么跑、怎么升级。我见过不少工程师,功能调通了就急着发布,结果升级一次就变砖——嗯,分区没规划好。

今天我把这些年踩过的坑和总结的经验,一次性讲清楚。

2.1 为什么必须分区?

你想想看,一个分析仪器,可能正在野外连续监测。如果升级过程中突然断电,或者网络断了,整个Flash里的程序被擦了一半——那设备就彻底废了。

分区就是为了解决这个问题。我们把Flash分成两个区域:

  • Bootloader区:负责启动和升级,一般只读不写(除了升级时)。
  • Application区:存放真正的业务代码,可以随时擦写。

这样设计的好处很明显:即使Application区坏了,Bootloader还在,设备还能进入升级模式,重新刷写固件。这就是所谓的「不死之身」。

核心原则:Bootloader必须足够稳定,且不能依赖Application区的任何数据。

2.2 Flash布局策略

Flash布局,说白了就是给每个功能模块分配地址空间。我习惯把Flash分成以下几块:

区域名称 起始地址 大小 说明
Bootloader 0x08000000 64KB 启动代码、升级协议栈
Application 0x08010000 384KB 主业务代码
参数存储区 0x08070000 32KB 校准参数、配置信息
日志/备份区 0x08078000 32KB 运行日志、升级备份

这个布局是我在做一个水质分析仪项目时定下来的。当时客户要求支持远程升级,而且必须保证参数不丢失。嗯,这个布局后来成了我们团队的模板。

2.3 Bootloader分区设计要点

Bootloader的设计,有几个关键点必须注意:

  • 入口地址固定:芯片复位后,第一条指令必须从Bootloader开始。所以Bootloader必须放在Flash的起始地址。
  • 最小化依赖:Bootloader里不要用复杂的库,比如文件系统、网络协议栈。越简单越可靠。
  • 升级协议要健壮:我曾经遇到过一个问题——升级过程中网络闪断,Bootloader收到一半的数据包,结果把Application区写坏了。后来我加了一个「校验-确认-写入」的三步握手机制,才彻底解决。

我的习惯:Bootloader里只放三样东西——串口驱动、Flash驱动、一个简单的升级协议。其他什么都不要。

2.4 Application分区设计要点

Application区是主战场。这里要注意的是:

  • 中断向量表重映射:Application的起始地址不是0x08000000,所以必须把中断向量表重映射到Application的起始地址。否则中断来了,CPU会跑到Bootloader的向量表里去。
  • 链接脚本要改:在编译Application时,链接脚本里的Flash起始地址要改成Application的地址。这个坑我见过好几个人踩过。
/* 以STM32为例,Application的链接脚本片段 */
MEMORY
{
  FLASH (rx) : ORIGIN = 0x08010000, LENGTH = 384K
  RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 64K
}

警告:如果Application的链接脚本没改对,下载进去直接跑飞。别问我怎么知道的——我当年调试了整整两天。

2.5 双备份策略

对于要求高可靠性的分析仪器,我建议采用双备份策略。说白了就是:

  • Flash里放两个Application区:App_A 和 App_B。
  • Bootloader启动时,检查哪个区是好的,就启动哪个。
  • 升级时,先升级不用的那个区,升级完成后切换启动。

这样做的好处是:即使升级过程中断电,另一个区还是好的,设备可以正常启动。我曾经在一个气体分析仪项目里用了这个方案,客户后来反馈说「从来没变过砖」。

2.6 避坑指南

最后,分享几个我亲身踩过的坑:

  • 擦除粒度问题:Flash擦除是按扇区来的,不是按字节。如果你要修改一个字节,得先把整个扇区读到RAM,擦除,再写回去。这个操作很耗时,而且容易出错。
  • 写保护:有些芯片的Flash有写保护功能,升级前必须解锁。我曾经忘了解锁,结果升级程序卡死在那里。
  • 校验不能省:每次写入后,一定要读回来校验。我见过有人图省事不校验,结果Flash有坏块,程序跑起来就死机。

总结一句话:固件存储架构,就是给代码找个安全的家。分区规划好了,升级就稳了;分区没规划好,后面全是坑。

下一章,咱们聊聊具体的升级协议设计——怎么把固件从云端安全地送到设备里。