2. 固件升级基础

在双分区 A/B 升级系统中,很多工程问题并不是“升级代码写错了”,而是对基础术语理解不一致:有人把 Bootloader 当成固件,有人把镜像和分区混为一谈,也有人认为“版本号相同就一定不用升级”。本节先把这些概念一次讲清,为后续 A/B 分区切换、回滚策略和升级状态机打下共同语言。

一、核心概念:从 Flash 到可运行系统

嵌入式设备的非易失存储(通常是 Flash)被划分为若干分区,每个分区是一段有固定起始地址和长度的存储区域。分区表就是描述这些区域边界与用途的元数据,它决定了 Bootloader 去哪里找镜像、升级程序往哪里写数据。

  • 固件(Firmware):运行在设备上的完整软件集合,通常包含 Bootloader、应用程序、文件系统、配置区等。口语中“刷固件”往往指更新应用镜像,但严格说固件是整体。
  • Bootloader:上电后最先运行的引导程序,负责初始化时钟、内存、外设,校验镜像完整性,选择从 A 分区还是 B 分区启动,并在必要时执行回滚。
  • 分区表:定义各分区名称、偏移、大小、类型和标志位。A/B 系统中至少包含 app_a、app_b,可能还有 bootloader、factory、data、misc 等。
  • 镜像(Image):可被烧录或升级的二进制文件,如 app.bin、boot.bin、rootfs.img。镜像通常带头部信息,包含版本、长度、校验值和签名。
  • 版本号:标识镜像新旧与兼容性的字符串或整数,常见形式为语义化版本 1.4.2 或构建号 20240612.1030。
  • 回滚(Rollback):当新镜像启动失败、校验不通过或看门狗超时,系统自动切回上一可用分区的过程。

一句话理解:分区表是“地图”,镜像是“货物”,Bootloader 是“调度员”,版本号是“货物标签”,回滚是“送错货后的退货通道”。

二、A/B 双分区下的启动与升级数据流

下面这张图展示了一个典型 A/B 系统的核心结构:Bootloader 读取分区表和元数据,决定从哪个 slot 启动;升级时数据写入非活动分区,校验通过后切换启动标志。

A/B 双分区固件升级系统结构 Bootloader 校验镜像 / 选择 slot 失败则触发回滚 分区表 app_a / app_b boot / factory / data misc(启动标志) 升级元数据 active_slot = A boot_ok = true retry_count = 0 Flash 存储布局 Slot A(活动) app_a.bin v1.4.2 状态:运行中 校验:CRC32 / SHA256 签名:有效 Slot B(待升级) app_b.bin v1.5.0 状态:已写入待验证 校验:CRC32 / SHA256 签名:有效 boot 分区 Bootloader 镜像 factory 分区 出厂恢复镜像 data 分区 用户配置 / 日志 升级写入 回滚切换 升级流程:写入非活动 slot → 校验签名与 CRC → 更新 misc 启动标志 → 重启验证 → 成功则确认,失败则回滚 关键原则:任何时刻至少保留一个可启动的完整镜像

三、版本号、镜像头与校验:升级判断的依据

版本号不只是给人看的字符串,它参与升级决策。设备上报当前版本,云端比较后决定是否下发新镜像。常见比较规则包括:主版本不同必须升级,次版本递增允许升级,构建号相同则跳过。镜像头通常包含以下字段:

struct image_header {
    uint32_t magic;        // 0x5A5A5A5A
    uint32_t header_crc;   // 头部校验
    uint32_t image_len;    // 镜像长度
    uint32_t image_crc;    // 镜像体校验
    char     version[16];  // "1.5.0"
    uint8_t  slot;         // 目标分区 A/B
    uint8_t  signature[];  // 数字签名
};

Bootloader 在启动前会依次检查 magic、header_crc、image_crc 和签名。任何一项失败,都不会跳转到该 slot,而是根据元数据决定重试或回滚。

四、常见术语与误区对照

术语 正确理解 常见误区
固件 设备上完整软件集合 只把 app.bin 叫固件
Bootloader 引导、校验、选择 slot、回滚 认为它不参与升级逻辑
分区表 定义偏移、大小、用途 升级时随意改动分区边界
镜像 带头部和签名的可烧录文件 把裸 bin 直接写入分区
版本号 参与升级判断和兼容性检查 只看字符串,不比较构建号
回滚 失败后切回上一可用 slot 认为回滚等于重新刷旧包

五、案例与复盘:一次“升级成功但无法启动”的排查

某设备 OTA 升级后重启,串口日志显示:

[BOOT] active_slot = B
[BOOT] verify image_b ... FAIL (crc mismatch)
[BOOT] retry_count = 1
[BOOT] switch to slot A
[BOOT] boot_ok = true

复盘发现:升级程序写入 B 分区后,没有回读校验就更新了启动标志。网络抖动导致最后 4KB 数据不完整,CRC 失败。Bootloader 按策略回滚到 A,设备恢复运行。这个案例说明:写入完成不等于升级成功,必须经过“写入 → 回读校验 → 更新标志 → 重启验证 → 确认”完整链路。

误区提醒:不要在活动分区上直接覆盖写入新镜像。A/B 的核心价值就是“后台写入、原子切换”,一旦破坏活动分区,回滚将失去落脚点。

工程建议:为每个 slot 维护独立的版本号、校验值和启动状态;misc 分区中的 active_slot、boot_ok、retry_count 三个字段是回滚策略的最小集合,务必保证写入原子性。

掌握这些基础概念后,后续学习 A/B 切换流程、升级状态机、差分升级和回滚策略时,就能准确判断每一步操作的对象、依据和风险边界。