第2章:固件升级核心原理:固件分区表设计

各位同学,今天我们来聊聊固件升级里最核心的东西——分区表设计。说实话,很多刚入行的工程师觉得升级不就是把新固件写进去吗?哪有那么简单。我在项目里见过太多次因为分区设计不合理,导致设备变砖的惨案了。

分区表,说白了就是给Flash存储器画格子。每个格子放什么,多大尺寸,谁先启动,谁做备份,这些都得提前规划好。你想想看,如果连这些都没想清楚,后面做升级方案就是空中楼阁。

2.1 三大核心分区:Bootloader、App、Recovery

一个成熟的物联网设备,至少要有三个分区。我习惯把它们比作一个团队:Bootloader是队长,App是干活的主力,Recovery是替补队员。

2.1.1 Bootloader分区

Bootloader是设备上电后第一个运行的程序。它的任务很简单:检查App分区是否完好,如果完好就跳过去执行;如果坏了,就启动Recovery模式。

我个人习惯把Bootloader做得尽量小,功能单一。为什么?因为Bootloader出问题,设备就彻底变砖了。越小越不容易出错。

关键点:Bootloader分区必须是只读的,或者有硬件写保护。我在一个项目中吃过亏,Bootloader被意外覆盖,整批设备都得返厂。

2.1.2 App分区

App分区存放主程序固件。这是最常更新的区域。设计时要注意两点:

  • 分区大小要留余量——固件只会越来越大,不会越来越小。我建议至少留30%的余量。
  • 版本号管理——每个固件都要带版本号,Bootloader靠它判断是否需要升级。

2.1.3 Recovery分区

Recovery分区是个保险。当App分区损坏时,设备可以进入Recovery模式,通过某种方式(比如U盘、网络、串口)重新烧录固件。

嗯,这里要注意:Recovery分区里的程序也要能升级,但频率很低。我一般只在出厂时烧录一次,后续除非重大bug,否则不动它。

2.2 启动流程详解

设备上电后,启动流程是这样的:

  1. CPU从固定地址读取Bootloader
  2. Bootloader校验App分区的完整性(CRC或签名验证)
  3. 如果App完好,跳转到App入口地址
  4. 如果App损坏,跳转到Recovery分区
  5. Recovery模式下等待用户干预或自动恢复

为什么会这样设计?因为设备可能在任何时候断电。如果升级过程中突然断电,App分区可能只写了一半。这时候Bootloader就能检测到校验失败,自动进入Recovery模式。

我的经验:在Bootloader里加一个"启动计数器"。如果连续3次启动失败,就强制进入Recovery模式。这能防止某些偶发性的硬件问题导致设备反复重启。

2.3 回滚机制

回滚,就是升级失败后能退回到上一个版本。这个功能在工业设备上尤其重要。你想想看,如果升级后设备全都不工作了,生产线得停多久?

实现回滚有两种常见方案:

方案一:双备份区(A/B分区)

这是最可靠的方式。把Flash分成两个App区:App_A和App_B。当前运行的是App_A,升级时就把新固件写入App_B。写入完成后,Bootloader下次启动时切换到App_B。

如果App_B启动失败,Bootloader自动切回App_A。这就是回滚。

分区 大小 用途
Bootloader 64KB 启动管理
App_A 1MB 当前运行版本
App_B 1MB 新版本/备份
Recovery 256KB 紧急恢复
配置区 64KB 存储启动标志、版本号等

这种方案的缺点很明显:Flash占用翻倍。对于成本敏感的物联网设备,可能不太现实。

方案二:增量升级 + 备份指针

如果Flash空间有限,可以用这个方案。升级时先把当前固件备份到另一个区域,然后写入新固件。如果新固件启动失败,就从备份区恢复。

我曾经在一个项目里用过这个方案。当时Flash只有2MB,但固件已经占了1.5MB。没办法做双备份,就只能用备份指针的方式。效果还行,但恢复速度比双备份慢一些。

警告:无论用哪种方案,升级过程中绝对不能断电!如果实在无法避免,至少要在Bootloader里做好断点续传的机制。我见过最惨的情况是:升级到一半断电,Bootloader和App都坏了,设备彻底变砖。

2.4 分区表设计实战建议

说了这么多理论,来点实际的。我总结了几条设计原则:

  • 分区边界要对齐Flash的擦除块大小——比如NAND Flash的擦除块是128KB,那分区大小最好是128KB的整数倍。否则会浪费空间,而且擦除操作会变慢。
  • 配置区单独放——不要把版本号、启动标志这些信息放在App分区里。否则升级时这些信息也会被覆盖,Bootloader就不知道当前是什么版本了。
  • 预留一个"工厂分区"——存放出厂时的原始固件。万一所有分区都坏了,还能从工厂分区恢复。这个分区我一般设为只读。

最后说一句:分区表设计没有标准答案。不同的硬件、不同的应用场景,方案都不一样。但核心思想是一样的——保证设备在任何情况下都能启动,都能恢复。

下一章我们会讲具体的升级协议设计,包括HTTP、MQTT、CoAP这些协议怎么选,怎么保证传输的可靠性。到时候再聊。