WS53指导资料

- hardware
  - SoC Wi-Fi、BLE和SLE Combo芯片 硬件用户指南
    - 前言
    - 封装与管脚
    - 电性能参数
    - 原理图设计建议
    - PCB设计建议
    - 热设计建议
    - 焊接工艺
    - 潮敏参数
    - 接口时序
    - 注意事项
    - 缩略语
  - 单板硬件关键器件选型指导
    - 前言
    - 关键器件选型约束
  - 射频测试 用户指南
    - 前言
    - 测试指令
    - 注意事项
- 软件资料
  - AT命令 使用指南
    - 前言
    - 指令说明
    - Wi-Fi模块AT指令
    - BLE&SLE模块AT指令
    - PLAT模块AT指令
  - BLE BQB认证测试 指导书
    - 前言
    - 蓝牙BQB认证简介
    - BQB协议认证
    - BQB 射频认证
    - WS53特性支持情况
    - 常见问题&测试命令汇总(FAQ)
  - BOOT API开发参考
    - 前言
    - 接口介绍
  - Boot移植应用 开发指南
    - 前言
    - Boot简介
    - RomBoot功能说明
    - LoaderBoot功能说明
    - FlashBoot说明
  - CJSON 开发指南
    - 前言
    - API接口说明
    - 开发指南
  - CoAP 开发指南
    - 前言
    - API接口说明
    - 开发指南
  - EFUSE 使用指南
    - 前言
    - 概述
    - 软件编程接口使用指导
    - Burntool烧写efuse_cfg.bin说明
  - FOTA 开发指南
    - 前言
    - 概述
    - 升级包制作
    - 升级包传输
    - 升级包保存
    - 升级包本地升级
    - AB面升级
    - FAQ
  - HTTP 开发指南
    - 前言
    - 开发指南
  - MQTT 开发指南
    - 前言
    - API接口描述
    - 开发指南
    - 注意事项
  - NV存储 用户指南
    - 前言
    - NV简介
    - NV编译预置
    - NV API指南
    - NV项汇总
  - SAMPLE用例 用户指南
    - 前言
    - SAMPLE简介
    - 外设SAMPLE用例
  - SDK 开发指南
    - 前言
    - 概述
    - 系统接口
  - SDK开发环境搭建 用户指南
    - 前言
    - 开发环境搭建
    - 编译
    - 新建APP
  - Syschannel 使用指南
    - 前言
    - Syschannel 简介
    - Syschannel 使用指南
    - Syschannel 编译指导
    - Syschannel 适配常见问题
  - TLS&DTLS 开发指南
    - 前言
    - API接口说明
    - 开发指南
    - 硬件适配
    - 注意事项
  - WFA认证测试指导书
    - 前言
    - WLAN WFA协议认证介绍
    - WS53 WFA认证
    - 缩略语
  - lwIP 开发指南
    - 前言
    - 概述
    - 功能特性
    - 开发指引
    - 网络安全
    - 常见问题
    - 术语
  - 产测FAQ
    - 前言
    - FAQ
    - 注意事项
    - 版本问题记录
  - 产线工装 用户指南
    - 前言
    - 测试软件准备
    - 单板测试方案
    - TX/RX测试参考
    - WLAN Facility与固件版本
    - 产线生产ESD防护要求
  - 低功耗 开发指南
    - 前言
    - 概述
    - 超深睡模式
    - 深睡模式
    - 浅睡模式
    - 常见问题
  - 单板冒烟 测试指南
    - 前言
    - 概述
    - 单板冒烟测试
  - 安全模块 使用指南
    - 前言
    - 概述
    - 错误码
    - 网络安全注意事项
  - 文件系统 使用指南
    - 前言
    - LFS简介
    - LFS编译预置
    - LFS接口API
  - 星闪认证测试 指导书
    - 前言
    - 星闪认证简介
    - 星闪协议认证
    - 星闪射频认证
    - 星闪特性支持
    - 常见问题&测试命令汇总（FAQ）
  - 特性开发说明书
    - 前言
    - 混杂模式特性说明
    - 动态国家码特性说明
    - BLE配网特性说明
    - 安全启动特性说明
    - FLASH在线解密特性说明
  - 第三方软件 移植指南
    - 前言
    - 移植指引
    - 常见问题
  - 设备驱动 开发指南
    - 前言
    - Pinctrl
    - GPIO
    - UART
    - SPI
    - QSPI
    - I2C
    - ADC
    - DMA
    - PWM
    - WDT
    - Timer
    - Systick
    - TCXO
    - SFC
    - eFuse
    - REBOOT
    - I2S
  - 软件 开发指南
    - 前言
    - 概述
    - Wi-Fi 软件开发指南
    - BLE&SLE 软件开发指南
    - 注意事项
  - 运维手册
    - 前言
    - 系统/平台模块
    - Wi-Fi模块
  - 鸿蒙XTS认证 指导书
    - 前言
    - 概述
    - 准备工作
    - 编译openharmony静态库
    - XTS版本编译和执行

### 该页内容

- 前言
- 概述
  - 功能描述
  - 固件升级流程
  - 开发接口
- 升级包制作
  - 流程原理
  - 开发流程
  - 开发实例
  - 注意事项
