固件升级指南
OTA 概述
OTA (Over-the-Air,空中下载技术),是指通过网络连接向远程设备传输新固件并完成更新的过程。 尽管其名称中包含 "空中"(暗示无线连接),但在实际应用中,通过有线连接(例如以太网)接收的更新,通常也被称为 OTA 更新。
Zephyr 支持多种 OTA 升级方案,具体如下表所示:
示例 |
路径 |
|---|---|
|
|
|
|
|
|
|
其中,SMP Server 示例提供了一套完整的 OTA 升级方案,它结合 MCUboot 安全引导程序与 MCUmgr 设备管理框架,可实现安全可靠的固件升级。下文将基于该示例介绍 OTA 升级流程。
关键组件
实现一次安全可靠的 OTA 更新,主要依赖以下三个组件协同工作:
MCUboot:一个开源的、安全的引导加载程序。它在设备启动时负责验证应用固件的完整性与合法性,并管理 Flash 中固件分区的切换与升级流程。
SMP Server:内置于您应用程序中的 Simple Management Protocol 服务。它负责接收并处理来自外部管理工具 MCUmgr 的各种命令,包括固件上传、状态查询等。
MCUmgr:运行在您开发主机上的客户端管理工具。它可以通过 UART/BT/UDP 等多种方式,向设备端的 SMP Server 发送指令,从而触发和控制整个 OTA 流程。
模块层级图
实现安全 OTA 更新会涉及以下 Zephyr 子模块,层级关系如下图所示:
升级流程
基于上述 关键组件,典型的 OTA 升级流程如下图所示:
备注
示意图以 Swap_using_offset 和 Swap_using_move 升级方式举例。
固件存储布局
Flash 分区映射
Flash map 是设备闪存的“分区表”,它将设备上的 Flash 划分为多个独立区域(flash area),并为每个区域分配一个数字 ID。 MCUboot 和应用程序通过这些 ID 找到具体的物理地址和大小,进行读写、擦除、升级等操作。
通常,每个固件会分配两个槽位(Slots):主槽(Primary slot) 和 次槽(Secondary slot)。 以下是两个固件场景下的 Flash 分区配置示例:
&flash0 {
reg = <0x103FF000 DT_SIZE_M(4)>;
status = "okay";
partitions {
compatible = "fixed-partitions";
#address-cells = <1>;
#size-cells = <1>;
/* flash area ID 0 */
boot_partition: partition@0 {
label = "bootloader";
reg = <0x00000000 0x00014000>;
read-only;
};
/* 为 AP(固件 0)分配 主槽(slot0_partition)和 次槽(slot1_partition) */
/* flash area ID 1 */
slot0_partition: partition@14000 {
label = "image-0";
reg = <0x00014000 0x00080000>;
};
/* flash area ID 2 */
slot1_partition: partition@94000 {
label = "image-1";
reg = <0x00094000 0x00080000>;
};
/* 为 NP(固件 1) 分配 主槽(slot2_partition)和 次槽(slot3_partition) */
/* flash area ID 3 */
slot2_partition: partition@114000 {
label = "image-2";
reg = <0x00114000 0x00080000>;
};
/* flash area ID 4 */
slot3_partition: partition@194000 {
label = "image-3";
reg = <0x00194000 0x00080000>;
};
};
};
固件格式
在 Zephyr 的 OTA 场景中,由 MCUboot 引导的应用固件必须符合 MCUboot 定义的固件格式,包含: Firmware Header, Firmware Body, TLV Area (Optional) 和 Firmware Trailer。
示意图如下所示:
备注
图示为应用固件的格式要求,并非 MCUboot 自身的固件格式。
固件头部( Header )
Zephyr 编译系统会在固件起始位置预留 0x200 字节作为固件头部空间,即 ih_hdr_size = 0x200。
#define IMAGE_MAGIC 0x96f3b83d
#define IMAGE_HEADER_SIZE 32
STRUCT_PACKED image_version {
uint8_t iv_major;
uint8_t iv_minor;
uint16_t iv_revision;
uint32_t iv_build_num;
};
/** Image header. All fields are in little endian byte order. */
STRUCT_PACKED image_header {
uint32_t ih_magic;
uint32_t ih_load_addr;
uint16_t ih_hdr_size; /* Size of image header (bytes). */
uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */
uint32_t ih_img_size; /* Does not include header. */
uint32_t ih_flags; /* IMAGE_F_[...]. */
struct image_version ih_ver;
uint32_t _pad1;
};
TLV 区(类型-长度-值)
TLV 区域位于固件主体之后,属于可选部分,通常用于存储哈希值、签名、安全计数器等信息。
若存在受保护 TLV 区域,则必须包含 IMAGE_TLV_PROT_INFO_MAGIC 标识头,且受保护 TLV 区域会参与哈希计算;若不存在,则哈希仅覆盖 “固件头 + 固件正文”。
为保证回滚保护有效, IMAGE_TLV_SEC_CNT, 0x50 需写入受保护 TLV 区域。使用 imgtool 的 --security-counter 选项会将其放入受保护 TLV 区。
#define IMAGE_TLV_INFO_MAGIC 0x6907
#define IMAGE_TLV_PROT_INFO_MAGIC 0x6908
/** Image TLV header. All fields in little endian. */
STRUCT_PACKED image_tlv_info {
uint16_t it_magic;
uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */
};
/** Image trailer TLV format. All fields in little endian. */
STRUCT_PACKED image_tlv {
uint16_t it_type; /* IMAGE_TLV_[...]. */
uint16_t it_len; /* Data length (not including TLV header). */
};
#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */
#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */
#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */
#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */
#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */
#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */
#define IMAGE_TLV_ED25519 0x24 /* ED25519 of hash output */
#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */
#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW-128 or 256 */
#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-P256 */
#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */
#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */
固件尾部( Trailer )
该区域用于记录固件交换状态等元数据,位于槽位末尾,不能用于存放固件正文。
固件 Trailer 结构如下(以字节为单位):
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ ~
~ Swap status (BOOT_MAX_IMG_SECTORS * min-write-size * s) ~
~ ~
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Encryption key 0 (16 octets) [*] |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from Encryption key 0) [*] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Encryption key 1 (16 octets) [*] |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from Encryption key 1) [*] |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Swap size (4 octets) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 4 octets from Swap size) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Swap info | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Copy done | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Image OK | 0xff padding (BOOT_MAX_ALIGN minus 1 octet) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| 0xff padding as needed |
| (BOOT_MAX_ALIGN minus 16 octets from MAGIC) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAGIC (16 octets) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- 参数说明
- BOOT_MAX_IMG_SECTORS:
默认值为
slot0_partition所包含的扇区数量。- min-write-size:
Flash 最小写入粒度。
- s:
s = 3(Swap_using_move),s= 2(Swap_using_offset)
- Swap status:
记录每个扇区的 交换状态,用于掉电后恢复升级流程。
- Encryption keys:
可选加密密钥,仅在开启固件加密时存在。
- Swap size:
本次交换需要搬移的总数据大小,包含固件正文与 TLV 区域。
- Swap info:
有效长度为 1 字节,按 BOOT_MAX_ALIGN 对齐;低 4 位为 交换类型,高 4 位为固件编号。
- Copy done:
有效长度为 1 字节,按 BOOT_MAX_ALIGN 对齐;表示复制阶段完成状态:0x01=Set,0xFF=Unset。
- Image OK:
有效长度为 1 字节,按 BOOT_MAX_ALIGN 对齐;表示新固件是否已确认,未确认将于下次启动回退。0x01=Set,0xFF=Unset。
- MAGIC:
16 字节,用于标记尾部区域是否有效。
BOOT_MAX_ALIGN=8时为固定 16 字节模式:const union boot_img_magic_t boot_img_magic = { .val = { 0x77, 0xc2, 0x95, 0xf3, 0x60, 0xd2, 0xef, 0x7f, 0x35, 0x52, 0x50, 0x0f, 0x2c, 0xb6, 0x79, 0x80 } };
否则,前 2 字节为对齐值,后 14 字节固定模式:
const union boot_img_magic_t boot_img_magic = { .align = BOOT_MAX_ALIGN, .magic = { 0x2d, 0xe1, 0x5d, 0x29, 0x41, 0x0b, 0x8d, 0x77, 0x67, 0x9c, 0x11, 0x0f, 0x1f, 0x8a } };
备注
该区域位于固件槽位的尾部,而不是紧跟固件正文。因此分配的槽位需要预留 Trailer 空间,并按扇区对齐。
OTA 升级机制
MCUboot 支持多种固件升级方式,如下表所示。详细说明可参考 MCUboot 官网介绍 。
升级方式 |
特点 |
Upgrade only |
将新固件直接覆盖旧固件,不进行交换,不可回滚 |
Swap using scratch |
通过 scratch 分区做中转,完成新旧固件交换,可回滚 |
Swap using offset |
依靠分区内的偏移布局进行交换,无需 scratch 分区,可回滚 |
Swap using move |
通过移动数据完成交换,无需 scratch 分区,可回滚 |
Direct XIP |
直接原地执行,无需交换 |
RAM load |
将固件完整加载到 RAM 后执行 |
Firmware loader |
通过专用加载器进行升级 |
当前支持以下两种升级方式:
Swap_using_move
Swap_using_move 是一种无需临时存储区(scratch)的固件交换算法,适用于主 / 次双槽 OTA 升级场景。 其核心是通过将主槽扇区整体后移一位,再交替复制主槽与次槽内容,完成固件交换。
可通过配置宏 CONFIG_BOOT_SWAP_USING_MOVE 启用此升级方式。采用 --sysbuild 时需在应用程序 sysbuild.conf 中配置 SB_CONFIG_MCUBOOT_MODE_SWAP_USING_MOVE=y
工作原理: 1. 扇区下移:先将主槽所有扇区向下移动一个扇区位置。 2. 交替复制:从第 1 个扇区开始,依次执行: 3. 将次槽第 N 个扇区复制到主槽第 N 个扇区; 4. 重复直至所有扇区交换完成。
从图示可知,每个扇区的交换过程包含 3 个状态:
主槽[N] → 主槽[N+1]已完成
次槽[N] → 主槽[N] 已完成
主槽[N+1] → 次槽[N] 已完成
Swap_using_offset
Swap_using_offset 是一种无需临时存储区(scratch)的固件交换算法,是 Swap_using_move 的增强版。 其核心是利用次槽中的一个扇区作为临时缓冲,交替复制主、次槽内容,完成固件交换。
可通过配置宏 CONFIG_BOOT_SWAP_USING_OFFSET 启用此升级方式;采用 --sysbuild 时需在应用程序 sysbuild.conf 中配置 SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET=y 。
工作原理: 1. 固件放置:待升级的新固件必须从次槽的第二个扇区开始存放。 2. 交替复制:从第 1 个扇区开始,依次执行: 3. 将主槽第 N 个扇区复制到次槽第 N 个扇区; 4. 将次槽第 N+1 个扇区复制到主槽第 N 个扇区; 5. 重复直至所有扇区交换完成。
从图示可知,每个扇区的交换过程包含 2 个状态:
主槽[N] → 次槽[N] 已完成
次槽[N+1] → 主槽[N] 已完成
交换管理机制
交换类型
在正常升级流程中(未异常掉电 / 中断),MCUboot 会根据固件 trailer 中的标志位决定执行的交换类型(swap type)。 受 Flash 硬件特性限制,trailer 的设计并不直观,直接读取原始字节难以直接解析设备状态。 因此,MCUboot 将“各种可能的 trailer 组合状态”映射为“交换类型”,并按优先级顺序判定。
State I 场景 I(仅 Swap_using_offset 模式):在执行 REVERT 前的风险窗口,记录 magic 和 copy-done 到次槽,下次启动会继续执行 REVERT。
State I (swap using offset only)
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | Unset |
copy-done | Any | Set |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_REVERT |
-------------------------------------------------'
State II 场景:次槽固件有效但未确认,执行“测试交换”。系统会将次槽固件交换到主槽并启动。 新固件需在应用中设置 image-ok 完成确认;若未确认,下一次启动将自动回退(REVERT)。
State II
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | Unset |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_TEST |
-------------------------------------------------'
State III 场景:次槽固件已预先标记 image-ok,执行“永久交换”,升级完成后不会回退。
State III
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Good |
image-ok | Any | 0x01 |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_PERM |
-------------------------------------------------'
State IV 场景:主槽复制已完成(copy-done=Set),但新固件未确认(image-ok=Unset),因此本次启动执行回退,恢复为旧固件。
State IV
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Good | Any |
image-ok | 0xff | Any |
copy-done | 0x01 | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_REVERT |
-------------------------------------------------'
若以上状态 State I~IV 均不匹配,MCUboot 不会执行固件交换,而是进入 State V,选择三种交换类型之一。
State V
| primary slot | secondary slot |
-----------------+--------------+----------------|
magic | Any | Any |
image-ok | Any | Any |
copy-done | Any | Any |
-----------------+--------------+----------------'
result: BOOT_SWAP_TYPE_NONE, |
BOOT_SWAP_TYPE_FAIL, or |
BOOT_SWAP_TYPE_PANIC |
-------------------------------------------------'
备注
State 编号越小优先级越高。一旦匹配某一状态,立即确定对应的交换类型并停止继续匹配。
交换类型:
BOOT_SWAP_TYPE_NONE:无升级任务,直接启动主槽固件。BOOT_SWAP_TYPE_TEST:测试运行;若未通过设置 image-ok=0x01 确认,下次启动将触发回退。BOOT_SWAP_TYPE_PERM:永久交换,不需要设置 image-ok=0x01 确认。BOOT_SWAP_TYPE_REVERT:上次 TEST 未确认,回退至原有旧固件。BOOT_SWAP_TYPE_FAIL:目标固件无效(如校验失败)。BOOT_SWAP_TYPE_PANIC:交换过程出现不可恢复错误,启动终止。
交换状态
交换状态(Swap status)用于掉电恢复,记录在主槽的 Trailer 。 若固件交换过程中发生异常重启,MCUboot 可根据已记录的交换状态恢复现场,继续完成未结束的交换流程。 该区域由一系列单字节状态记录组成,每条记录独立写入,并按照设备的最小写入粒度进行对齐填充。 典型交换流程如下:
初始化: 同时擦除主、次槽 Trailer;然后在主槽 Trailer 写入 swap_info、swap_size 及 MAGIC 标记。
逐扇区交换:仅对固件实际占用的扇区(包含 TLV 区域)进行交换,并根据所选算法记录每个扇区的状态。
收尾:交换完成后,在主槽 Trailer 中写入 copy done,标记交换流程结束。
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec127,state 0 |sec127,state 1 |sec127,state 2 |sec126,state 0 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec126,state 1 |sec126,state 2 |sec125,state 0 |sec125,state 1 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|sec125,state 2 | |
+-+-+-+-+-+-+-+-+ +
~ ~
~ [Records for indices 124 through 1 ~
~ ~
~ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
~ |sec000,state 0 |sec000,state 1 |sec000,state 2 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
备注
图示假设 min-write-size = 1,每个“扇区索引”对应 3 个状态记录,适用于 Swap_using_move 模式;而在 Swap_using_offset 模式下,每个扇区仅有 2 个状态记录,所需总空间更小。
防回滚功能
为了防止设备被降级到有已知漏洞的固件版本,MCUboot 支持防回滚策略,分为 软件防回滚 与 硬件防回滚。 其中 软件防回滚 又分为基于版本号(version) 和基于安全计数器(security counter) 两种实现方式。
软件防回滚(基于版本号)
基于版本号的软件防回滚,只有 !BOOT_DIRECT_XIP 升级方式下存在。该方式为纯软件实现,无法抵御物理攻击(如 Flash 擦除、重编程)。
原理:
MCUboot 每次启动对固件版本号进行比较。
若新固件版本号低于当前运行固件版本,则拒绝启动。
若新固件版本号大于或等于当前版本,则允许启动。
配置:
CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y
固件签名时嵌入版本号:
# 示例:在应用程序 prj.conf 或板卡特定配置中设置 CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION="0.0.2"
软件防回滚(基于安全计数器)
基于 security counter 的软件防回滚,只有 (BOOT_SWAP_USING_MOVE || BOOT_SWAP_USING_SCRATCH || BOOT_SWAP_USING_OFFSET) 方式下存在。
该方式为纯软件实现,无法抵御物理攻击(如 Flash 擦除、重编程)。
原理:
MCUboot 每次启动对 security counter 进行比较。
若新固件的 security counter 小于当前运行固件的 security counter,则拒绝启动。
若新固件的 security counter 大于或等于当前运行固件的 security counter,则允许启动。
配置:
CONFIG_MCUBOOT_DOWNGRADE_PREVENTION=y # security counter 选项开启依赖于该配置 CONFIG_MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER=y
固件签名时嵌入 security counter :
# 示例:在应用程序 prj.conf 或板卡特定配置中设置 CONFIG_MCUBOOT_IMGTOOL_SIGN_SECURITY_COUNTER=2
备注
当前 NP 固件使用的 security counter 与 AP 固件相同。
硬件防回滚(基于 OTP)
原理:
MCUboot 将当前已接受的 security counter 保存到硬件计数器(OTP)中。
如果新固件的 security counter 小于 OPT 记录值 ,则拒绝启动。
如果新固件的 security counter 大于或等于 OPT 记录值,则允许启动。
配置:
CONFIG_MCUBOOT_HW_DOWNGRADE_PREVENTION=y
固件签名时嵌入 security counter :
# 示例:在应用程序 prj.conf 或板卡特定配置中设置 CONFIG_MCUBOOT_IMGTOOL_SIGN_SECURITY_COUNTER=2
备注
当前 NP 固件使用的 security counter 与 AP 固件相同。
固件 NP 与 AP 的硬件计数器均有上限,请仅在必要时更新,达到上限后可能造成设备启动失败。
快速上手
环境准备与配置
1. PC 主机安装 MCUmgr 命令行工具
您可以从 Apache Mynewt 官网 下载对应平台的 newtmgr 工具包:
Windows 64-bit: apache-mynewt-newtmgr-bin-windows-1.14.0.tgz
Linux 64-bit: apache-mynewt-newtmgr-bin-linux-1.14.0.tgz
将解压后的 newtmgr 工具包路径添加至对应平台的环境变量中,
命令行执行 newtmgr --help,若显示全局选项即配置成功。
2. Zephyr 设备端的工程配置
示例路径: zephyr/samples/subsys/mgmt/mcumgr/smp_svr。
Flash 分区
smp_svr 示例采用的 Flash 分区布局,可参考 Flash 分区映射 配置。
基础 MCUboot 与 SMP 配置
您需要确保 smp_svr 示例的配置文件(如 prj.conf 或板卡特定的配置文件)中包含以下关键配置:
# 启用 MCUboot 引导程序
CONFIG_BOOTLOADER_MCUBOOT=y
CONFIG_UPDATEABLE_IMAGE_NUMBER=2 # AP(固件 0) 和 NP(固件 1)
CONFIG_MCUMGR_GRP_IMG_ALLOW_CONFIRM_NON_ACTIVE_IMAGE_ANY=y # 允许对非运行固件进行确认
# 使能 MCUmgr 必要的命令组
CONFIG_MCUMGR_GRP_IMG=y # 固件管理命令组
CONFIG_MCUMGR_GRP_OS=y # 基本 OS 命令组
# 启用 MCUmgr 和 SMP 服务
CONFIG_MCUMGR=y
CONFIG_MCUMGR_SMP_UART=y # 使用 UART 作为传输层
# 配置 UART 缓冲区与 MTU(最大传输单元)
CONFIG_MCUMGR_TRANSPORT_NETBUF_SIZE=1024
CONFIG_UART_MCUMGR_RX_BUF_SIZE=256 # 需要大于等于 MCUmgr 工具设置的 MTU
您需要在应用程序的 sysbuild 配置文件 sysbuild.conf 中 指定升级方式,否则顶层会按默认逻辑选择 swap_using_move 方式。
# 配置升级方式
SB_CONFIG_MCUBOOT_MODE_SWAP_USING_OFFSET=y
UART 传输层配置
您需要在设备树(overlay 文件)中指定使用的 UART 引脚,并确保该 UART 端口已正确配置并启用。
/ {
chosen {
zephyr,uart-mcumgr = &uart2;
};
};
&uart2 {
pinctrl-0 = <&uart2_default>;
pinctrl-names = "default";
current-speed = <115200>;
status = "okay";
};
编译命令
./nuwa.py build -d rtl8721f_evb//mcuboot -a zephyr/samples/subsys/mgmt/mcumgr/smp_svr --sysbuild -p
或者
west build -b rtl8721f_evb//mcuboot zephyr/samples/subsys/mgmt/mcumgr/smp_svr --sysbuild -p
设备上电/复位后,打开串口终端,确认示例已启动并打印初始化日志:
*** Booting Zephyr OS build f50378385a85 ***
<inf> smp_sample: build time: Jan 7 2026 11:52:35
升级操作流程
假设设备已运行包含 smp_svr 示例的固件,并且选定的 UART 端口已连接到 PC 。
建立 MCUmgr 连接
配置连接
命令格式: newtmgr conn add <连接名> type=<类型> connstring="<key=value[,key=value...]>"
示例:
$ newtmgr conn add myConn type=serial connstring=\"dev=<COM3>,baud=115200,mtu=256\"
Connection profile myConn successfully added
备注
波特率配置需要与设备树中的设定一致。MTU 配置需要小于等于应用配置中的
CONFIG_UART_MCUMGR_RX_BUF_SIZE。newtmgr conn 配置其他传输方式,请参考 官网说明 。
查看固件列表
命令格式: newtmgr image list -c <connection_profile>
示例:
$ newtmgr image list -c myConn Images: image=0 slot=0 version: 0.0.0 bootable: true flags: active confirmed hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257 image=1 slot=0 version: 0.0.0 bootable: true flags: active confirmed hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
- 参数说明
- image:
多个固件时的固件编号 。
- slot:
槽位(0=主槽,1=次槽)
- version/hash:
版本号与哈希
- bootable:
该槽位是否存在有效固件(固件头部正确且可校验)
- flags:
active表示是正在运行的固件槽位pending表示在下次 reset 时 MCUboot 会测试该固件槽位confirmed表示该固件槽位已被确认,不会回滚
备注
编译新固件时,可修改版本号
CONFIG_MCUBOOT_IMGTOOL_SIGN_VERSION,方便版本管理和查询。当前 NP 固件总是使用与 AP 固件相同的版本号。
上传新固件
使用 image upload 命令将新编译好的、已签名的固件发送到设备。
命令格式: newtmgr image upload <image-file> -c <conn_profile> [-n <image_int>]
示例:
$ newtmgr -c myConn image upload -n 0 /path/to/your/image_0.bin 77.55 KiB / 77.55 KiB [=======================================================================] 100.00% 4.38 KiB/s 17s Done $ newtmgr -c myConn image upload -n 1 /path/to/your/image_1.bin 228.48 KiB / 228.48 KiB [=====================================================================] 100.00% 4.44 KiB/s 51s Done
- 参数说明
- -n:
指定固件编号,缺省时表示固件 0 。
标记为待测试
传输成功后,新固件将存放在对应固件的次槽分区中。通过以下命令标记新固件为 test ,MCUboot 会在下次启动时尝试运行它。
命令格式: newtmgr -c <conn_profile> image test <hex-image-hash>
示例:
$ newtmgr -c myConn image test 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
Images:
image=0 slot=0
version: 0.0.0
bootable: true
flags: active confirmed
hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257
image=0 slot=1
version: 0.0.1
bootable: true
flags: pending
hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
image=1 slot=0
version: 0.0.0
bootable: true
flags: active confirmed
hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
image=1 slot=1
version: 0.0.1
bootable: true
flags:
hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Split status: N/A (0)
$ newtmgr -c myConn image test 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Images:
image=0 slot=0
version: 0.0.0
bootable: true
flags: active confirmed
hash: d0d1fe6e6f39a65d2442009e6a620736ea88e437c7bd297abdd9e9eaf1e3e257
image=0 slot=1
version: 0.0.1
bootable: true
flags: pending
hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812
image=1 slot=0
version: 0.0.0
bootable: true
flags: active confirmed
hash: 8ea9150f95da9c3f97134984b731928ae93e14752c54fe53b629d005ab94a948
image=1 slot=1
version: 0.0.1
bootable: true
flags: pending
hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
Split status: N/A (0)
备注
被指定为测试的固件槽位信息中可以看到:
flags: pending。
重启设备
可以通过 MCUmgr 工具发送重启命令,或手动复位设备。
触发 reset 后,MCUboot 检测到次槽被标记为 test,根据配置的 OTA 升级机制, 决定是否执行主/次槽交换;采用 swap_using_xxx 策略时会在应用启动前完成交换。
命令格式: newtmgr reset -c <conn_profile>
示例:
$ newtmgr -c myConn reset
Done
确认新固件
设备重启并成功运行新固件后,必须执行确认操作,Zephyr 提供了两种确认方式:
应用程序自行确认
设备在启动并验证新固件成功后,由应用程序主动调用 MCUboot 提供的确认接口
boot_write_img_confirmed(),将当前固件标记为confirmed。通过 MCUmgr 工具下发确认命令
命令格式:
newtmgr image confirm [hex-image-hash] -c <conn_profile>示例:
$ newtmgr -c myConn image confirm 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe Images: image=0 slot=0 version: 0.0.1 bootable: true flags: active confirmed hash: 99bb5f0867744a238693f522b31be146a74fca2ad587d878feebef70b7d1e812 image=1 slot=0 version: 0.0.1 bootable: true flags: active confirmed hash: 137d5f84dc5c302f2d15d22ca7fa76c48e030a5d5c6f73b6ae89598143af92fe
备注
被确认的固件槽位信息中可以看到:
flags: confirmed。当前 smp_svr 示例采用第二种确认方式。