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”。
二、启动流程与各区域职责
芯片上电或复位后,首先执行的是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。
四、实际配置中的关键参数
在具体工程中,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值。
总结来说,nRF芯片的启动与存储布局是一个“地址约束链”:SoftDevice决定Bootloader起点,Bootloader决定Application起点,Application和Settings共同决定DFU暂存区起点。双区DFU通过额外暂存区换取升级安全性和回滚能力,单区DFU则以牺牲可靠性为代价节省Flash空间。理解这条链,才能在后续配置DFU时做出正确取舍。