- 升级包传输
- 升级包保存
  - 开发流程
  - 注意事项
  - 编程实例
- 升级包本地升级
  - 开发流程
  - 注意事项
  - 编程实例
- AB面升级
  - 接口说明
  - 开发实例
  - 注意事项
- FAQ

# 前言

概述

本文档主要介绍固件升级流程和接口的使用方法，指导用户应用及开发升级特性。

产品版本

与本文档相对应的产品版本如下。

产品名称 | 产品版本
--- | ---
WS53 | V100

读者对象

本文档主要适用于以下工程师：

- 技术支持工程师

- 软件开发工程师

符号约定

在本文中可能出现下列标志，它们所代表的含义如下。

符号 | 说明
--- | ---
| 表示如不避免则将会导致死亡或严重伤害的具有高等级风险的危害。
| 表示如不避免则可能导致死亡或严重伤害的具有中等级风险的危害。
| 表示如不避免则可能导致轻微或中度伤害的具有低等级风险的危害。
| 用于传递设备或环境安全警示信息。如不避免则可能会导致设备损坏、数据丢失、设备性能降低或其它不可预知的结果。 “须知”不涉及人身伤害。
| 对正文中重点信息的补充说明。 “说明”不是安全警示信息，不涉及人身、设备及环境伤害信息。

修改记录

文档版本 | 发布日期 | 修改说明
--- | --- | ---
05 | 2026-04-20 | 更新“流程原理”小节内容。
04 | 2025-05-28 | 更新“功能描述”小节内容。 新增“AB面升级”小节内容。
03 | 2025-01-14 | 更新“注意事项”小节内容。
02 | 2024-11-25 | 更新“注意事项”小节内容。
01 | 2024-08-08 | 第一次正式版本发布。 更新“注意事项”小节内容。
00B02 | 2024-07-15 | 更新“概述”小节内容。 更新“升级包制作”小节内容。 更新“开发流程”、“注意事项”小节内容。 更新“注意事项”小节内容。
00B01 | 2024-06-11 | 第一次临时版本发布。

# 概述

- 功能描述

- 固件升级流程

- 开发接口

## 功能描述

固件升级功能可用于对设备芯片的固件进行更新。

WS53的固件包含多个固件镜像，不同镜像可能采用不同的升级方式，包括全镜像升级、压缩升级，每个核可以根据情况选择合适的升级方式。

- 全镜像升级：新的固件镜像不做处理，直接打包到固件升级包（以下简称为“升级包”）中，在设备上直接将其更新至目标位置。这种方式一般适用于固件镜像较小的场景。

- 压缩升级：新的固件镜像经过压缩处理后，打包到升级包中，首先在设备上解压恢复，再更新至目标位置。这种方式一般适用于固件镜像较大的场景。

- AB面升级：不制作升级包，直接将镜像传输到指定分区，然后切换使用的镜像分区，每次升级都在A面与B面之间切换，且互为备份，该方式一般适用于有一定安全性考虑的场景。该升级方式与前两种不兼容，使用方法请直接参考“AB面升级”章节，“升级包制作”~“升级包本地升级”章节不适用于该升级方式。

## 固件升级流程

从端到端来看，整个芯片固件的升级操作包括以下过程，如图1所示。

- 升级包制作：固件开发完成后，根据实际情况，将要升级的固件镜像按照配置文件中对应的升级方式，生成固件升级包文件的过程。

- 升级包传输：将升级包传输到设备上。根据实际情况，可能有多种传输方式，如蓝牙、串口等。

- 升级包保存：将传输到设备的固件升级包保存至本地存储器，在WS53中，固件升级包存储在FLash中。

- 升级包本地校验：升级程序替换前，在设备上校验升级包的完整性和合法性。

- 升级包本地升级：APP程序中在设备上将新的固件更新至目标位置，重启进入FlashBoot继续执行升级，升级完成后重启。

图 1 固件升级流程图

## 开发接口

根据所在的程序的不同，升级接口可分为两部分。

- 升级包存储，由APP程序执行。

  - 初始化接口。

  - 升级包写入本地存储器和从本地存储器读取的接口。

  - 申请开始进行本地升级接口。

- 升级包本地升级，分两部分执行，执行流程是一致的，NV镜像的升级由APP程序完成，其他镜像的升级在flashboot程序中完成。

  - 初始化接口。

  - 升级包校验接口。

  - 升级进度通知函数注册接口。

  - 开始执行本地升级接口。

  - 获取升级结果接口。

升级模块提供的接口如表1和表2所示。参见头文件：include\middleware\utils\upg.h。接口参数及返回值说明如表3所示。

表 1 升级接口（升级包存储部分）描述

接口名称 | 描述
--- | ---
uapi_upg_init | 升级模块初始化。
uapi_upg_prepare | 保存升级包到本地存储器前的准备工作。
uapi_upg_write_package_async/uapi_upg_write_package_sync | 将升级包数据写入本地存储器（异步方式/同步方式）。
uapi_upg_read_package | 从本地存储器读取升级包数据。
uapi_upg_request_upgrade | 申请开始进行本地升级，所有升级包数据全部保存完成后，调用此接口。
uapi_upg_get_storage_size | 获取可存放升级包的空间大小。

