固件升级编程指南

概述

OTA (空中下载技术)升级机制允许设备在固件正常运行时,通过 Wi-Fi、SPI、UART 以及文件系统等方式接收新的固件并将其保存到 Flash 对应的固件区,完成后重启即可运行新固件,用以问题修复,新功能发布和性能优化等场景。

使用 OTA 之前请先参考 Flash 布局 章节了解更多 Flash 分区相关细节。

固件分区

在 Flash 布局 中 BOOT 和 APP 固件均有两个固件分区( OTA1 和 OTA2 )用于冗余存储和版本控制。

用户需要根据实际的固件大小以及合理的预留空间来设置这些分区的大小。

备注

BOOT 固件的 OTA 功能默认关闭,如需使用,请参考 BOOT OTA

固件标签和版本号

OTA1/OTA2 固件的启动选择主要依赖于固件头中的固件标签和版本号。如下图所示,固件头包含 8 字节的固件标签、2 字节的主版本号和 2 字节的次版本号。

../_images/major_and_minor_version_zh.svg

固件标签和版本号的详细说明如下表所示:

项目

描述

OTA 选择流程

固件标签

  • 固定长度为 8 字节的数据

  • BOOT 固件和 APP 固件各自拥有独立的固件标签

检查固件标签的有效性

版本号

  • 由主版本号和次版本号组合而成,计算公式如下:

    版本号 = 主版本号 << 16 | 次版本号

  • BOOT 固件和 APP 固件各自拥有独立的版本号

比较版本号的大小

系统每次启动时, Bootloader 会检查固件标签的有效性并比较版本号,以决定从 OTA1 或 OTA2 分区启动。固件标签和版本号的一致性校验确保了启动过程的可靠性,具体检查流程如下:

  1. 检查 OTA1 和 OTA2 分区的固件标签是否有效:

    • 若仅一个固件标签有效,则从该有效固件启动;

    • 若两个固件标签均无效,则启动失败。

  2. 比较 OTA1 和 OTA2 分区的版本号:

    • 若两个固件的版本号均有效且不相同,则从版本号较高的固件启动;

    • 若两个固件的版本号均有效但相同,则默认从 OTA1 分区启动。

../_images/ota_select_diagram_zh.svg

固件切换策略

基于 固件标签和版本号 中的启动流程, OTA 功能支持以下两种固件切换策略,用于在下次启动时切换到新固件:

备注

有关切换策略的配置,请参考 配置工程(menuconfig) ,进入 CONFIG OTA OPTION > OTA Image Switch Strategy 进行选择。

基于版本号切换(默认):

在新固件中设置更高的版本号, OTA 升级完成后,设备将在下次启动时自动从新固件启动。 此策略适用于需要版本控制的场景。

配置步骤:

  1. 在 menuconfig 中选择切换策略:

       CONFIG OTA OPTION  --->
          [*] OTA Image Switch Strategy
             Select Strategy (Version Number Based)  --->
                   (X) Version Number Based
                   ( ) Valid Header Based
    
  2. 在目标 IC 的配置文件 manifest.json5 中修改新固件的版本号:

    RTL8721Dx:

    github source code

    1. BOOT 固件版本号配置(有效范围:0 ~ 32767)

      "image1":
      {
         "img_id": 0,
         "img_ver_major": 1,
         "img_ver_minor": 1,
         ...
      },
      
    2. APP 固件版本号配置(有效范围:0 ~ 65535)

      "image2":
      {
         "img_id": 1,
         "img_ver_major": 1,
         "img_ver_minor": 1,
         ...
      },
      

小技巧

每次 OTA 升级时,应递增版本号,确保新固件的版本号高于当前运行的固件。

两种切换策略对比

特性

基于版本号切换

基于有效标签切换

版本号要求

需递增版本号

无需修改版本号

配置复杂度

中等(需维护版本号)

简单(无需额外配置)

旧固件状态

保留完整

标签损坏

BOOT OTA 支持

所有芯片均支持

部分支持( RTL8726E/RTL8713E/RTL8720E/RTL8710E 不支持)

APP OTA 支持

所有芯片均支持

所有芯片均支持

防回滚功能

防回滚功能用于防止设备回滚到主版本号低于 OTP 中防回滚版本号的固件。启用该功能后,固件头中的主版本号不能小于 OTP 中设置的防回滚版本号,否则该固件将被视为无效。

通常,若本次 OTA 升级修复了旧版本的安全问题,可在升级完成后,将防回滚版本号设置为本次升级固件的主版本号,以防止设备回滚到存在该问题的旧版本。

防回滚流程如下:

  1. 分别读取 OTA1 和 OTA2 固件头中的主版本号,并与 OTP 中的防回滚版本号比较。

  2. 若固件的主版本号小于防回滚版本号,则该固件无效。

