3. nRF 固件量产版本管理

在 nRF 系列产品的量产交付中,固件版本管理不是简单的“打个 tag 存档”,而是决定产线能否稳定复现、售后能否精准追溯的核心工程能力。nRF5 SDK 与 SoftDevice(S 系列协议栈)是两套独立发布、独立版本号的组件,二者通过 sdk_config.h 与协议栈 API 头文件耦合;nRF Connect SDK(NCS)则把协议栈、驱动、应用统一到 Zephyr 与 west 的版本体系中。无论哪条技术路线,量产版本管理都要回答四个问题:锁定了什么、烧了什么、参数从哪来、出了问题怎么追。

一、SDK 与 SoftDevice 版本锁定

版本锁定要落到“可复现”三个字上。对 nRF5 SDK 项目,至少锁定四项:SDK 版本号(如 nRF5_SDK_17.1.0_ddde560)、SoftDevice 版本与规格(如 S140 v7.3.0,对应 hex 文件 s140_nrf52_7.3.0_softdevice.hex)、编译工具链版本(GCC ARM Embedded 或 Keil MDK 具体版本)、以及芯片型号与修订版(如 nRF52840 QIAA / build code)。SoftDevice 与 SDK 存在兼容矩阵:S140 v7.x 要求 SDK 17.x,且不同 SoftDevice 版本对 RAM 起始地址、中断向量表位置、可用外设资源的要求不同,混用会直接导致启动异常或协议栈初始化失败。

对 NCS 项目,锁定对象是 west.yml 中 manifest 的 revision(commit hash 或 tag)、Zephyr 版本、nrfxlib 与 MCUboot 版本,以及 west build 使用的 board 定义。建议在仓库根目录维护一份 version.lock 或 manifest 快照,把上述信息以文本形式固化,并在 CI 中校验:任何一次量产固件构建,都必须从锁定版本检出,禁止使用“最新 master”。

版本锁定不是禁止升级,而是让升级成为一次显式、可评审、可回滚的动作。升级 SoftDevice 前,先在测试固件上验证协议栈初始化、Flash 分区与 DFU 流程,再进入量产固件。

二、生产固件与测试固件分离

产线上常见的错误是“用同一份固件既做功能测试又做最终交付”。正确做法是把固件分成两类:测试固件(Test FW)用于产线功能验证、射频校准、外设自检,允许包含调试串口、测试模式、临时 GPIO 翻转等;生产固件(Production FW)是最终交付给客户的版本,关闭调试接口、固定安全配置、只保留必要功能。二者共享同一套业务代码,但通过编译宏或独立配置区分,例如:

// 编译期区分
#if defined(FW_BUILD_TEST)
    #define DEBUG_UART_ENABLED   1
    #define TEST_MODE_GPIO       1
#else
    #define DEBUG_UART_ENABLED   0
    #define TEST_MODE_GPIO       0
#endif

// 版本号注入
#define FW_VERSION_MAJOR   1
#define FW_VERSION_MINOR   4
#define FW_VERSION_PATCH   2
#define FW_BUILD_TYPE      "PROD"   // 或 "TEST"

分离的关键在于:测试固件可以“脏”,生产固件必须“干净”。测试固件允许通过 SWD 反复烧录、允许打印日志;生产固件应启用读保护(APPROTECT)、关闭不必要的调试外设,并在启动时校验自身完整性。产线流程通常是:先烧测试固件完成校准与功能测试,再烧生产固件并写入配置参数,最后做一次最小化冒烟测试。

三、版本号烧录与追溯

版本号不能只存在于源码里,必须“烧进芯片、可被读出”。nRF 常用做法是在 Flash 固定地址(如 UICR 或自定义 Flash 页)写入一个版本结构体,包含固件版本、构建时间、Git commit 短哈希、构建类型。设备启动或产线工具通过 SWD/串口读取该结构体,即可确认当前芯片上运行的是哪一版固件。

typedef struct __attribute__((packed)) {
    uint32_t magic;        // 0x4E524656 "NRFV"
    uint8_t  major;
    uint8_t  minor;
    uint8_t  patch;
    uint8_t  build_type;   // 0=PROD, 1=TEST
    uint32_t git_hash;     // commit 短哈希
    uint32_t build_time;   // Unix 时间戳
} fw_version_t;

