2. nRF设备启动与固件存储布局
nRF5系列芯片(如nRF52832、nRF52840)的固件升级体系,本质上是一套围绕内部Flash地址空间展开的“分区协作”机制。理解这套机制的关键,不是记住某个具体地址,而是理解四个角色——SoftDevice、Bootloader、Application、Bootloader Settings——各自占据哪段Flash、谁先运行、谁负责搬运、谁负责校验,以及链接脚本如何把这些约定固化到编译产物中。
一、Flash分区的基本模型
nRF5芯片的Flash从0x00000000开始编址,容量依型号不同(nRF52832为512KB,nRF52840为1MB)。Nordic的升级体系把这整块Flash划分为若干连续区域,每个区域有明确的起始地址和大小。典型布局如下:
- SoftDevice区:从0x00000000开始,包含协议栈和底层驱动,由Nordic以二进制形式提供,不可修改。
- Application区:紧接SoftDevice之后,存放用户应用固件。
- Bootloader区:位于Application之后,负责接收新固件、校验、搬运和跳转。
- Bootloader Settings区:位于Bootloader之后,保存当前有效固件信息、CRC校验值、升级标志等元数据。
- MBR区:在启用SoftDevice时,MBR(Master Boot Record)占据0x00000000起始的一小段,负责中断向量转发和启动调度。
这些区域的边界不是随意设定的,而是由SoftDevice的规格、Bootloader的链接脚本、Application的链接脚本三者共同约定。任何一方的地址偏移写错,都会导致启动失败或升级后无法跳转。
二、链接脚本如何固化分区约定
在nRF5 SDK中,Application的链接脚本(通常是 flash_placement.xml 或 .ld 文件)通过 FLASH_ORIGIN 和 FLASH_SIZE 两个宏来指定Application的起始地址和可用空间。这两个值必须与SoftDevice的结束地址对齐,且不能侵入Bootloader区域。
以nRF52832 + S132 SoftDevice为例,S132 v7.0.1的结束地址为0x26000,Bootloader起始地址通常配置为0x66000。因此Application的链接脚本应设置为:
FLASH_ORIGIN = 0x26000;
FLASH_SIZE = 0x40000; /* 0x66000 - 0x26000 = 0x40000,即256KB */
Bootloader的链接脚本则独立配置,其起始地址由 BOOTLOADER_START_ADDR 宏定义,通常与Application的结束地址对齐。Bootloader Settings区的起始地址由 BOOTLOADER_SETTINGS_ADDR 指定,一般紧接Bootloader之后。
这里有一个容易出错的细节:SoftDevice的结束地址不是固定值,不同版本、不同协议栈(S132、S140、S112)的结束地址不同。例如S140 v7.2.0的结束地址为0x27000,比S132多出4KB。如果Application的链接脚本仍按0x26000配置,就会与SoftDevice区域重叠,导致启动时HardFault。
三、Bootloader Settings区的数据结构
Bootloader Settings区保存的是升级体系的核心元数据,其结构体 bootloader_settings_t 包含以下关键字段:
| 字段 | 含义 | 典型值/说明 |
|---|---|---|
| bank_0 | 当前有效固件信息 | 包含固件长度、CRC校验值 |
| bank_1 | 待升级固件信息 | DFU过程中写入 |
| bootloader_size | Bootloader自身大小 | 用于校验Bootloader完整性 |
| app_size | Application大小 | 用于校验Application完整性 |
| crc | Settings区自身CRC | 防止Settings被篡改 |
每次DFU升级完成后,Bootloader会更新Settings区中的bank_0信息,将新固件的CRC和长度写入,然后跳转到新的Application。如果Settings区的CRC校验失败,Bootloader会认为当前固件无效,进入DFU模式等待重新升级。
四、地址规划的实际操作步骤
在实际项目中,地址规划通常按以下顺序进行:
- 确定SoftDevice型号和版本,查阅Nordic官方文档获取其结束地址。例如S132 v7.0.1的结束地址为0x26000。
- 确定Bootloader的起始地址。通常取Application结束地址向上对齐到4KB边界。例如Application结束于0x66000,则Bootloader起始于0x66000。
- 确定Bootloader Settings区的起始地址。通常取Bootloader结束地址向上对齐到4KB边界。例如Bootloader结束于0x7E000,则Settings起始于0x7E000。
- 将上述地址写入Application和Bootloader的链接脚本,并确保两者不重叠。
- 编译后使用
nrfutil或nrfjprog烧录,观察设备是否能正常启动并跳转到Application。
nrfjprog --memrd 命令读取Flash特定地址的内容,验证分区边界是否正确。例如读取0x26000处的前16字节,如果看到的是Application的中断向量表(栈顶指针和复位向量),说明Application确实从该地址开始存放。
五、常见地址规划错误与排查
地址规划错误通常表现为三类现象:设备上电后无响应、DFU升级后无法跳转、或运行一段时间后HardFault。排查时可按以下顺序检查:
- 检查链接脚本中的FLASH_ORIGIN是否与SoftDevice结束地址一致。不一致会导致向量表错位,设备无法启动。
- 检查Application的FLASH_SIZE是否侵入了Bootloader区域。如果Application编译后的大小超过了FLASH_SIZE,链接器会报错,但有时会静默截断,导致运行时异常。
- 检查Bootloader Settings区的地址是否与Bootloader重叠。重叠会导致Settings被Bootloader代码覆盖,升级后无法读取有效固件信息。
- 检查MBR是否被正确烧录。MBR是启动调度的第一环,如果MBR缺失或损坏,设备会直接卡在复位状态。
总结来说,nRF设备的Flash分区是一套由SoftDevice规格、链接脚本、Bootloader Settings三者共同约束的地址契约。理解每个区域的起始地址、大小和职责,并在链接脚本中精确固化这些约定,是固件升级体系能够稳定运行的基础。地址规划不是一次性的配置工作,而是每次更换SoftDevice版本或调整Bootloader功能时都需要重新核对的环节。