../_images/anti_rollback_flow_zh.svg
RTL8721Dx:

OTP 配置说明:

防回滚版本号存储在芯片的 OTP 区域中:

  • OTP 的物理地址范围为 0x36E ~ 0x36F。

  • OTP 值中比特 0 的个数即为防回滚版本号。OTP 默认值为全 F ,对应的防回滚版本号为 0。

  • OTP 的比特位只能从 1 写为 0 ,且该地址范围共 16 位,因此最多支持 16 个防回滚版本号(版本号范围:1 ~ 16)。

启用防回滚:

启用防回滚功能需完成以下 2 个步骤:

  1. 设置 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) 的固件将被禁止启动。

  2. 使能防回滚功能:

    通过以下命令读取当前值:

    AT+OTP=RRAW,368,1
    

    将读取到的值中的比特 6 置 0 后写回。例如读取到的值为 FF ,则写入 BF :

    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 需依次完成以下 3 个步骤,其中步骤 1、2 涉及 Flash 布局与设备 OTP 配置,步骤 3 仅影响编译产物,不涉及代码修改:

  1. 确认 BOOT OTA2 分区(按需修改):

    SDK 已在 Flash 布局 中默认规划了 BOOT OTA2 分区。默认分区满足需求时可直接使用;如需调整,请修改分区范围,并确保地址 4K 对齐。

  2. 将步骤 1 中确认的 BOOT OTA2 分区起始地址写入 OTP :

    RTL8721Dx:
    AT+OTP=WRAW,36C,2,<Value_OTP>
    

    Value_OTP 由 BOOT OTA2 起始地址计算得出,公式如下:

    Value_OTP = (IMG_BOOT_OTA2 >> 12 & 0xFF) << 8 | (IMG_BOOT_OTA2 >> 20 & 0xFF)
    

    举例:当 IMG_BOOT_OTA2 为 0x08234000 时,Value_OTP 为 3482 ,对应的写入命令为:

    AT+OTP=WRAW,36C,2,3482
    
  3. 启用 BOOT OTA 打包选项:

    参考 配置工程(menuconfig) ,进入 CONFIG OTA OPTION 配置,启用 Enable Bootloader OTA 。该选项仅在编译时将 BOOT 固件集成到 ota_all.bin 中,供 OTA 升级使用,不会改变代码。

       CONFIG OTA OPTION  --->
          [*] Enable Bootloader OTA
    

重要

  • OTP 写入不可逆:BOOT OTA2 地址一经写入将永久生效且无法修改,请在写入前确认地址无误。

  • 若 OTP 中的 BOOT OTA2 地址为 0xFFFF (即未配置),则:

    • OTA 升级时将跳过 BOOT 固件更新;

    • 设备始终从 BOOT OTA1 启动。

  • 强烈建议在批量生产前完成 BOOT OTA2 地址的 OTP 配置,并启用 BOOT OTA 打包选项。

BOOT OTA 选择流程

芯片 ROM 中的引导程序根据 BOOT 固件头中的标签和版本号选择启动分区,流程如下图所示。

../_images/km4_bootloader_ota_select_flow_zh.svg

APP OTA

APP OTA 启用步骤

APP 固件支持 OTA 升级,可从 OTA1 或 OTA2 分区启动。APP OTA 功能默认启用,无需额外配置。

RTL8726E:

该芯片带有 DSP,DSP 固件需合并打包到 APP 固件中,才能随 APP 固件一同进行 OTA 升级。具体的打包与启动方式请参考 DSP 编译和烧录 。

APP OTA 选择流程

BOOT 固件运行时,会根据 APP 固件的固件标签和版本号,选择从 OTA1 或 OTA2 分区启动。标签与版本号的含义请参考 固件标签和版本号,选择流程如下图所示:

../_images/application_image_ota_select_flow_zh.svg

OTA 压缩方案

启用 OTA 固件压缩功能后, APP OTA1 分区保持不变, APP OTA2 分区中的固件将以压缩形式存储,从而减小固件体积,节省 Flash 存储空间。

压缩算法采用 LZMA(Lempel-Ziv-Markov 链算法)。这是一种无损数据压缩算法,具有压缩比高、解压内存需求较低的特点,广泛应用于 .7z 格式文件及 7-Zip 软件中。

压缩效果用压缩率衡量,计算公式如下:

压缩率(%) = 压缩后固件大小 / 原始固件大小 × 100

压缩率受固件内容影响:原始固件内容越复杂,压缩率通常越高(即压缩效果越弱),一般为 50% ~ 70%。

启用 OTA 压缩功能后,启动流程具有以下特点:

  1. 固件标签和版本号的判断与普通流程一致。

  2. 仅 APP 固件支持压缩。

  3. 压缩固件在 OTA 过程中始终被下载到 OTA2 区域。

  4. 若判断需从 OTA2 中的压缩固件启动,BOOT 将先把固件解压并覆盖到 OTA1 区域,再从 OTA1 启动。

  5. OTA 下载过程和启动时的解压过程均支持掉电保护。

  6. 压缩固件仅在下载完成后的首次启动时解压一次,后续启动不再解压。