表 2 升级接口（本地升级部分）描述

接口名称 | 描述
--- | ---
uapi_upg_init | 升级模块初始化。
uapi_upg_register_progress_callback | 注册升级进度通知回调函数，注册后，在本地升级过程中会调用回调函数通知当前进度。
uapi_upg_start | 开始本地升级。
uapi_upg_get_result | 获取升级结果。
uapi_upg_verify_file_head | 校验升级包头结构。
uapi_upg_verify_file_image | 校验升级包中的升级镜像。
uapi_upg_verify_file | 校验整个升级包。
uapi_upg_register_user_defined_verify_func | 注册用户自定义字段的校验函数。 升级包结构中预留了48Byte用于用户自定义数据的校验。注册自定义校验函数后，被注册的函数会在调用uapi_upg_verify_file_head和uapi_upg_verify_file函数时被调用到。如果自定义数据校验失败uapi_upg_verify_file_head和uapi_upg_verify_file会返回失败。

表 3 升级接口入参及返回值描述

接口原型 | 参数及返回值说明
--- | ---
errcode_t uapi_upg_init(const upg_func_t *func_list) | 入参说明： func_list：注册回调列表，upg_func_t类型。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_start(void) | 入参说明：无。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_register_progress_callback(uapi_upg_progress_cb func) | 入参说明： func：回调函数，该函数需业务实现。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_get_result(upg_result_t *result, uint32_t *last_image_index) | 入参说明： result：出参，保存升级结果的内存地址，类型upg_result_t。 last_image_index：出参，保存最后一个处理的镜像的索引。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_prepare(upg_prepare_info_t *prepare_info) | 入参说明： prepare_info：入参，upg_prepare_info_t*类型，准备信息的指针。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_write_package_async(uint32_t offset, const uint8_t *buff, uint16_t len, uapi_upg_write_done_cb callback) | 入参说明： offset：入参，uint32_t类型，相对升级包开头的偏移。 buff：入参，const uint8_t *类型，存放升级包数据的buffer。 len：入参，uint16_t类型，升级包数据buffer的长度。 callback：入参，uapi_upg_write_done_cb类型，写入完成的回调函数。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_write_package_sync(uint32_t offset, const uint8_t *buff, uint16_t len) | 入参说明： offset：入参，uint32_t类型，相对升级包开头的偏移。 buff：入参，const uint8_t *类型，存放升级包数据的buffer。 len：入参，uint16_t类型，升级包数据buffer的长度。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_read_package(uint32_t offset, uint8_t *buff, uint32_t len) | 入参说明： offset：入参，uint32_t类型，相对升级包开头的偏移。 buff：出参，uint8_t *类型，存放升级包数据的buffer。 len：入参，uint32_t类型，读取数据buffer的长度。 返回值： ERRCODE_SUCC：成功。 其他：失败。
uint32_t uapi_upg_get_storage_size(void) | 入参说明：无。 返回值： 0：失败返回0。 其他：成功返回空间大小。
errcode_t uapi_upg_request_upgrade(bool reset) | 入参说明： reset：入参，bool类型，申请流程结束后是否重启系统。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_verify_file_head(const upg_package_header_t *pkg_header) | 入参说明： pkg_header：入参，upg_package_header_t *类型，指向升级包头结构的指针。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_verify_file_image(const upg_image_header_t *img_header, const uint8_t *hash, uint32_t hash_len, bool verify_old) | 入参说明： img_header：入参，upg_image_header_t*类型，指向升级包中升级镜像头结构的指针。 hash：入参，uint8_t*类型，升级镜像的HASH值。 hash_len：入参，uint32_t类型，HASH的长度（单位：Byte）。 verify_old：入参，bool类型，是否校验旧镜像。 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t uapi_upg_verify_file(const upg_package_header_t *pkg_header) | 入参说明： pkg_header：入参，upg_package_header_t *类型，指向升级包头结构的指针。 返回值： ERRCODE_SUCC：成功。 其他：失败。
void uapi_upg_register_user_defined_verify_func(uapi_upg_user_defined_check func, uintptr_t param) | 入参说明： func：入参，upg_package_header_t *类型，用于校验用户自定义字段的校验函数。 param：入参，uintptr_t类型，注册参数。 返回值：无。

# 升级包制作

- 流程原理

- 开发流程

- 开发实例

- 注意事项

## 流程原理

图 1 升级包制作流程

升级包的制作流程示意图如图1所示，具体操作步骤如下：

1. 编译生成带签名的明文新镜像。编译生成镜像请参见《WS53V100 SDK开发环境搭建 用户指南》中“编译SDK（Cmake）-编译方法”章节。

2. 对新镜像进行处理。处理方式包含场景如下：

   - 将新镜像压缩并加密，生成加密的压缩新镜像。

   - 将新镜像压缩不加密，生成压缩的新镜像。

   - 新镜像不做处理。

3. 对步骤2中生成的镜像添加升级镜像头。

4. 对所有需要升级的镜像根据所选择的不同处理方式执行步骤1～步骤3。

5. 将生成的所有带升级镜像头的升级镜像文件合并到最终升级包中，并对整包添加升级包头，进行数字签名。