// 放置到固定 Flash 地址,例如 0x0007F000
const fw_version_t g_fw_version
    __attribute__((section(".fw_version"))) = {
    .magic = 0x4E524656,
    .major = 1, .minor = 4, .patch = 2,
    .build_type = 0,
    .git_hash = 0xA1B2C3D4,
    .build_time = 1710000000
};

追溯链要串起来:芯片唯一 ID(FICR DEVICEID)→ 烧录记录 → 固件版本 → 配置参数 → 测试结果。产线每烧录一颗芯片,就把 DEVICEID、固件版本、MAC、密钥版本、校准值、测试结论写入 MES 或本地数据库。售后返回一台设备时,读出 DEVICEID 和版本结构体,就能反查它当时用的是哪批参数、哪版固件、测试是否通过。

四、配置参数的生成与注入

量产参数主要包括三类:MAC 地址(BLE 公有地址或随机静态地址)、密钥(如绑定密钥、加密密钥、LICENSE key)、校准值(射频偏置、晶振负载电容、温度补偿系数等)。这些参数不能硬编码在固件里,必须“一机一参”,由产线工具生成并注入。

典型注入方式有两种:一是通过 SWD 用 nRF Util 或自研工具写入 UICR/Flash;二是通过串口 Bootloader 或自定义协议写入。以 MAC 为例,nRF52 的 FICR 中有出厂预置的 48 位设备地址,但量产中通常使用自定义 MAC 段,由产线从号段池分配,写入 UICR 的 CUSTOMER[0] 和 CUSTOMER[1] 寄存器,固件启动时读取并设置为 BLE 地址。

# 使用 nrfutil 写入 UICR 示例(示意)
nrfutil settings generate --family NRF52840 \
  --application app.hex --application-version 1 \
  --bootloader bootloader.hex --bootloader-version 1 \
  --bl-settings-version 2 settings.hex

nrfjprog --program settings.hex --chiperase
nrfjprog --program s140_nrf52_7.3.0_softdevice.hex --sectorerase
nrfjprog --program app.hex --sectorerase
nrfjprog --reset

密钥注入要特别注意安全:密钥不应以明文形式出现在产线日志或 MES 中,建议使用加密传输、一次性写入、写入后校验回读。校准值则应在测试工位上实测后写入,例如射频输出功率校准,先测量实际功率,再计算偏置值写入 UICR 或 Flash 参数区,固件启动时加载并应用到 RADIO 外设。

配置参数区必须与固件升级解耦。DFU 升级时不能擦除参数区,否则设备会丢失 MAC、密钥和校准值。建议在 Flash 分区表中把参数区标记为“保留”,并在 Bootloader 中显式跳过该区域。

五、版本管理闭环

把上述四件事串起来,就形成量产版本管理的闭环:锁定版本 → 构建测试/生产固件 → 产线烧录并注入参数 → 记录追溯信息 → 售后反查。任何一次量产,都应该能回答:这批设备用的是哪版 SDK、哪版 SoftDevice、哪版应用固件、哪批 MAC 号段、哪组校准参数、测试通过率多少。做不到这一点,故障闭环就无从谈起。

nRF 量产固件版本管理与参数注入闭环 版本锁定 SDK 17.1.0 S140 v7.3.0 GCC 10.3 / MDK 5.36 nRF52840 QIAA west.yml / version.lock 固件分离 测试固件 TEST 调试串口 / 测试模式 生产固件 PROD 读保护 / 关闭调试 参数生成与注入 MAC 地址(号段分配) 密钥(加密写入) 校准值(实测偏置) UICR / Flash 参数区 追溯记录 DEVICEID 固件版本结构体 MAC / 密钥版本 测试结论 MES / 本地数据库 产线烧录流程 烧测试固件 功能测试/校准 烧生产固件 注入参数 冒烟测试+记录 售后反查闭环 读出 DEVICEID + 版本结构体 → 反查烧录记录 → 定位固件版本 / 参数批次 / 测试结论 任一环节缺失,故障闭环即断裂

最后强调一点:版本管理的价值不在“记录”,而在“可复现”和“可反查”。当产线出现批量异常、售后返回故障机时,能在一分钟内定位到固件版本、参数批次和测试记录,才是这套机制真正发挥作用的时候。