3. DFU架构与双区/单区模式

nRF5 SDK 的 DFU(Device Firmware Update)并不是一个简单的“把新固件写进去”的动作,而是一套由 Bootloader、SoftDevice、应用固件和 DFU 服务共同构成的升级体系。理解这套体系的关键,是先分清两个问题:升级时新固件放在哪里,以及谁在负责搬运和校验。前者对应双区与单区模式,后者对应 Bootloader 的角色。

一、双区模式:Bank0 与 Bank1 的接力

双区(Dual Bank)是 nRF5 SDK 默认推荐的 DFU 模式。Flash 被划分为两个固件区:Bank0 存放当前正在运行的应用固件,Bank1 作为“暂存区”接收新固件。升级时,Bootloader 先把新固件完整写入 Bank1,校验通过后再把 Bank1 的内容复制到 Bank0,最后跳转到新固件运行。

这种“先存后搬”的机制带来两个直接好处:一是升级过程中即使断电,Bank0 中的旧固件仍然完整,设备可以继续运行;二是新固件在复制前已经过 CRC 校验,能避免把损坏的镜像写入运行区。代价是需要额外的 Flash 空间,通常要求芯片 Flash 容量至少能容纳两份应用固件加 Bootloader 和 SoftDevice。

双区模式下的关键参数是 bank_0 和 bank_1 的地址与大小,它们由链接脚本和 DFU 配置共同决定。例如在 dfu_types.h 中可以看到类似定义:

#define CODE_REGION_1_START  (0x00000000 + BOOTLOADER_SIZE + SOFTDEVICE_SIZE)
#define CODE_REGION_1_END    (CODE_REGION_1_START + BANK_0_SIZE)
#define CODE_REGION_2_START  (CODE_REGION_1_END)
#define CODE_REGION_2_END    (CODE_REGION_2_START + BANK_1_SIZE)

实际项目中,Bank0 和 Bank1 的大小必须一致,且要留出足够的空间给 Bootloader 和 SoftDevice。如果应用固件接近 Bank 上限,升级会因空间不足而失败,这是现场维护中常见的“升级包写不进去”的原因之一。

二、单区模式:省空间但风险更高

单区(Single Bank)模式只保留一个固件区。升级时,Bootloader 直接擦除并写入当前运行区,新固件覆盖旧固件。它节省了约一半的固件存储空间,适合 Flash 容量紧张或成本敏感的产品。

但单区模式的代价也很明显:写入过程中如果断电或复位,旧固件已被擦除,新固件又未写完,设备将无法启动,只能进入 Bootloader 等待重新升级。因此单区模式通常要求配合外部存储、双备份或严格的电源管理使用。在 nRF5 SDK 中,单区模式通过 NRF_DFU_SINGLE_BANK 或相关配置启用,且对 Bootloader 的启动逻辑有额外要求。

单区模式不适合“升级过程中可能断电”的现场设备。若产品无法保证升级期间稳定供电,应优先选择双区模式,或增加外部 Flash 做镜像暂存。

三、Bootloader 的角色:不只是搬运工

Bootloader 是 DFU 的执行主体。它位于 Flash 起始区域,上电后先运行,判断是否需要进入 DFU 流程。它的职责包括:

  • 初始化 DFU 传输接口(BLE、UART、USB 等);
  • 接收升级包,写入 Bank1 或直接写入运行区;
  • 对接收到的镜像做 CRC 校验和签名验证;
  • 在双区模式下执行 Bank1 到 Bank0 的复制;
  • 更新 Bootloader 设置区中的固件版本、有效标志等元数据;
  • 跳转到应用固件或 SoftDevice。

Bootloader 自身也可以被升级,这称为 Bootloader DFU。它通常与 SoftDevice 升级一起构成“完整升级包”。在现场维护中,如果 Bootloader 版本过旧,可能不支持新的签名算法或传输特性,因此升级 Bootloader 往往是解决兼容性问题的第一步。

四、传输流程与状态机

DFU 传输过程可以概括为“初始化—传输—校验—激活”四个阶段。以 BLE DFU 为例,手机端先扫描并连接到设备广播的 DFU 服务,然后发送启动命令,Bootloader 进入接收状态;接着按数据包分片传输固件,每包都有序号和 CRC;全部接收完成后,Bootloader 校验整个镜像,若通过则写入 Bank0 并复位。

在 Bootloader 内部,这一过程由 DFU 状态机管理。常见状态包括:IDLE(空闲)、RECEIVING(接收中)、VALIDATING(校验中)、ACTIVATING(激活中)和 ERROR(错误)。状态迁移由事件驱动,例如收到启动命令从 IDLE 进入 RECEIVING,收到完整镜像后进入 VALIDATING,校验通过后进入 ACTIVATING。任何一步失败都会进入 ERROR,并可通过 DFU 协议查询错误码。

nRF5 SDK DFU 双区模式传输与状态机 Flash 分区(双区) Bootloader + SoftDevice Bank0:当前运行固件 Bank1:新固件暂存区 Bootloader 设置区(版本/有效标志) DFU 状态机 IDLE RECEIVING VALIDATING ACTIVATING ERROR 启动 收完 校验通过 失败 复位 传输数据流(BLE DFU 示例) 手机/上位机 DFU 主控 BLE 传输层 GATT 服务 Bootloader 接收/校验 Bank1 暂存新固件 Bank0 复制后运行 校验通过后复制 分片发送 写入命令 CRC 校验

五、现场维护中的判断依据

在实际维护中,判断 DFU 是否成功,不能只看“升级完成”提示。应检查以下信息:设备复位后是否正常广播应用服务;Bootloader 设置区中的固件版本是否更新;Bank0 的 CRC 是否与升级包一致;如果升级失败,读取 DFU 错误码,常见的有 NRF_DFU_ERR_CRC(校验失败)、NRF_DFU_ERR_SIZE(空间不足)和 NRF_DFU_ERR_TIMEOUT(传输超时)。

双区与单区的选择,本质上是在“空间”和“可靠性”之间做权衡。对于现场维护体系而言,双区模式提供了可回退的升级路径,是更稳妥的默认方案;单区模式则适合空间受限且升级环境可控的场景。理解 Bootloader 的状态机和传输流程,才能在升级失败时快速定位是传输问题、校验问题还是空间问题。