> 说明： 2中的场景，需根据实际情况做选择。例如WS53不使能镜像加密，所有需要加密的场景则不能选择。

## 开发流程

提供基于Windows/Linux系统下的Python入口编译能力，升级包可以在所有镜像编译完成后，通过执行“build/config/target_config/ws53/build_ws53_update.py”来制作，脚本及配置文件说明如下：

- 在“build_ws53_update.py”文件中配置各个镜像的路径以及其他脚本所需的参数，并调用升级包制作脚本“build_upg_pkg.py”，如下所示。“build_ws53_update.py”中配置的各个路径可以根据实际路径修改。

  “build_ws53_update.py”调用“build_upg_pkg.py”代码示例如下：

      import os
      import sys
      import argparse
      file_dir = os.path.dirname(os.path.realpath(__file__))
      g_root = os.path.realpath(os.path.join(file_dir, "..", "..", "..", ".."))
      sys.path.append(os.path.join(g_root, 'build', 'script'))
      from build_upg_pkg import begin
      class upg_base_info:
          def __init__(self):
              self.root_path = g_root
              # 升级包结构配置文件
              self.fota_format_path = os.path.join(self.root_path, "build", "config", "target_config", "ws53", "fota")
              # 产品升级配置文件
              self.fota_cfg = os.path.join(self.root_path, "build", "config", "target_config", "ws53", "fota", "fota.cfg")
              # 产品镜像输出路径
              self.output = os.path.join(self.root_path, "output", "ws53")
              # 产品升级镜像包输出路径
              self.upg_output = os.path.join(self.output, "upgrade")
              # 产品升级制作临时文件输出路径
              self.temp_dir = os.path.join(self.upg_output, "temp_dir")
              # 产品镜像路径
              self.flashboot = os.path.join(self.output, "acore", "ws53-flashboot", "flashboot_sign.bin")
              self.app_bin = os.path.join(self.output, "acore", "ws53-liteos-app", "ws53-liteos-app-sign.bin")
              self.nv_bin = os.path.join(self.output, "acore", "nv_bin", "ws53_all_nv.bin")

              self.flashboot_old_bin = os.path.join(self.output, "acore", "old_version", "flashboot_sign.bin")
              self.app_old_bin = os.path.join(self.output, "acore", "old_version", "ws53-liteos-app-sign.bin")
              self.nv_old_bin = os.path.join(self.output, "acore", "old_version", "ws53_all_nv.bin")
      def get_new_image(input,info):
          image_list = []
          if 'app' in input:
              image_list.append("=".join([info.app_bin, "application"]))
              print(1)
          if 'boot' in input:
              image_list.append("=".join([info.flashboot, "flashboot"]))
              print(2)
          if 'nv' in input:
              image_list.append("=".join([info.nv_bin, "nv"]))
              print(3)
          new_image = "|".join(image_list)
          return new_image
      def get_old_image(input,info):
          image_list = []
          if 'app' in input:
              image_list.append("=".join([info.app_old_bin, "application"]))
              print(11)
          if 'boot' in input:
              image_list.append("=".join([info.flashboot_old_bin, "flashboot"]))
              print(22)
          if 'nv' in input:
              image_list.append("=".join([info.nv_old_bin, "nv"]))
              print(33)
          old_image = "|".join(image_list)
          return old_image
      def get_parameters():
          parser = argparse.ArgumentParser()
          parser.add_argument('--pkt', type=str, default = 'app',
                              help='需要生成的镜像,包括: app,boot,nv')
          config = parser.parse_args()
          return config
      if __name__ == '__main__':
          info = upg_base_info()
          conf = get_parameters()
          input = conf.pkt.split(",")
          conf.app_name        = "update"
          conf.upg_format_path = info.fota_format_path
          conf.base            = info.fota_cfg
          conf.temp_dir        = info.temp_dir
          conf.new_images      = get_new_image(input,info)
          conf.old_images      = get_old_image(input,info)
          conf.output_dir      = info.upg_output
          conf.type            = 0
          begin(conf)

- 升级包制作脚本为：build/script/build_upg_pkg.py。

- 配置文件为：build/config/target_config/ws53/fota/fota.cfg，配置文件的各个字段说明如表1所示。

- 配置文件“fota.cfg”中涉及到镜像ID，请参考表2，表中的ID均为特殊的魔术字。

- 配置文件“fota.cfg”中可配置升级校验方式，。

- 安全校验使用ECC256_SHA256密钥算法，密钥文件使用openssl工具（https://www.openssl.org）进行生成，执行命令为：openssl ecparam -genkey -name brainpoolP256r1 -out "ec_bp256_key.pem"

表 1 fota.cfg主要内容说明

