固件升级编程指南
概述
OTA (空中下载技术)升级机制允许设备在固件正常运行时,通过 Wi-Fi、SPI、UART 以及文件系统等方式接收新的固件并将其保存到 Flash 对应的固件区,完成后重启即可运行新固件,用以问题修复,新功能发布和性能优化等场景。
使用 OTA 之前请先参考 Flash 布局 章节了解更多 Flash 分区相关细节。
固件分区
在 Flash 布局 中 BOOT 和 APP 固件均有两个固件分区( OTA1 和 OTA2 )用于冗余存储和版本控制。
用户需要根据实际的固件大小以及合理的预留空间来设置这些分区的大小。
备注
BOOT 固件的 OTA 功能默认关闭,如需使用,请参考 BOOT OTA
固件标签和版本号
OTA1/OTA2 固件的启动选择主要依赖于固件头中的固件标签和版本号。如下图所示,固件头包含 8 字节的固件标签、2 字节的主版本号和 2 字节的次版本号。
固件标签和版本号的详细说明如下表所示:
项目 |
描述 |
OTA 选择流程 |
|---|---|---|
固件标签 |
|
检查固件标签的有效性 |
版本号 |
|
比较版本号的大小 |
系统每次启动时, Bootloader 会检查固件标签的有效性并比较版本号,以决定从 OTA1 或 OTA2 分区启动。固件标签和版本号的一致性校验确保了启动过程的可靠性,具体检查流程如下:
检查 OTA1 和 OTA2 分区的固件标签是否有效:
若仅一个固件标签有效,则从该有效固件启动;
若两个固件标签均无效,则启动失败。
比较 OTA1 和 OTA2 分区的版本号:
若两个固件的版本号均有效且不相同,则从版本号较高的固件启动;
若两个固件的版本号均有效但相同,则默认从 OTA1 分区启动。
固件切换策略
基于 固件标签和版本号 中的启动流程, OTA 功能支持以下两种固件切换策略,用于在下次启动时切换到新固件:
备注
有关切换策略的配置,请参考 配置工程(menuconfig) ,进入 进行选择。
在新固件中设置更高的版本号, OTA 升级完成后,设备将在下次启动时自动从新固件启动。 此策略适用于需要版本控制的场景。
配置步骤:
在
menuconfig中选择切换策略:CONFIG OTA OPTION ---> [*] OTA Image Switch Strategy Select Strategy (Version Number Based) ---> (X) Version Number Based ( ) Valid Header Based
在配置文件
SDK/component/soc/amebaxxx/project/manifest.json5中修改新固件的版本号:BOOT 固件版本号配置(有效范围:0 ~ 32767)
"image1": { "img_id": 0, "img_ver_major": 1, "img_ver_minor": 1, ... },
APP 固件版本号配置(有效范围:0 ~ 65535)
"image2": { "img_id": 1, "img_ver_major": 1, "img_ver_minor": 1, ... },
小技巧
每次 OTA 升级时,应递增版本号,确保新固件的版本号高于当前运行的固件。
该策略无需修改新固件的版本号,而是在 OTA 升级完成后破坏旧固件的固件标签,使其变为无效固件,从而强制 Bootloader 使用新升级的固件。 此策略会主动使旧固件失效,适用于长期运行测试或无需版本控制的场景。
配置步骤:
在 menuconfig 中选择切换策略:
CONFIG OTA OPTION --->
[*] OTA Image Switch Strategy
Select Strategy (Version Number Based) --->
( ) Version Number Based
(X) Valid Header Based
警告
此策略会破坏旧固件的头部信息,导致其无法启动。若新固件存在问题,需重新烧录固件。
特性 |
基于版本号切换 |
基于有效标签切换 |
|---|---|---|
版本号要求 |
需递增版本号 |
无需修改版本号 |
配置复杂度 |
中等(需维护版本号) |
简单(无需额外配置) |
旧固件状态 |
保留完整 |
标签损坏 |
BOOT OTA 支持 |
所有芯片均支持 |
部分支持( RTL8726E/RTL8713E/RTL8720E/RTL8710E 不支持) |
APP OTA 支持 |
所有芯片均支持 |
所有芯片均支持 |
防回滚功能
防回滚功能用于防止回滚到主版本号低于 OTP 中设置的版本号的固件版本。当启用防回滚功能时,固件头中的主版本号不能小于 OTP 中设置的防回滚版本号, 否则固件将被视为无效。
通常,如果此次 OTA 更新解决了此前的版本的安全性问题,用户可以在更新完成之后将防回滚版本号设置为此次更新的版本号,以防止回滚到低于此版本的固件版本。
防回滚流程如下所示:
分别比较从 OTA1 和 OTA2 固件头获取的主版本号与 OTP 中的防回滚版本号的大小。
如果固件的主版本号小于防回滚版本号,该固件将被视为无效。
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中。
OTP 的物理地址范围为 0x36E ~ 0x36F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 16 位,所以只支持 16 个防回滚版本号(版本号范围:1 ~ 16)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,36E,2
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,36E,2,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFC或FFCF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0或0FFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
OTP 配置说明:
防回滚版本号存储在芯片的 OTP 区域中:
OTP 的物理地址范围为 0x378 ~ 0x37F。
OTP 中的值的比特 0 的数量表示防回滚版本号。OTP 默认值为全 F ,则表示默认的防回滚版本号为 0。
由于 OTP 物理地址只能从 1 写 0 ,地址范围最多 64 位,所以只支持 64 个防回滚版本号(版本号范围:1 ~ 64)。
启用防回滚:
用户可以通过以下步骤启用防回滚功能:
更改 OTP 中的防回滚版本号。
通过以下命令读取当前的防回滚版本号:
AT+OTP=RRAW,378,8
通过以下命令设置防回滚版本号:
AT+OTP=WRAW,378,8,<Value_OTP>
其中
<Value_OTP>为需要设置的版本号对应的 OTP 值。版本号计算示例:例 1:写入
FFFCFFFFFFFFFFFF或FFCFFFFFFFFFFFFF,两个值中各有 2 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 2 ,表示版本号小于(2 << 16)的固件都被禁止启动例 2:写入
FFF0FFFFFFFFFFFF或0FFFFFFFFFFFFFFF,两个值中各有 4 个比特被置为 0 ,则 OTP 中的防回滚主版本号为 4 ,表示版本号小于(4 << 16)的固件都被禁止启动
启用防回滚功能。
通过以下命令读出当前值:
AT+OTP=RRAW,368,1
将读出来的值比特 6 置 0 后再写入,比如读出来为 FF ,则写入:
AT+OTP=WRAW,368,1,BF
默认情况下, BOOT 固件和 APP 固件均使用同一个防回滚版本号,即从 OTP 中读取。
如果 BOOT 固件和 APP 固件需要使用不相同的防回滚版本号,修改 bootloader\boot_ota_km4.c 或 bootloader\boot_ota_hp.c 中
的函数 BOOT_OTA_GetCertRollbackVer() ,为 APP 固件自定义一个其他获取防回滚版本号的方式。
重要
OTP 是一次性可编程存储器,一旦将某位从 1 写为 0 ,无法再恢复
防回滚功能启用后无法禁用,请谨慎操作
建议在量产阶段启用防回滚功能但不设置具体版本号,待 OTA 升级完成后再写入版本号
在应用中修改防回滚版本号,请参考 OTP API 参考 中接口的用法,在代码中实现
BOOT OTA
BOOT OTA 启用步骤
默认情况下, BOOT OTA 功能处于未启用状态。用户可通过以下步骤启用 BOOT OTA:
参考 配置工程(menuconfig) ,进入
CONFIG OTA OPTION配置,启用Enable Bootloader OTA:此步骤会在编译过程中将 BOOT 固件集成到
ota_all.bin中,供 OTA 升级使用。CONFIG OTA OPTION ---> [*] Enable Bootloader OTA
在 Flash 布局 中规划并修改 BOOT OTA2 的分区范围,请确保地址 4K 对齐。
将 BOOT OTA2 分区的起始地址写入 OTP :
AT+OTP=WRAW,36C,2,<Value_OTP>
BOOT OTA2 地址写入 OTP 的计算公式如下:
Value_OTP = (IMG_BOOT_OTA2 >> 12 & 0xFF) << 8 | (IMG_BOOT_OTA2 >> 20 & 0xFF)
举例说明:当
IMG_BOOT_OTA2为0x08234000时,Value_OTP为3482。写入 OTP 的命令为:AT+OTP=WRAW,36C,2,3482
重要
BOOT OTA2 的地址必须在写入 OTP 前确认无误,一旦写入将永久生效且无法修改。
若 OTP 中 BOOT OTA2 地址为
0xFFFF(未配置状态),系统行为如下:OTA 升级时将跳过 BOOT 固件更新;
设备始终从 BOOT OTA1 启动。
强烈建议在批量生产前配置并启用 BOOT OTA2。
BOOT OTA 选择流程
芯片 ROM 中的引导程序根据 BOOT 固件头中的标签和版本号选择启动分区,流程如下图所示。
APP OTA
APP OTA 启用步骤
APP 固件支持 OTA 更新,并能够从 OTA1 或 OTA2 启动。默认启用 APP OTA。
APP OTA 选择流程
BOOT 固件运行时会根据 APP 固件的版本号和标签选择启动分区,选择流程如下图所示:
OTA 压缩方案
当启用 OTA 固件压缩功能时, APP OTA1 分区保持不变,而 APP OTA2 分区的固件将被压缩,从而减小固件体积,有效节省 Flash 存储空间。
压缩算法采用 LZMA(Lempel-Ziv-Markov 链算法),这是一种无损数据压缩算法,以其高压缩比和相对较低的解压内存需求著称,广泛应用于 .7z 格式文件压缩及 7-Zip 软件中。
压缩效果可通过压缩率衡量。压缩率的计算公式如下:
压缩率(%) = 压缩后固件大小 / 原始固件大小 × 100
压缩率受固件内容影响:原始固件内容越复杂,压缩率通常越高(一般为 50% ~ 70%)。
启用 OTA 压缩功能后的启动流程具有以下特点:
版本号和固件标签的判断与普通流程一致。
仅 APP 固件支持压缩方案。
压缩固件在 OTA 过程中始终被下载到 OTA2 区域。
若判断需从 OTA2 的压缩固件启动, BOOT 流程将进行解压缩并覆盖 OTA1 区域。
支持 OTA 过程及启动时解压过程的掉电保护。
压缩固件仅需在下载完成后的首次启动时解压一次,后续启动将不再解压。
以下流程图展示了压缩 OTA 的工作示例:
设备从 OTA1 启动并运行 OTA 应用。
压缩固件被下载到 OTA2。
下一次启动时,系统选择压缩固件,将其解压到 OTA1 ,然后从 OTA1 启动。
用户可通过以下步骤启用并验证 OTA 压缩功能:
参考 配置工程(menuconfig) ,进入
CONFIG OTA OPTION配置,启用Enable Compress APP image:CONFIG OTA OPTION ---> [*] Enable Compress APP image
编译项目时, APP 固件被压缩后生成压缩固件
{SDK}\build_<soc>\build\project_xx\image\xxx_app_compress.bin,同时生成压缩后的ota_all.bin供 OTA 使用。烧录
{SDK}\build_<soc>\xxx_bool_all.bin到 BOOT OTA1 区域,并烧录压缩固件{SDK}\build_<soc>\build\project_xx\image\xxx_app_compress.bin到 APP OTA2 区域,以验证压缩固件的解压过程。
重要
首次启用 OTA 压缩功能需更新 BOOT ,需重新烧录 BOOT 固件。
建议在批量生产前启用 OTA 压缩功能。
OTA 固件
OTA 固件格式
ota_all.bin 的文件格式如下图所示,其中 OTA 固件头部分为主头部和子头部,主头部包含版本号和子头部的数量,子头部包含升级固件的信息。
每增加一个升级固件,便增加对应的一个子头部。
项目 |
地址偏移 |
大小 |
描述 |
|---|---|---|---|
版本号 |
0x00 |
4 字节 |
OTA 固件主头部中的版本号,默认值是 0xFFFFFFFF |
数量 |
0x04 |
4 字节 |
OTA 固件子头部的数量,值为 1 或 2 |
标签 |
0x08 |
4 字节 |
OTA 固件子头部的标签,值为 |
长度 |
0x0C |
4 字节 |
OTA 固件子头部的长度,值为 0x18 |
校验和 |
0x10 |
4 字节 |
对应升级固件的校验和 |
固件长度 |
0x14 |
4 字节 |
对应升级固件的长度 |
固件偏移量 |
0x18 |
4 字节 |
在当前 OTA 固件中此升级固件的起始位置 |
固件 ID |
0x1C |
4 字节 |
对应升级固件的固件 ID
|
修改编译配置
生成 OTA 固件
在编译项目时, OTA 固件将自动生成。
OTA 固件内将包括如下内容
OTA 固件头
BOOT 固件 (参考 BOOT OTA )
APP 固件或者压缩的 APP 固件
VFS2 固件 (参考 APP 固件中的 VFS)
编译项目。OTA 固件 (
ota_all.bin) 将生成在{SDK}\build_<soc>目录下。
OTA 例程
本节提供了多种固件 OTA 升级示例,涵盖不同的传输协议和数据源:
示例 |
说明 |
|---|---|
HTTP 升级: 通过 HTTP 协议从远程服务器下载并升级固件 。 |
|
HTTPS 安全升级: 通过 HTTPS 协议安全下载并升级固件,支持 SSL/TLS 加密 。 |
|
用户自定义升级: 支持自定义数据源, SPI、UART 等 。 |
|
文件系统升级: 从虚拟文件系统(SD 卡、Flash 等)读取固件并升级 。 |