做低功耗物联网产品这两年,我基本绕不开 STM32WB 系列的选型。这颗芯片表面上是个普通射频 MCU,实际上里面住着两个核:Cortex-M4 跑应用逻辑,Cortex-M0+ 跑无线协议栈,而 ST 固件升级服务(Firmware Upgrade Services,简称 FUS)就悄悄住在 M0+ 上。注意,这里说的 ST 是意法半导体,不是 PLC 编程里的 ST 语言,别搞混了。FUS 平时不出声,但它负责的事情很关键:升级 BLE / 802.15.4 协议栈、维护安全启动、管理固件签名密钥。如果你的项目要 OTA 升级、要换协议栈版本、或者要过安全认证,基本都绕不过它。这套笔记是我把 FUS 从原理到实操完整过了一遍之后的总结,适合刚拿到开发板的同学,也适合已经在做量产的老手。
1. STM32WB 双核架构:为什么 M0+ 里还藏着一套固件升级服务
1.1 双核架构里,M4 和 M0+ 各自管什么
STM32WB 系列内部的架构,一句话概括就是:一颗应用核 + 一颗无线核。应用核是 Cortex-M4,主频 64 MHz,带 FPU 和 DSP 指令,负责跑你的业务逻辑、传感器驱动、GUI、RTOS、蓝牙应用层这些“重活”;无线核是 Cortex-M0+,同样跑 64 MHz,负责 BLE 协议栈、802.15.4 协议栈(Zigbee / Thread)、以及射频相关的底层服务。这两个核之间通过 IPC(Inter-Processor Communication)和共享内存通信。M4 要发蓝牙数据,并不会直接操作射频外设,而是把数据封装成消息丢给 M0+,由 M0+ 按照协议栈的状态机发出去。
为什么 ST 要把协议栈放在 M0+ 而不是和用户应用一起放在 M4 上?答案很简单:隔离。无线协议栈对时序和状态机的要求非常严格,如果跑在 M4 上,很容易被用户代码里的中断、临界区、看门狗打断,出问题之后排查起来极其痛苦。M0+ 独立跑协议栈,相当于把“射频实时性”这件事从应用工程里彻底剥离,M4 只要及时处理事件回调,协议栈自身的行为就可控。而 FUS 就运行在这个 M0+ 的私密环境里,比普通协议栈的层级还要底一层,普通用户程序根本没权限碰它。
1.2 FUS 的职责:协议栈升级、安全启动与密钥管理
FUS 的英文全称是 Firmware Upgrade Services,中文叫固件升级服务。它不是一个普通用户程序,而是 ST 出厂时预置在系统安全区里的管理型固件。它自己占据一小块 Flash,官方叫 Secure Flash Area,运行在 M0+ 上,不参与业务通信。它的核心工作有三块。
第一是协议栈升级。你拿到新版本 BLE 协议栈,比如从 1.13 升到 1.16,正常情况下不能直接拿 ST-Link 把 hex 往 Flash 里一拖就完事,因为协议栈要写入的安全区域是受 FUS 管控的。FUS 负责校验、解密、写入新版协议栈,并在必要时替换旧版本。这个机制保证了协议栈区域不会被随意覆盖。
第二是安全启动。芯片复位之后,M0+ 首先执行的就是 FUS(或无线协议栈的启动代码),启动代码会检查镜像是否有效、签名是否匹配,确认没问题之后才把执行权交给后续层级。这个流程叫 Secure Boot,主要防止有人篡改或伪冒固件。
第三是密钥管理。为了让协议栈能安全签名、安全升级,FUS 内部维护一组密钥,包括根密钥、固件验证密钥,甚至用于生成无线 OTA 会话密钥的随机源。普通用户应用访问不到这部分,只有通过特定命令接口才能触发。说得直白点,FUS 就是芯片内部的一个“安全管家”,负责看管最敏感的底层资源。
1.3 没有 FUS 的话,你的项目会遇到什么问题
有人可能会问:我不升级协议栈,也不在乎安全,可以不要 FUS 吗?理论上,你把 Flash 整个擦空再烧一份用户程序,MCU 也能跑,但实际操作中会遇到三类麻烦。
第一,协议栈版本陈旧。新发布的 SDK 可能要求最低协议栈版本,某个新功能或 Bug 修复必须建立在 1.16 以上。没有 FUS,你就只能靠调试器全片擦除再烧录,风险高、步骤多,稍不注意就误伤别的分区。
第二,OTA 升级无从谈起。现在做消费电子产品,几乎都要求能通过蓝牙远程升级应用和协议栈。无线升级协议栈这件事本身就是由 FUS 承载的,如果 FUS 版本不支持或根本不存在,OTA 升级只能停留在改应用层的阶段,对协议栈本身无能为力。
第三,产线烧录效率低。有了 FUS,产线可以通过命令接口把协议栈和应用分开刷写,也可以让整机出厂后再通过无线方式补齐协议栈版本,不用每台都开壳接调试器。没有 FUS,产线就只能按最原始的方式整片烧录,灵活性差很多。
所以结论很简单:FUS 不是可有可无的后台程序,而是 STM32WB 平台在做安全升级和量产维护时的一根地基支柱。
2. FUS 版本和内存布局:升级前必看的三个底层概念
2.1 如何读取 FUS 版本:v0.x 和 v1.x 的区别
想升级 FUS 之前,必须先搞清楚芯片里当前 FUS 的版本。在 STM32CubeProgrammer 里,右侧栏切成Firmware Upgrade Services面板,连接成功后很快就能看到一行信息:FUS version。常见的显示是 v0.8.0、v1.2.0 这种格式,V 后面第一位是关键区分点。
| FUS 版本 | 含义 | 关键能力 |
|---|---|---|
| v0.x | 早期量产版本 | 支持线下升级协议栈;不支持通过无线接口升级 FUS 自身 |
| v1.x | 新一代版本 | 支持通过无线方式升级 FUS 和协议栈 |
| 无 FUS | 出厂被擦除或异常 | 需要先刷入标准 FUS 固件 |
如果显示 v1.x,基本代表这颗芯片具备完整的 OTA 维护能力,这也是现在的主流选择。v0.x 不是完全不能用,只是它在安全升级和远程维护上受限,很多新 SDK 已经直接要求至少 v1.x。所以拿到新板子如果发现是 v0.x,后面大概率要做一次 FUS 升级。
实操时注意一个关键点:FUS 版本信息要结合芯片型号看。STM32WB55 和 STM32WB15 的 FUS 固件文件不通用,烧错了轻则升级失败,重则把无线核搞成不可用状态。下载 FUS 固件时,优先到 STM32CubeWB 固件包里对应芯片型号的文件夹下拿,不要随便从第三方仓库下载。
2.2 Flash 分区:FUS、无线协议栈、用户应用的位置关系
既然 FUS 要管理协议栈,它肯定知道协议栈住在哪。STM32WB 系列的 Flash 分区不是用户在 linker script 里随便定义的,而是由 FUS 和选项字节共同约束的。以一个常见的 STM32WB55(1MB Flash)为例,大致区域划分如下:
| 区域 | 大致地址 | 内容 |
|---|---|---|
| FUS 安全区 | Flash 起始区域(如 0x08000000 起的一段) | FUS 固件本体 |
| 无线协议栈区 | FUS 之后 | BLE 协议栈或 802.15.4 协议栈 |
| 用户应用区 | 协议 |