标签 | 属性 | 说明
--- | --- | ---
[SIGN_CFG] | - | 签名相关属性。
SignSuite | 签名密钥类型，默认为SHA256(RSA_4096)。
UpgImagePath | 升级包文件路径和名称。
UpgSignedImagePath | 升级包签名后的路径和名称。
RootKeyFile | 根密钥文件路径。
SubKeyFile | 二级秘钥文件路径。
[TOOLS] | - | 制作工具配置属性。
UpgToolPath | 升级工具路径和名称（升级工具可用作制作签名文件）。
LzmaToolPath | 压缩工具路径和名称。
[FOTA_KEY_AREA] | - | 升级包Key区属性。
ImageId | 升级包Key区镜像ID，固定为0xCB8D154E。
KeyAlg | 升级包Key区域密钥算法类型。 0x2A13C812：ECC256_SHA256 其他值：非法。
KeyVersion | 升级包Key区版本号。
KeyVersionMask | 升级包Key区版本号掩码。
Msid | 市场区域ID。
MsidMask | 市场区域ID掩码。
[FOTA_INFO_AREA] | - | 升级包INFO区属性。
ImageId | 升级包INFO区镜像ID，固定为0xCB8D154E。
HardwareID | 硬件版本号。
Msid | 市场区域ID。
MsidMask | 市场区域ID掩码。
[FlashBoot/nv/application] | - | 镜像类型名称。
HeaderMagic | 头结构魔术字，固定为0x464F5451。
ImageId | 升级镜像ID，与原始镜像的ImageId相同，镜像ID请参考表2。
DecompressFlag | 升级镜像模式标记。NV不支持压缩，仅支持全量升级 0x3C7896E1：压缩。 其他：原始镜像不处理。
ReRncFlag | 升级镜像加密标记。仅app镜像支持加密升级 0x3C7896E1：加密。 其他：不加密。

表 2 镜像ID列表

镜像 | 镜像ID
--- | ---
FLASHBOOT IMAGE ID | 0x4B1E3C2D
APPLICATION IMAGE ID | 0x4B0F2D2D
NV IMAGE ID | 0xCB9E063C

## 开发实例

以下提供制作升级包的一个实例，一切操作都是在SDK包中的默认配置上进行修改。

1. 修改配置文件“build/config/target_config/ws53/fota/fota.cfg”（可选）。

   - 升级方式

     配置选项[FOTA_INFO_AREA]之后的每一项[xxx]都是对应镜像、镜像签名的配置项，根据升级方式选择修改DecompressFlag参数。

     如：

     - flashboot的镜像以全量（原镜像不作处理）升级的方式，则[flashboot]的DecompressFlag设置为0。

     - app的镜像签名以压缩升级的方式，则[application]的DecompressFlag设置为0x3C7896E1。

     - NV的镜像的升级方式由NV特性自己完成，对配置方式不感知。

     - app镜像可以开启加密升级，在开启flash在线解密功能的前提下，配置[application]的[ReRncFlag]为0x3C7896E1。

   - 防回滚号

     若此次升级不希望用户进行回退版本，则可以在升级包中修改对应镜像、镜像签名配置项中的防回滚版本号version_ext。

     - 防回滚版本号可以单独对某镜像进行设置。

     - 防回滚版本号按照二进制向高位逐个置一的规则进行增加；如：0x0(0000) -> 0x1(0001) -> 0x3(0011) ->0x7(0111) -> 0xF(1111) ->0x1F(0001 1111)。

2. 核对并修改升级包生成脚本“build/config/target_config/ws53/build_ws53_update.py”。

   - 核对产品镜像路径。确保要升级的产品镜像路径是正确的，路径不限于脚本中默认填写的路径，可根据实际情况进行修改。

3. 生成升级包文件。

   运行脚本“build/config/target_config/ws53/build_ws53_update.py”生成升级包，本脚本支持在Linux或Windows上运行，请在运行环境安装python3.7级以上版本Python；根据需要，可添加入参 --pkt=<image1>,<image2>，不添加入参时，默认入参为app。

   - Windows环境请在cmd窗口SDK根目录下执行以下指令：

         python build\config\target_config\ws53\build_ws53_update.py

   - Linux环境请在SDK根目录下执行以下指令：

         python3 build/config/target_config/ws53/build_ws53_update.py

   执行完成后，在默认路径“output/ws53/upgrade”生成升级包“update.fwpkg”。

## 注意事项

SDK默认配置为非加密，在开启flash在线解密功能时，镜像以加密的状态存储在flash上，制作OTA升级包时需要配置为加密状态。

以压缩加密升级为例：

1. 将"sdk/build/config/target_config/ws53/sign_config/ws53_liteos_app_ecc.cfg"配置文件中Iv项注释打开，例如："Iv=FF000000000000000000000000000000"。

2. 在sdk下执行"./build.py -c ws53-liteos-app"编译出app镜像(该app镜像仅用于打包生成OTA镜像，直接烧录无法启动)。

3. 执行"python3 build/config/target_config/ws53/build_ws53_update.py"将app镜像打成OTA升级包结果在output/ws53/upgrade/update.fwpkg。

> 说明： 开启镜像加密功能时，推荐使用数字签名服务器对OTA镜像签名加密，可参考《WS53V100 二次开发网络安全 注意事项》中数字签名服务器章节打包OTA镜像

# 升级包传输

升级包的传输，由应用程序实现，可以有多种方式，本文中不做详细描述。

> 说明：

> 1. 升级包的文件名固定为“update.fwpkg”。

> 2. 升级包的存放位置可能会需要根据实际情况有所差异，以实际的Flash分区表中FOTA分区的地址为准

# 升级包保存

- 开发流程

- 注意事项

- 编程实例

## 开发流程