启动选择流程如下图所示:

../_images/compress_image_ota_select_flow_zh.svg

下面以一个示例说明压缩 OTA 的工作过程:

  1. 设备从 OTA1 启动并运行 OTA 应用。

  2. 压缩固件被下载到 OTA2。

  3. 下一次启动时,系统选择压缩固件,将其解压到 OTA1,然后从 OTA1 启动。

示意图如下:

../_images/compress_ota_select_diagram_zh.svg

启用 OTA 压缩功能需完成以下 2 个步骤:

  1. 启用 OTA 压缩选项:

    参考 配置工程(menuconfig) ,进入 CONFIG OTA OPTION 配置,启用 Enable Compress APP image 。

       CONFIG OTA OPTION  --->
          [*] Enable Compress APP image
    
  2. 编译生成压缩固件:

    编译时, APP 固件会被压缩,生成以下文件:

    • 压缩固件: {SDK}\build_<soc>\build\project_xx\image\xxx_app_compress.bin

    • 包含压缩固件的 ota_all.bin ,供 OTA 使用。

启用后,可通过烧录压缩固件来验证解压过程:

  1. 将以下文件烧录到对应区域:

    • {SDK}\build_<soc>\boot.bin :烧录到 BOOT OTA1 区域。

    • {SDK}\build_<soc>\build\project_xx\image\xxx_app_compress.bin :烧录到 APP OTA2 区域。

  2. 重启设备。系统将选择 OTA2 中的压缩固件,解压到 OTA1 后从 OTA1 启动,即可验证压缩固件的解压过程。

重要

  • 解压由 BOOT 固件完成,因此首次启用 OTA 压缩功能时,需同步更新并重新烧录 BOOT 固件。

  • 建议在批量生产前就启用 OTA 压缩功能。

备注

对于 RTL8735C:

  • BOOT 固件为 {SDK}\build_RTL8735C\amebapro3_boot.bin ,压缩固件为 {SDK}\build_RTL8735C\build\project_np\image\amebapro3_app_compress.bin 。

  • 压缩内容包含追加在 APP 固件之后的 AP FIP 固件。

  • 仅当从 NOR Flash 启动时,BOOT 固件才会执行解压。

OTA 固件

OTA 固件格式

ota_all.bin 的文件格式如下图所示,其中 OTA 固件头部分为主头部和子头部,主头部包含版本号和子头部的数量,子头部包含升级固件的信息。 每增加一个升级固件,便增加对应的一个子头部。

../_images/firmware_format_zh.svg
OTA 固件头部

项目

地址偏移

大小

描述

版本号

0x00

4 字节

OTA 固件主头部中的版本号,默认值是 0xFFFFFFFF

数量

0x04

4 字节

OTA 固件子头部的数量,值为 1 或 2

标签

0x08

4 字节

OTA 固件子头部的标签,值为 OTA

长度

0x0C

4 字节

OTA 固件子头部的长度,值为 0x18

校验和

0x10

4 字节

对应升级固件的校验和

固件长度

0x14

4 字节

对应升级固件的长度

固件偏移量

0x18

4 字节

在当前 OTA 固件中此升级固件的起始位置

固件 ID

0x1C

4 字节

对应升级固件的固件 ID

  • OTA_IMGID_BOOT:0x0

  • OTA_IMGID_APP:0x1

修改编译配置

  1. 参考 固件标签和版本号 调整版本号或者切换方案。

  2. 调整 BOOT 固件的防回滚版本号,并在必要时启用防回滚,启用步骤请参考 防回滚功能 。

  3. 如果用户需要升级 BOOT 固件,启用步骤请参考 BOOT OTA 。

  4. 如果需要启用 OTA 压缩功能,请按照 OTA 压缩方案 中的步骤进行操作。

生成 OTA 固件

在编译项目时, OTA 固件将自动生成。

  1. OTA 固件内将包括如下内容

  2. 编译项目。OTA 固件 ( ota_all.bin ) 将生成在 {SDK}\build_<soc> 目录下。

OTA 例程

本节提供了多种固件 OTA 升级示例,涵盖不同的传输协议和数据源:

示例

说明

ota_http

HTTP 升级: 通过 HTTP 协议从远程服务器下载并升级固件 。

ota_https

HTTPS 安全升级: 通过 HTTPS 协议安全下载并升级固件,支持 SSL/TLS 加密 。

ota_user

用户自定义升级: 支持自定义数据源, SPI、UART 等 。

ota_vfs

文件系统升级: 从虚拟文件系统(SD 卡、Flash 等)读取固件并升级 。