尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32WB双核OTA升级实战:BLE通道与FUS协议栈更新全解析

STM32WB双核OTA升级实战:BLE通道与FUS协议栈更新全解析 在STM32F1/F4上做OTA,老哥们基本都熟: bootloader跳app,双bank倒腾一下,再加上Ymodem或者HTTP拉固件,流程跑通就完事。但真当你拿到STM32WB这颗双核无线MCU,想按老套路照搬,很快就发现世界不一样了。这颗料里面是Cortex-M4跑应用、Cortex-M0跑BLE/802.15.4协议栈,固件是两份,Flash要分区,M0上的协议栈还不能随便写,官方应用笔记AN5247就是为解决这一串问题而写的。这篇文章会把AN5247里涉及的双核OTA核心思路拆开讲,包括Flash分区怎么理解、Bootloader如何配合M4和M0、如何通过BLE通道把新固件无线刷进去,以及我实际操作中踩过的坑。适合手里已有一块NUCLEO-WB55RG、正准备给自己的产品加无线升级功能的工程师,也适合刚接触STM32WB、想把OTA原理搞清楚的朋友。我对这颗料的评价是: OTA架构本身不复杂,但如果你只用STM32F1那个思路做,基本做不出来,后面会详细说为什么。1. STM32WB做OTA,为什么和普通单片机不是一回事1.1 双核架构下的两份固件STM32WB55从芯片层面就不是一颗“普通单片机”,它里面实际住着两个Cortex-M核:Cortex-M4: 主核,跑你的业务逻辑、网络协议栈的API、传感器控制等用户代码。Cortex-M0: 网络核,专门跑BLE或者802.15.4协议栈,以及ST的安全固件FUS。两个核共享同一片Flash和RAM,但各自跑各自的代码。你在STM32CubeWB SDK里打开一个工程,会看到工程结构里明确分出了M4和M0两套代码。M4侧的应用程序通过IPC(Inter-Process Communication)机制,向M0发起无线事件请求,比如“打开广播”“发送数据”等。M0跑的是ST以二进制文件形式提供的协议栈固件,例如stm32wb5x_BLE_Stack_full_fw.bin,它不是一个你能随意改源码的东西。这就带来一个跟普通MCU OTA完全不一样的问题: 你的设备里其实有两份需要维护的固件。第一份是M4上的App,也就是产品业务代码;第二份是M0上的BLE协议栈。平时你OTA大概率只想升级自己的业务逻辑,这就是“只升级M4应用”。但遇到蓝牙协议栈本身有bug,或者你想用ST新发布的、支持更多特性的协议栈版本,就必须考虑“升级M0”这条路。我见过不少同行刚开始接触这颗料时,下意识地想把M4和M0的固件合并成一个大的bin文件,通过一次OTA全部写完。这个思路理论上可行,但非常不建议直接套用: 首先,协议栈固件有安全属性,乱写容易破坏保护机制;其次,升级协议栈意味着要中断当前正在进行的BLE连接,如果没做好提示和恢复逻辑,用户体验会很差。ST的官方思路也是把这两件事拆开,分别处理。1.2 M0协议栈的特殊地位M0上跑的协议栈,不是一段普普通通的可执行代码,它在ST的安全模型里是受保护的区域。这一点非常重要,因为它决定了你“不能像写App一样直接往协议栈区写数据”。在STM32WB上,和协议栈更新密切相关的是一个叫FUS(Firmware Upgrade Service)的组件。你可以把它理解成一个运行在M0里的“安全管家”,它负责协议栈固件的校验、解密、烧写,以及一些安全相关的状态管理。正常情况下,你用ST-Link配合STM32CubeProgrammer烧协议栈时,CubeProgrammer底层其实也是在调用FUS来完成写入,不是你直接朝Flash地址force write。但走OTA通道时,没有CubeProgrammer这个“中间人”帮你触发FUS了。设备上的M4应用需要自己承担这个角色,去和M0通信,把新协议栈镜像交给FUS,让FUS完成后续的校验和烧写。这段逻辑看上去不复杂,实际做起来却容易出问题,因为你要处理M0协议栈的启动、停止,FUS的接收接口,以及升级完成后整个系统怎么恢复。AN5247里很多篇幅其实就是在讲这块。另外,协议栈区域本身的布局也不是随便定的。协议栈固件会占用一段固定的Flash空间,并且版本要和FUS版本匹配。如果FUS版本太老,可能不认识新协议栈的镜像格式,直接拒绝升级。这个问题在普通STM32上根本不存在,属于WB平台特有的坑。1.3 AN5247整体方案的角色定位AN5247的全称大概是“STM32WB Series OTA and wireless firmware update”。它的核心价值,是把上面这些复杂的背景,整理成一套可以落地的方案。里面会讲清楚:内存布局怎么规划,各个区域分别放什么;Bootloader的职责边界,启动时做什么、升级时做什么;M4应用如何把通过BLE收到的数据写入下载区;固件校验和状态管理的推荐做法;协议栈升级如何借助FUS来完成。需要注意,AN5247本身是一篇应用笔记,不是一份可以复制粘贴的完整工程代码。它给的是设计思路和关键实现细节,配合STM32CubeWB SDK里的两个典型工程一起看:BLE_OTA_Bootloader(Bootloader工程)和BLE_OTA(App工程)。文档解释“为什么”,示例提供“怎么跑”,两者配合,你才能把这件事吃透。所以我建议你拿到这颗料之后,第一步不是急着写自己的业务代码,而是先把官方这套示例在NUCLEO-WB55RG板上原封不动跑通。跑通之后再改自己的工程,心里就有底了。很多人一上来就改链接脚本、改UUID,结果出了问题都不知道是Bootloader的锅还是App的锅,排查起来非常难受。2. 动手前必须想清楚的几个设计选型2.1 升级通道: BLE、HTTP、串口怎么选既然是无线MCU,很多人的第一反应是“升级通道当然走BLE”。对便携设备来说,BLE直连手机确实是最自然的方案,手机App本身可以充当传输工具,不需要额外架设服务器。但BLE的传输速率有限,你算一笔账就明白: 一个200KB的固件,如果用默认20字节有效负载传,差不多要传10000包,再加上连接间隔、应答开销,没有几十秒下不来。产品如果对升级时长有要求,就得考虑把MTU提上去,或者用更快的连接参数。另外一些做法是把HTTP或云端拉固件作为升级通道。比如设备本身通过Wi-Fi模块或者网关接入网络,固件可以先从服务器下载到网关或本地存储,再转给MCU。这种方式的好处是速度快、可以断点续传;坏处是STM32WB本身不带以太网和Wi-Fi,你往往要接外部模块,硬件成本和软件复杂度会上去。网上很多“嵌入式HTTP OTA”教程,讲的通常是带以太网或Wi-Fi的MCU方案,不能直接照搬到WB上。串口/USB则更适合工厂产线和开发调试。AN5247虽然标题是无线固件更新,但Bootloader和Flash分区这套机制,串口升级同样能复用。我个人的习惯是先把串口方式调通,再切到BLE,这样排查问题时少一个变量。2.2 Flash分区: 活动区、下载区、协议栈区怎么摆Flash分区是整个OTA的命根子,分区没规划好,后面做什么都别扭。以STM32WB55 1MB Flash为例,大致可以这样理解:Flash最高地址区域: 保留给FUS和安全机制使用,尽量不要碰;M0协议栈区: 存放当前运行的BLE或802.15.4协议栈;M4 Bootloader区: 负责上电启动和升级引导;M4 App活动区: 当前运行的用户业务固件;OTA下载区: 用来暂存通过无线通道收到的新固件。这几个区域的边界在哪里,并没有一个全局统一的标准答案。SDK里的示例工程会给出一种划分,但它依赖具体的芯片Flash大小和协议栈版本。你换一颗不同Flash容量的WB,分配比例就要重新调。我建议你把分区表抽象成一个头文件,比如叫partition.h,Bootloader工程和App工程都include同一份。里面定义每个区域的起始地址、大小,以及保护标志。这样改地址时只改一个文件,两个工程同步生效。后面升级到某个阶段发现地址不对,也只需要检查这一个地方。分区的实际大小怎么定?要看你的App需求。如果App本身很大,那App活动区要留足;如果协议栈是Full版本,占用Flash也很大,那协议栈区也不能省。SDK示例里的默认值可以帮你起步,但别当成万年不变的配置,一定要按自己产品实际镜像大小去调。2.3 “AB分区”与“搬运式升级”: 别被手机OTA带偏手机系统升级里很流行“AB分区”: 一个分区放当前系统,另一个分区放着升级包,升级时把新系统写到备用分区,重启后切换,失败就自动回滚。这种方案的优点是可靠,但代价是系统分区要准备两份空间,存储成本翻倍。STM32WB这种Flash资源有限的MCU上,不是所有产品都舍得让App分区翻倍。ST官方BLE_OTA示例采用的思路,看起来更像是“暂存-搬运”:新固件通过BLE先下载到一个单独的OTA下载区;整个镜像收完并校验通过后,设备复位;Bootloader在启动阶段检测到“有待升级镜像”,把下载区内容搬运到App活动区;搬运完成并再次校验,最后跳转启动新App。这种方案只需要“一个App区一个下载区”,空间占用比真正的AB分区低,但回滚能力也弱一些: 如果搬运到一半断电,App区可能不完整。不过Bootloader本身还在,你可以通过串口或再次OTA来恢复,不至于完全变砖。有人会问: 能不能不设下载区,直接把新固件写到App区?技术上可以,但非常危险。传输一旦中断,旧固件都没了。我建议量产产品至少保留“下载区活动区”的架构,并在Bootloader里增加“下载区校验失败则保持当前App运行”的逻辑。2.4 固件校验收尾: 签名、哈希和版本号固件写完下载区之后,Bootloader在搬运之前,必须做完整性校验。最简单的做法是对整个镜像算CRC32或者SHA-256,比对Bootloader里预置的期望值;更严谨的做法是在固件上做RSA或ECDSA签名,用公钥验证镜像确实来自官方。AN5247里也提到ST支持的安全功能,比如通过SFU(Secure Firmware Update)体系来实现加密和签名,但这通常要配合FUS和OTP区域的密钥来处理,复杂度会高一些。不管用哪种校验方式,我强烈建议固件头部里至少包含几个字段:魔数(Magic Number): 固定的一串字节,用来区分“这确实是一份OTA镜像”,而不是随便一块Flash数据;镜像长度: Bootloader需要知道总共要搬运多少数据;版本号: 用来判断“是否应该升级”;目标地址: 告诉Bootloader这份镜像应该写到哪个App区;校验字段: 哈希值或签名值,放在头部尾部均可。版本号这个字段尤其容易出幺蛾子。你不检查版本,用户可能拿着旧固件反复“升级”;你检查太严格,又可能因为版本号比较方式写得不对,导致新版本号反而比旧版本号低,设备永远拒绝升级。最好定一个像“主版本.次版本.修订号”这样的规范,并在打包脚本里统一维护。3. 基于AN5247的OTA完整实操3.1 硬件和软件准备我实践的硬件平台是NUCLEO-WB55RG开发板,这基本是STM32WB生态里最常见的评估板,也是官方BLE_OTA示例默认适配的板子。如果你用自绘板,原理上一样,但要特别注意天线阻抗匹配和晶振选择,这些会直接影响无线传输稳定性,进而影响OTA可靠性。软件方面需要准备几样东西:STM32CubeProgrammer: 用于烧录Bootloader、App、FUS和协议栈,也支持命令行操作;STM32CubeIDE或Keil: 编译工程,二选一即可;STM32CubeWB SDK: 从ST官网下载,里面包含了BLE_OTA和BLE_OTA_Bootloader示例工程;手机安装ST BLE Toolbox: ST官方出品的蓝牙调试工具,支持OTA功能,免去自己写App的麻烦。第一次跑流程,建议全部使用官方SDK自带的示例,不要用自己的业务代码。等你理解了整个链路,再逐步把示例代码裁剪、移植到产品工程里。3.2 编译烧录Bootloader与FUS在STM32CubeWB SDK的工程目录下,找到Projects\NUCLEO-WB55RG\Applications\BLE\BLE_OTA_Bootloader目录,打开工程编译,生成Bootloader的bin文件。同理,BLE_OTA目录生成App的bin文件。烧录时,我习惯先用STM32CubeProgrammer命令行操作,方便记录和复现:STM32_Programmer_CLI.exe -c portSWD modeUR STM32_Programmer_CLI.exe -c portSWD -d Bootloader.bin 0x08000000 STM32_Programmer_CLI.exe -c portSWD -d App.bin 0x08008000这里的0x08008000只是示例地址,实际取决于Bootloader工程里的Flash分区定义。你自己的板子如果Flash大小不同,这些地址一定要跟着变。这里有一个非常大的坑: 很多NUCLEO板出厂时Flash里已经带好了FUS和原厂BLE协议栈。如果你上来图省事,把整片Flash擦掉再烧,很可能会把FUS区域一并抹掉。抹掉之后,后面一切需要FUS参与的协议栈升级都会很痛苦。拿到新版子先别急着擦全片,用CubeProgrammer的读取功能,把整片Flash读出来备份一下,至少也要记录一下出厂分区布局。3.3 修改App工程的链接脚本要让你的自研App能配合Bootloader启动,必须把App工程的链接脚本改成从“Bootloader之后”的地址开始。以STM32CubeIDE为例,打开App工程的.ld链接脚本,找到类似这样的配置:FLASH (rx) : ORIGIN 0x08008000, LENGTH 224K RAM (xrw) : ORIGIN 0x20000000, LENGTH 252K把ORIGIN改成你规划的App区起始地址,LENGTH改成App区大小。如果你用Keil,则在“Options for Target - Target”页签里修改IROM1的起始地址和
返回列表