在WS53中，使用文件系统保存升级包，因此升级包是以二进制数据保存在flash中。

保存升级包文件有以下两种典型场景，可以根据升级包传输的实现来选择合适的开发场景。

场景：传输流程中，传输升级包数据到内存中

1. 调用uapi_upg_init初始化升级模块。

2. 调用uapi_upg_prepare函数，执行本地存储器的准备工作，包括初始化升级标记等。

3. 应用程序开始接收升级包，调用uapi_upg_write_package_sync函数，将内存中的分包数据写到flash对应位置。

4. 继续接收下一个分包数据，再次调用uapi_upg_write_package_sync函数，写入到对应位置。直到所有数据全部写入完成。如果该接口返回错误，则停止升级流程。

5. 调用uapi_upg_request_upgrade，传入重启参数后可以开始升级流程。

6. 中传入参数不包含重启时，需要进行手动重启，进入flashboot自动开始升级流程。

## 注意事项

- 升级包的分包数据必须按照顺序传输和保存，已经保存的分包不能再次传输。例如：第一次传输并保存了0～1023的数据，下一次必须从1024开始，否则uapi_upg_write_package_sync接口会报错。

- 在传输过程中是否有数据的分段校验，由传输过程保证。如果传输过程中数据出现错误而没有校验和重传机制，则只能等到全部保存完成后，在本地升级启动过程中调用uapi_upg_verify_file校验，若该校验失败，会导致升级失败。

- 需要在flash上配置对应的分区分别给运行程序和升级包数据，在适配压缩升级时，推荐运行程序的flash分区大小与升级包数据存放区的flash分区大小比例为1:0.7，该比例数据来源于压缩升级中的压缩比

- 当前ws53上flash分区详情如下表所示。

  序号 | 分区编号 | 分区名称 | 大小 | 地址空间 | 备注
  --- | --- | --- | --- | --- | ---
  1 | 0x00 | ssb | 24 KB | 0x0_2000~0x0_7FFF | 安全boot，只能在产测时用burntool工具烧录，不建议更改此分区的地址和长度
  2 | 0x01 | FLASH Boot | 64 KB | 0x2_0000~0x2_FFFF | FlashBoot区，起始地址需要4K对齐，不建议更改此分区的地址和长度。
  3 | 0x02 | FLASH Boot_Backup | 64 KB | 0x1_0000~0x1_FFFF | FlashBoot备份，只能在产测时用burntool工具烧录，不建议更改此分区的地址和长度。
  4 | 0x03 | Root public key | 2 KB | 0x0_0000~0x0_07FF | 根公钥，与分区表打包为一个文件，地址和空间禁止改动。
  5 | 0x08 | Customer factory | 16 KB | 0x0_8000~0x0_BFFF | 用户工厂区，本分区设计产测时写入，产测结束后，后续app只读，可以用于存放客户规划的比较重要的数据，该分区内容，完全由客户自行管理规划。
  6 | 0x09 | NV Factory | 16 KB | 0x0_C000~0x0_FFFF | NV工厂区，本分区在退出产测模式时，被写入，之后在app运行过程中不可写入。不建议改动此分区的地址和长度。
  7 | 0x10 | NV DATA | 16 KB | 0x3F_C000~0x3F_FFFF | NV工作区
  8 | 0x11 | crash info | 8 KB | 0x3F_A000~0x3F_BFFF | panic信息离线保存区
  AB面升级方案
  9 | 0x20 | ImageA | 1924 KB | 0x3_0000～0x21_0FFF | ImageA文件，包含A核镜像以及C核镜像，A核镜像起始地址参考0x21对应分区的起始地址
  10 | 0x22 | ImageB | 1928 KB | 0x21_1000～0x3F_2FFF | ImageB文件，包含A核镜像以及C核镜像，最后一个4Kpage会用来存放ab面相关配置参数
  压缩升级方案
  9 | 0x20 | Image | 2304 KB | 0x3_0000～0x26_FFFF | Image文件，包含A核镜像以及C核镜像，该部分包含0x21的地址区间在内，0x21为A核镜像的起始地址与分区大小
  10 | 0x22 | fota data | 1548 KB | 0x27_0000～0x3F_2FFF | 压缩升级包，压缩比为0.7左右
  剩余flash空间
  11 | 0x30 | reserve | 28 KB | 0x3F_3000～0x3F_9FFF | 预留区

- 分区ID从0x00开始到0x09，共计6个分区，设计为在产测阶段写入，在后续app运行过程中处于被写保护状态，只读。

- 每个分区的起始地址以及大小均需与4K对齐

- 分区ID最大规格支持16个，从0x30开始的分区ID，可以定制开发，无特殊要求，不建议删除分区ID，如果预留区空间过小，希望增加预留区空间，可以从imageA/imageB/fota data分区切割部分空间，调整方法请参考《WS53V100 SDK开发环境搭建 用户指南》文档中“Flash分区表配置”章节。

