2. nRF芯片启动与存储布局

nRF5系列芯片(如nRF52832、nRF52840)的DFU升级能力,本质上是由Flash存储布局和启动流程共同决定的。理解这一点,是后续配置Bootloader、生成升级包、排查升级失败的前提。本节从Flash分区入手,逐步展开Bootloader、SoftDevice、Application三者的位置关系,再说明双区与单区DFU的差异。

一、Flash分区的基本结构

nRF芯片内部Flash通常为512KB或1MB,地址从0x00000000开始向上增长。Nordic的DFU方案把这整块Flash划分为若干区域,每个区域有明确的起始地址和大小。典型的分区包括:

  • SoftDevice:协议栈,位于Flash最底部,从0x00000000开始,占用固定大小(如S132 v7为0x26000,即152KB)。
  • Bootloader:紧接SoftDevice之后,负责启动判断和DFU执行,大小通常为0x26000~0x30000之间。
  • Application:用户固件,位于Bootloader之后,是正常运行时执行的代码。
  • Bootloader Settings:一小块配置区,记录当前有效固件、CRC校验值、升级状态等。
  • MBR:主引导记录,位于0x00000000之前的一小段保留区,负责在SoftDevice和Bootloader之间做跳转。

这些区域的边界不是随意设定的,而是由链接脚本(linker script)和Bootloader配置共同约束。如果Application的起始地址与Bootloader的结束地址不匹配,芯片启动后就会跳转到错误位置,表现为“程序不运行”或“HardFault”。

nRF52840 Flash 分区布局(双区DFU示例) Flash 地址空间 MBR (0x00000000 前保留) SoftDevice (S140) 0x00000000 – 0x00026000 Bootloader 0x00026000 – 0x0002F000 Application 0x0002F000 – 0x0005B000 Bootloader Settings 0x0005B000 – 0x0005C000 DFU 双区暂存区 0x0005C000 – 0x00080000 0x00000000 0x00026000 0x0002F000 0x0005B000 0x0005C000 0x00080000 启动顺序 1. 复位 → MBR 执行 2. MBR 跳转 SoftDevice 3. SoftDevice 初始化后跳 Bootloader Bootloader 判断 • 检查 Settings 中有效固件 • 校验 Application CRC • 有效 → 跳转 Application DFU 触发条件 • 按键长按进入 DFU • 应用请求 DFU 标志 • 固件 CRC 校验失败

二、启动流程与各区域职责

芯片上电或复位后,首先执行的是MBR(Master Boot Record)。MBR是一段极小的代码,位于Flash最前端,它的职责非常单一:判断当前应该跳转到SoftDevice还是Bootloader。在正常启动路径中,MBR会先跳转到SoftDevice,SoftDevice完成协议栈初始化后,再跳转到Bootloader。Bootloader读取Bootloader Settings区域,检查Application是否有效(CRC校验通过、版本标记正确),如果有效则跳转到Application执行;如果无效或收到DFU请求,则停留在Bootloader中等待升级。

这里有一个容易混淆的点:SoftDevice并不是“操作系统”,它只是一组协议栈库和中断向量表。Application在编译时需要链接SoftDevice提供的API,并且中断向量表要放在Application起始地址处。如果Application的链接地址与SoftDevice预留的地址冲突,编译能通过,但运行时会覆盖协议栈数据,导致蓝牙连接异常或芯片死机。

三、双区DFU与单区DFU

Nordic的DFU机制分为双区(Dual Bank)和单区(Single Bank)两种模式,它们的核心差异在于升级过程中新固件存放的位置和回滚能力。

对比项双区DFU单区DFU
新固件存放位置暂存区(Bank 1)直接覆盖Application区
升级过程中设备可用性旧固件继续运行设备暂停,等待升级完成
回滚能力支持,升级失败可回退不支持,失败需重新烧录
Flash占用需要额外暂存区,占用较大占用小,适合Flash紧张场景
典型适用场景OTA升级、要求高可靠性产线烧录、Flash资源受限

双区DFU的工作过程可以这样理解:设备当前运行Application(Bank 0),收到升级包后,Bootloader把新固件写入Bank 1暂存区。写入完成后,Bootloader校验新固件完整性,如果通过,则把Bank 1的内容复制到Bank 0,或者直接标记Bank 1为有效并跳转过去。如果升级中途断电或校验失败,Bootloader仍然可以回退到原来的Bank 0,设备不会变砖。

单区DFU则没有暂存区。Bootloader收到升级包后,直接擦除Application区并写入新固件。这个过程一旦中断,Application区就是残缺的,设备只能停留在Bootloader中,等待重新升级。因此单区DFU通常用于产线烧录或有线升级场景,不建议用于无线OTA。

在nRF Connect SDK或nRF5 SDK中,双区DFU的暂存区大小必须大于等于Application的最大尺寸。如果Application编译后为180KB,暂存区至少需要180KB,否则升级包无法完整写入。

四、实际配置中的关键参数

在具体工程中,Flash分区不是手动随意划分的,而是通过链接脚本和Bootloader配置共同确定。以nRF5 SDK为例,几个关键参数需要保持一致:

  • Bootloader起始地址:由SoftDevice大小决定,例如S132 v7占用0x26000,则Bootloader从0x26000开始。
  • Application起始地址:由Bootloader大小决定,通常在0x2F000或0x30000附近。
  • Bootloader Settings地址:由Bootloader和Application大小共同决定,通常紧接Application之后。
  • DFU暂存区起始地址:紧接Bootloader Settings之后,到Flash末尾。

这些地址在bootloader.ld、application.ld以及dfu_settings.h中都有对应定义。如果修改了SoftDevice版本或Bootloader大小,必须同步更新所有相关链接脚本和配置,否则会出现“升级包生成成功但设备无法启动”的问题。

// 示例:nRF5 SDK中Bootloader Settings的地址定义
#define BOOTLOADER_SETTINGS_ADDRESS  0x0005B000
#define BOOTLOADER_SETTINGS_PAGE_SIZE 0x1000

// Application起始地址(需与链接脚本一致)
#define CODE_START                   0x0002F000
#define CODE_SIZE                    0x0002C000

实际排查启动问题时,可以先用nRF Connect Programmer读取芯片Flash内容,确认各区域是否有数据、起始地址是否正确。如果Application区为空或全是0xFF,说明Bootloader没有成功跳转,需要检查Settings中的有效固件标记和CRC值。

修改Flash分区后,必须重新生成Bootloader Settings并烧录,否则Bootloader仍按旧地址判断Application有效性,导致新固件无法被识别。

总结来说,nRF芯片的启动与存储布局是一个“地址约束链”:SoftDevice决定Bootloader起点,Bootloader决定Application起点,Application和Settings共同决定DFU暂存区起点。双区DFU通过额外暂存区换取升级安全性和回滚能力,单区DFU则以牺牲可靠性为代价节省Flash空间。理解这条链,才能在后续配置DFU时做出正确取舍。