## 编程实例

    static void upg_serial_putc(const char c)
    {
        printf("%c", c);
    }
    void test_update(void)
    {
        uint32_t file_size = 0x2000;  /*  升级文件大小（实际大小由APP获取）  */
        uint32_t max_len;
        uint32_t read_len;
        uint32_t write_len = 0;
        errcode_t ret;
        /* 依赖分区模块 */
        ret = uapi_partition_init(); /* 该接口可以重复调用 */
        if (ret != ERRCODE_SUCC){
            printf("uapi_partition_init error. ret = 0x%08x\r\n", ret);
        }
        /* 1. 初始化update模块 */
        /*  2. 获取APP升级文件大小上限.  */
        max_len = uapi_upg_get_storage_size();
        if (file_size > max_len) {
            return ERRCODE_FAIL;
        }
        upg_prepare_info_t prepare_info;
        /* 3. 将升级文件的大小，传给uapi_upg_prepare函数，执行升级准备工作 */
        prepare_info.package_len = file_size;
        ret = uapi_upg_prepare(&prepare_info);
        if (ret != ERRCODE_SUCC) {
            printf("uapi_upg_prepare error = 0x%x\r\n", ret);
        }
        /* 4. 用户自行实现：通过串口或网络下载升级文件，调用uapi_upg_write_package_sync函数存储（场景一），
           或直接保存至文件系统中（场景二） */
        char *buf = (char *)malloc(0x1000 * sizeof(char)); /* 单次读取分包的长度可以自行调整 */
        memset_s(buf, 0x1000 * sizeof(char), 0, 0x1000 * sizeof(char));
        while (write_len < file_size) {
            通过网络或者其他方式获取升级包(&read_len, buf);
            if (write_len + read_len > max_len) {
                free(buf);
                return;
            }
            ret = uapi_upg_write_package_sync(write_len,(uint8_t *)buf, read_len);
            if (ret != ERRCODE_SUCC) {
                free(buf);
                return;
            }
            write_len += write_len;
            memset_s(buf, 0x1000 * sizeof(char), 0, 0x1000 * sizeof(char));
        }
        /* 5. 升级文件下载完成后，申请开始本地升级 */
        ret = uapi_upg_request_upgrade(false);
        if (ret != ERRCODE_SUCC) {
            printf("uapi_upg_request_upgrade error = 0x%x\r\n", ret);
        }
        /* 6. 升级准备工作完成后，复位开始本地升级 */
        upg_reboot();
        return;
    }

# 升级包本地升级

- 开发流程

- 注意事项

- 编程实例

## 开发流程

在WS53中，本地升级程序在被称为flashboot的程序中运行。

1. 调用uapi_upg_init初始化升级模块。

2. 调用uapi_upg_register_progress_callback函数注册进度通知回调函数。

3. 如果用户有自定义的校验数据，调用uapi_upg_register_user_defined_verify_func注册自定义数据校验函数。

4. 调用uapi_upg_start开始本地升级。

5. 复位重启。

## 注意事项

- flashboot中调用的uapi_upg_init与"升级包保存"中的uapi_upg_init虽然是同一个函数，但是运行在不同的程序，互相不受影响，均需要调用。

- 虽然升级模块提供了数据校验的接口，但由于调用uapi_upg_start开始本地升级后，内部会首先调用校验的接口校验升级包，因此若无特殊需求（例如只做校验不做本地升级），正常情况下不必单独调用校验接口。

- 如果注册了进度通知回调函数，在升级过程中会调用回调函数，并传入当前进度值。在回调函数中减少执行耗时较长的操作，否则会影响升级流程的时间。

- 本地升级过程中不要断电，升级开始后不支持停止升级。

- uapi_upg_start函数如果返回成功，则说明所有镜像都成功更新。更新成功后会重启重新进入flashboot，随后正常模式启动。

- uapi_upg_start函数如果返回失败，则有两种情况：

  - 如果是更新镜像之前的校验失败，则说明升级包传输出错，或者收到的是不适合本设备的升级包，则直接退出升级流程，不会再进入flashboot，正常模式启动。

  - 如果是更新镜像已经开始后出现错误导致失败，则重启后仍然会进入flashboot进行重试，最多重试三次。恢复正常成功升级或者三次均失败后，不再重启进入flashboot，正常模式启动。

- app和flashboot的升级在flashboot中完成，NV在app程序中升级。

## 编程实例

    static void upg_serial_putc(const char c)
    {
        printf("%c", c);
    }
    static void upg_progress_callback(uint32_t percent)
    {
        printf("%d%% ", percent);
    }
    void app_init(void)
    {
        uint32_t file_size = 0x2000;  /*  升级文件大小（实际大小由APP获取）  */
        uint32_t max_len;
        /* 文件系统相关的初始化 */
        /* 依赖flash */
        /* 依赖分区模块 */
        ret = uapi_partition_init(); /* 该接口可以重复初始化 */
        if(ret != ERRCODE_SUCC){
            printf("uapi_partition_init error. ret = 0x%08x\r\n", ret);
        }
        /* 注册进度通知函数 */
        (td_void)uapi_upg_register_progress_callback(upg_progress_callback); /* 此处回调函数需业务实现 */
        /* 开始本地升级 */
        ret = uapi_upg_start();
        if (!(ret == ERRCODE_UPG_NOT_NEED_TO_UPDATE || ret == ERRCODE_UPG_NOT_INIT ||  ret == ERRCODE_SUCC)) {
            printf("uapi_upg_start error = 0x%x\r\n", ret);
        }
        /* 复位 */
        upg_reboot();
        return;
    }

# AB面升级

- 接口说明

- 开发实例

- 注意事项

## 接口说明

表 1 AB面升级接口入参及返回值描述

接口原型 | 参数及返回值说明
--- | ---
upg_region_index upg_get_run_region(void) | 接口说明：获取当前运行分区 入参说明：无。 返回值： UPG_REGION_A：A面分区。 UPG_REGION_A：B面分区。
upg_region_index upg_get_upg_region(void) | 接口说明：获取升级分区 入参说明：无。 返回值： UPG_REGION_A：A面分区。 UPG_REGION_A：B面分区。
uint32_t upg_ab_image_read(upg_region_index upg_region, uint32_t offset, uint8_t *buf, size_t len) | 接口说明：读取镜像 入参说明： upg_region：需要读取的分区。 offset：读取偏移 buf：读取的数据的缓存区 len：读取数据的长度 返回值： ERRCODE_SUCC：成功。 其他：失败。
uint32_t upg_ab_image_write(upg_region_index upg_region, uint32_t offset, uint8_t *buf, size_t len) | 接口说明：写入镜像 入参说明： upg_region：需要写入的分区。 offset：写入偏移 buf：写入的数据 len：写入数据的长度 返回值： ERRCODE_SUCC：成功。 其他：失败。
errcode_t upg_set_run_region(upg_region_index upg_region) | 接口说明：设置下次启动的镜像区域 入参说明： upg_region：下次启动的分区 返回值： ERRCODE_SUCC：成功。 其他：失败
errcode_t upg_ab_start(upg_region_index upg_region) | 接口说明：开始升级接口，擦除对应分区 入参说明： upg_region：需要升级的分区 返回值： ERRCODE_SUCC：成功。 其他：失败

## 开发实例

    void app_init(void)
    {
        upg_region_index upg_region = upg_get_upg_region();  /*  获取要升级的分区  */
        errcode_t ret = upg_ab_start(upg_region); /* 该接口会擦除对应分区 */
        if(ret != ERRCODE_SUCC){
            printf("upg_ab_start error. ret = 0x%08x\r\n", ret);
        }
        /* 写入镜像数据 */
        uint32_t offset;
        uint32_t max_size = 0;
        uint32_t buf_size = 0;
        uint8_t upg_buf[0x1000];
        net_get_upg_package_size(&max_size);/* 此处需要通过云端获取镜像大小，需要自行实现 */
        for (offset = 0; offset < max_size;) {
            buf_size = ((max_size - offset) < 0x1000) ? (max_size - offset) : 0x1000;
            net_get_upg_package_buf(offset, upg_buf, buf_size); /* 此处需要通过云端获取镜像数据，需要自行实现 */
            ret = upg_ab_image_write(upg_region, offset, upg_buf, buf_size);
            if (ret != ERRCODE_SUCC){
                printf("upg_ab_image_write error. ret = 0x%08x\r\n", ret);
            }
            offset += buf_size;
        }
        /* 写入完成后切换启动分区 */
        ret = upg_set_run_region(upg_region);
        if (ret != ERRCODE_SUCC) {
            printf("upg_set_run_region error = 0x%x\r\n", ret);
        }
        /* 复位 */
        upg_reboot();
        return;
    }

使用AB面升级方式的方法：

1. SDK下执行 ./build.py ws53-flashboot menuconfig 命令配置flashboot的menuconfig，Middleware -> Chips -> Chip Configurations for ws53 -> 选中 FOTA for AB；然后保存即可。

2. 同步骤1方式，执行 ./build.py ws53_liteos_app menuconfig 命令配置flashboot的menuconfig，Middleware -> Chips -> Chip Configurations for ws53 -> 选中 FOTA for AB；然后保存即可。

3. 参考上述示例代码，编写AB面升级流程代码。

4. 配置完成后重新编译flashboot以及ws53_liteos_app即可。

## 注意事项

- 写入新镜像前，需要确认镜像大小没有超过对应分区大小。

- 写入镜像时，需要确认镜像数据正常，无错传漏传。

- 写入镜像与实际烧录镜像一致，不需要封装镜像，增加包头等。

- AB面升级方式对flash的利用率较低，且当前已有足够的措施保证压缩升级过程的安全性以及后续运行阶段的稳定性，更推荐使用压缩升级方式。

- 可根据自身需求选择AB面升级或者压缩升级，但选定并完成过一次相应的升级之后，不建议再切换到另外一种升级方式。

# FAQ

1. uapi_upg_prepare返回错误码：ERRCODE_PARTITION_CONFIG_NOT_FOUND。

   请检查是否烧写分区表。

2. 新增了bin文件需要通过OTA进行升级，怎么扩展：

   1. upg_common_porting.c文件中 g_img_partition_map 中增加新配置，格式为 {新增镜像ID， 新增镜像分区表ID}。

      镜像ID 在upg_definitions_porting.h文件中进行定义。

   2. fota.cfg 。

   3. build_ws53_update.py修改升级包生成脚本。

   4. newbin的字段与fota.cfg中配置的需要对齐，否则打包会报错。

      可以调整不输入入参时，脚本默认打包的内容。