杰理 SDK 文档中心
首页
首页
  • 概述

    • SDK 概览与产品定位
    • 支持芯片平台与蓝牙认证
    • SDK 架构与目录分层
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建系统
    • 板级工程与配置
    • 烧录与固件升级工具
  • 应用工程

    • 应用选择与工程总览
    • SPP + BLE 数传应用框架
    • 透传与 AT 指令示例
    • BLE 广播/中心与定位示例
    • 2.4G 私有协议与 Dongle 示例
    • 云平台接入示例
    • HID 人机交互应用框架
    • HID 示例工程(键盘/鼠标/遥控器/手柄)
    • Bluetooth Mesh 应用框架
    • Mesh 模型与 Mesh DFU 固件升级
    • Mesh 音频编解码演示
  • 芯片平台与硬件抽象

    • 芯片平台总览与差异
    • 音频编解码与时钟管理
    • 外设驱动接口(ADC/IIC/SPI/PWM/LED/充电)
    • 芯片配置工具与下载支持
  • 蓝牙协议栈

    • 蓝牙控制器层(btctrler)
    • 蓝牙协议栈与 Profile(btstack)
    • 蓝牙模块选择与配置
  • 媒体与音频框架

    • 音频流框架
    • 音频编解码与 A2DP 媒体
    • 音频效果处理(EQ/频谱/变调/环绕/超低音)
    • 本地 TWS 与音频同步
  • 系统服务与运行时

    • 实时操作系统与任务调度
    • 消息事件机制
    • 电源管理与低功耗
    • 存储与配置系统
    • 设备驱动框架(USB/RTC)
  • 应用公共组件

    • 音频应用组件
    • 设备外设抽象(按键/触摸/传感器/存储)
    • 蓝牙公共模块与消息联动
    • 调试与配置组件
    • 杰理关键词唤醒(jl_kws)
  • 第三方协议与云平台接入

    • 杰理 RCSP 私有协议
    • 低功耗蓝牙 Mesh 方案(llsync_mesh)
    • Sig Mesh 方案
    • 涂鸦协议接入
    • 腾讯连连接入
    • 华为 HiLink 接入
  • 固件升级与维护

    • OTA 升级机制
    • 升级补丁与版本维护
    • 升级工具链(BLE OTA / USB Dongle OTA)
  • 文档与开发资源

    • 数据手册与架构文档
    • 协议与云平台开发文档
    • 常见问题与技术支持

升级补丁与版本维护

AC63 系列蓝牙 SoC 的固件升级(OTA/烧录)与版本管理体系:涵盖 SDK 与 bootloader(uboot)之间的升级参数握手协议、升级通道(UART/USB/SD/BT/双 Bank 等)分发机制、升级结果状态机、TWS 双耳协同升级,以及模块库版本注册与校验机制。本文是"固件升级"目录下的补丁发布与版本维护专项页。

Purpose and Scope

本页面向"固件升级与版本维护"这一主题,回答以下问题:

  • SDK 如何发起一次固件升级、如何把升级参数安全地传递给 uboot,并在重启后拿到升级结果?
  • 支持哪些升级通道(UPDATA_TYPE),各通道如何注册与分发?
  • 升级过程的状态机(UPDATA_RESULT / UPDATE_STATE_T)与异常码如何定义与流转?
  • TWS 双耳同时升级(OTA_TWS_SAME_TIME_ENABLE)如何协同、校验与上报?
  • 库(lib)版本如何注册、查询与一致性校验(version.h 机制)?

不覆盖的内容(留给兄弟页面): RCSP/JL 协议侧的 OTA 指令与 App 交互细节属于 rcsp_updata 相关页面;UART 升级主从机链路细节属于 UART 升级页面;具体各芯片的 flash 布局与烧录工具属于对应芯片平台页面。本页聚焦补丁/升级的框架协议、版本维护机制与通用流程。

Overview

在 AC63_BT_SDK 中,固件升级被设计为 SDK 与 bootloader(uboot)协作 的机制:SDK 负责探测升级源(SD 卡、UART、USB、蓝牙等)、构造升级参数区 UPDATA_PARM,然后跳转到 uboot;uboot 根据参数区完成擦写并回写结果;SDK 重启后读取结果并做后续处理(校验、上报、清理)。

该设计的关键工程动机:

  1. 升级通道隔离:升级源千差万别(SD 文件、UART 流、空中 OTA),但落盘逻辑统一由 uboot 完成,SDK 只负责"把文件/数据准备好"。
  2. 参数传递可靠性:SDK 与 uboot 之间通过固定内存地址(UPDATA_BEG)和带 CRC/魔数的结构体通信,避免跳转后状态丢失。
  3. 失败可恢复:通过 UPDATA_RESULT 状态码区分参数错误、设备错误、密钥错误等,配合 update_result_deal() 在下次启动时补偿处理。
  4. 多目标扩展性:REGISTER_UPDATE_TARGET 段注册机制允许各应用(spp_and_le、hid、mesh)注入自己的升级目标驱动。

版本维护方面,SDK 提供两套机制:应用级版本号(如涂鸦协议中的 TY_APP_VER_NUM)用于 OTA 协议中的版本查询(APP_PROTOCOL_OTA_GET_APP_VERSION);库级版本注册(version.h 的 .lib_version 段)用于启动时校验各库模块版本一致性。

Architecture

flowchart TD
    subgraph sg_App["应用层 (apps)"]
        APP["app 主程序"]
        RCSP["rcsp_user_update<br/>(JL RCSP OTA)"]
        TWS["update_tws<br/>(TWS 协同 OTA)"]
        CFG["lib_update_config.c<br/>(升级配置)"]
    end

    subgraph sg_SDK["升级框架 (include_lib/update)"]
        API["update_mode_api_v2<br/>update_result_get/deal"]
        PARM["UPDATA_PARM<br/>参数区 (CRC+魔数)"]
        TARGET["update_target 段注册表<br/>REGISTER_UPDATE_TARGET"]
    end

    subgraph sg_Boot["bootloader (uboot)"]
        UBOOT["uboot 升级执行"]
        FLASH[("Flash 烧写")]
    end

    subgraph sg_Ch["升级通道"]
        CH1["UART_UPDATA"]
        CH2["SD0/SD1_UPDATA"]
        CH3["BT/BLE/SPP_UPDATA"]
        CH4["DUAL_BANK_UPDATA"]
        CH5["USB/PC_UPDATA"]
    end

    APP --> API
    RCSP --> API
    TWS --> API
    CFG --> API
    API --> PARM
    API --> TARGET
    TARGET --> CH1
    TARGET --> CH2
    TARGET --> CH3
    TARGET --> CH4
    TARGET --> CH5
    PARM -->|"跳转 + 参数区"| UBOOT
    UBOOT --> FLASH
    FLASH -->|"回写结果"| PARM

架构说明:

  • API 层(update_mode_api_v2 等,见 update.h):统一入口,按 UPDATA_TYPE 分发到对应通道;负责填充 UPDATA_PARM、注册私有参数回调、最终跳转。
  • 参数区 UPDATA_PARM(见 update.h):parm_type 标记升级方式,parm_result 由 uboot 回写,magic=0x5441 做合法性校验,parm_crc 校验整体完整性。
  • 目标注册表 update_target:通过段(section).update_target 收集各升级通道的关闭驱动,list_for_each_update_target 遍历执行(见 update.h)。
  • uboot 协作:SDK 跳转前把 UPDATA_PARM 写到 UPDATA_BEG 附近固定地址,uboot 据此执行烧写,完成后回写 parm_result(UPDATA_RESULT 枚举值)。

升级参数协议(SDK ↔ uboot)

升级的核心是 参数区 + 固定地址 + 魔数校验 三方协作,定义于 update.h:

extern u32 UPDATA_BEG;

#define UPDATA_FLAG_ADDR		((void *)((u32)&UPDATA_BEG + 0x08))
#define BOOT_STATUS_ADDR		((void *)((u32)&UPDATA_BEG)) //预留8个bytes

#define UPDATA_MAGIC            (0x5A00)        //防止CRC == 0 的情况

设计意图:UPDATA_BEG 是链接脚本中预留的固定 RAM/共享区符号,BOOT_STATUS_ADDR 前 8 字节留给 boot 状态,UPDATA_FLAG_ADDR 存放升级标志;UPDATA_MAGIC = 0x5A00 特意选择非 0 值,注释点明其作用是 防止 CRC == 0 的误判——若 CRC 恰好为 0,容易与"未初始化/空数据"混淆,魔数参与校验可消除这一边界歧义。

UPDATA_TYPE:升级方式枚举

升级通道被编码为连续枚举值(见 update.h),从 0x5A00 起连续递增,且注释明确要求 已有定义不可调整顺序、新方式必须加在 USER_NORFLASH_UFW_UPDATA 之前——这是因为 parm_type 作为跨 SDK/uboot 的二进制协议值,一旦乱序会破坏新旧固件互操作:

枚举值含义
USB_UPDATA (0x5A00)USB 升级
SD0_UPDATA / SD1_UPDATASD 卡控制器 0/1 升级
PC_UPDATAPC 工具升级
UART_UPDATAUART 串口升级
BT_UPDATA经典蓝牙 OTA
BLE_APP_UPDATA / SPP_APP_UPDATABLE/SPP App OTA
DUAL_BANK_UPDATA双 Bank 备份升级
BLE_TEST_UPDATABLE 测试升级
NORFLASH_UPDATANorFlash 升级
USER_LC_FLASH_UFW_UPDATA用户 LC Flash UFW 升级
USB_HID_UPDATAUSB HID 升级
USER_NORFLASH_UFW_UPDATA用户 NorFlash UFW 升级
NON_DEV (0xFFFF)无设备

UPDATA_PARM:参数区结构

参数区采用 固定 112 字节布局(USE_SDFILE_NEW 版本,8+64+8+32),带 CRC 与魔数双重校验(见 update.h):

#define UPDATE_PARAM_MAGIC		0x5441
typedef struct _UPDATA_PARM {
    u16 parm_crc;
    u16 parm_type;              //UPDATA_TYPE:sdk pass parm to uboot
    u16 parm_result;            //UPDATA_TYPE:uboot return result to sdk
    u16 magic;					//0x5441
    //8byte
    union {
        struct {
            u8  file_path[32];         //updata file path
        };
        struct {
            u8  file_patch[32];         //updata file path
        };
    };
    u8  parm_priv[32];          //sd updata
    //64byte
    u32 ota_addr;
    u16 ext_arg_len;
    u16 ext_arg_crc;
    //8 byte
} UPDATA_PARM;

字段职责与设计权衡:

  • parm_crc:SDK 填充时计算,uboot 读取时校验,防止参数区在跳转/复位过程中被破坏。
  • parm_type:下行(SDK → uboot),告诉 uboot 用哪种方式升级(对应 UPDATA_TYPE)。
  • parm_result:上行(uboot → SDK),回写 UPDATA_RESULT 枚举,SDK 重启后通过 update_result_get() 读取。
  • magic:固定 0x5441(UPDATE_PARAM_MAGIC),标记参数区已初始化,避免把垃圾数据当参数。
  • file_path/file_patch:联合体,同一 32 字节承载升级文件路径(SD 升级场景)。
  • parm_priv[32]:私有参数区,配合 update_param_priv_fill() 填充(如 LDO trim 值等产线参数)。
  • ota_addr + ext_arg_len + ext_arg_crc:OTA 地址与扩展参数段(带独立 CRC),用于需要携带额外上下文(如 EXT_LDO_TRIM_RES、EXT_JUMP_FLAG)的场景:
enum EXT_ARG_TYPE {
    EXT_LDO_TRIM_RES = 0,
    EXT_JUMP_FLAG,
    EXT_TYPE_MAX = 0xff,
};

struct ext_arg_t {
    u8 type;
    u8 len;
    u8 *data;
};

非 SDFILE_NEW 的旧布局更精简(无 magic/ota_addr/扩展参数),说明该结构随 SDK 演进逐步加固——新增字段均以"CRC + 魔数"方式保护,体现了跨组件共享内存协议对可靠性的高要求。

升级结果状态机

UPDATA_RESULT 从 UPDATA_MAGIC 起始编码,保证"未升级"与"升级完成"状态可区分(见 update.h):

typedef enum {
    UPDATA_NON = UPDATA_MAGIC,   //0x5A00 初始/无升级
    UPDATA_READY,                //0x5A01 就绪
    UPDATA_SUCC,                 //0x5A02 成功
    UPDATA_PARM_ERR,             //0x5A03 参数错误
    UPDATA_DEV_ERR,              //0x5A04 设备错误
    UPDATA_KEY_ERR,              //0x5A05 密钥/校验错误
} UPDATA_RESULT;

状态流:UPDATA_NON → UPDATA_READY → UPDATA_SUCC(正常路径);异常分支落在 PARM_ERR / DEV_ERR / KEY_ERR。SDK 侧通过以下 API 管理结果(见 update.h):

  • update_result_get():读取 uboot 回写的结果。
  • update_result_set(u16 result):主动写入结果(如 TWS 从机上报场景)。
  • update_result_deal():启动时处理上次升级结果(补偿/上报/清理)。
  • update_clear_result():清除结果,恢复 UPDATA_NON。
  • update_success_boot_check():校验本次启动是否为升级成功后的首次启动。
  • device_is_first_start():判断是否首次启动(产线场景)。

升级入口与目标注册机制

update_mode_api_v2() 是统一升级入口(见 update.h):

void update_mode_api_v2(UPDATA_TYPE type,
                        void (*priv_param_fill_hdl)(UPDATA_PARM *p),
                        void (*priv_update_jump_handle)(int type));

三个参数分别控制:升级通道选择、私有参数填充回调(跳转前把业务自定义参数写入 UPDATA_PARM)、跳转后回调。这种"回调注入"设计让升级框架保持通用,具体应用的定制逻辑(如 RCSP 的 OTA 指令、产线参数)通过回调挂载,无需修改框架本体。

升级目标通过 段注册宏 收集(见 update.h):

struct update_target {
    char *name;
    update_handler_t driver_close;
};

#define REGISTER_UPDATE_TARGET(target) \
        const struct update_target target sec(.update_target)

extern const struct update_target update_target_begin[];
extern const struct update_target update_target_end[];

#define list_for_each_update_target(p) \
    for (p = update_target_begin; p < update_target_end; p++)

REGISTER_UPDATE_TARGET 把每个升级目标(如 UART 从机、测试盒)的"关闭驱动"函数指针放入专用段 .update_target;框架通过 update_target_begin/end 边界遍历全部注册项。这是典型的 编译期注册表(linker section registry) 模式:新增升级目标只需定义结构体并用宏注册,无需改动框架分发代码,各应用目录(apps/spp_and_le、apps/hid、apps/mesh 下的 lib_update_config.c)据此配置本应用的升级能力。

配套的升级状态枚举 UPDATE_STATE_T(UPDATE_TASK_INIT → UPDATE_CH_INIT → UPDATE_CH_SUCESS_REPORT → UPDATE_CH_EXIT,见 update.h)描述了一次升级任务从初始化、通道初始化、成功上报到退出的生命周期。

TWS 双耳协同升级(OTA_TWS_SAME_TIME_ENABLE)

TWS 场景下,双耳需同时完成 OTA 并保持一致版本。apps/common/include/update_tws.h 定义了完整的协同协议(仅在 OTA_TWS_SAME_TIME_ENABLE && (RCSP_ADV_EN || AI_APP_PROTOCOL) && !OTA_TWS_SAME_TIME_NEW 条件下编译,见 update_tws.h)。

事件与状态定义

主/从机通过自定义系统事件 SYS_BT_OTA_EVENT_TYPE_STATUS('O'<<24|'T'<<16|'A'<<8|'\0',即 "OTA\0")交互(见 update_tws.h):

enum {   //OTA 总体状态
    OTA_OVER = 0,
    OTA_INIT,
    OTA_START,
    OTA_VERIFY_ING,
    OTA_VERIFY_END,
    OTA_SUCC,
};

enum {   //OTA 控制命令
    OTA_START_UPDATE = 0,
    OTA_START_UPDATE_READY,
    OTA_START_VERIFY,
    OTA_UPDATE_OVER,
    OTA_UPDATE_ERR,
    OTA_UPDATE_SUCC,
};

控制面支持 SET/GET 语义(OTA_TYPE_SET/GET、OTA_STATUS_SET/GET、OTA_REMOTE_STATUS_SET/GET、OTA_RESULT_SET/GET),用于双耳同步类型、状态、对端状态与结果(见 update_tws.h)。停止原因单独枚举(OTA_STOP_APP_DISCONNECT、OTA_STOP_LINK_DISCONNECT、OTA_STOP_UPDATE_OVER_SUCC、OTA_STOP_UPDATE_OVER_ERR、OTA_STOP_PHONE,见 update_tws.h),便于区分是链路断开、升级完成还是手机主动取消。

双耳同步命令与接口

主从机通过 RF 链路同步关键动作(见 update_tws.h):

enum {
    SYNC_CMD_START_UPDATE,   //同步开始升级
    SYNC_CMD_START_VERIFY,   //同步开始校验
    SYNC_CMD_UPDATE_OVER,    //同步升级结束
    SYNC_CMD_UPDATE_ERR,     //同步升级出错
};

对外接口(见 update_tws.h):

  • tws_ota_init() / tws_ota_close():模块生命周期管理。
  • tws_ota_open(struct __tws_ota_para *para):携带升级参数打开一次 TWS OTA。
  • tws_ota_stop(u8 reason):按原因停止。
  • tws_ota_enter_verify() / tws_ota_exit_verify():进入/退出固件校验阶段。
  • tws_ota_updata_boot_info_over():双 Bank 场景下烧录 boot 信息完成回调。
  • tws_ota_data_send_m_to_s(buf, len):主→从数据转发。
  • tws_ota_sync_cmd(reason):向对端同步命令。
  • tws_ota_control(int type, ...):可变参数控制口(SET/GET 语义的统一入口)。
  • tws_ota_send_data_to_sibling(opcode, data, len) / tws_ota_get_data_from_sibling():对端数据收发。
  • bt_ota_event_handler(struct bt_event *bt):挂接蓝牙事件驱动 OTA 流程。

设计要点:双耳必须同进同退——任一耳校验失败即通过 SYNC_CMD_UPDATE_ERR 通知对端回滚/重试,避免双耳版本分裂;OTA_REMOTE_STATUS_* 命令使每侧都能感知对端实时状态,为"先升级一只、校验成功后升级另一只"或"同时升级"策略提供基础。

版本维护机制(version.h)

include_lib/system/generic/version.h 提供 库模块版本注册与校验 框架。每个库模块定义一个版本查询函数,并通过宏注册到专用段 .lib_version(见 version.h):

typedef int (*version_t)(int);

const version_t __version_##module  \
        __attribute__((section(".lib_version"),used)) = module##_version

宏展开后,形如 __version_btstack 的指针变量被放入 .lib_version 段;框架通过段边界 lib_version_begin[] / lib_version_end[] 遍历全部注册项(见 version.h),并提供类似下面的遍历校验宏(见 version.h):

version_t *version; \
    ... \
    log_i("=========version check===========\n"); \

这背后的工程动机:SDK 由大量预编译库(如 cpu/br25/liba/update.a 等,各芯片平台的 liba 目录)组成,预编译库与应用代码之间最容易出现版本错配。把版本指针集中到 .lib_version 段,启动时统一遍历比对,可以在运行期第一时间暴露 ABI 不兼容,而不是等崩溃后排查。

应用侧版本号则直接以宏定义,如涂鸦协议中(见 tuya_ble_app_demo.h):

//固件版本
#define TY_APP_VER_NUM       0x0100
#define TY_APP_VER_STR	     "1.0"

数字版本号(0x0100 = 1.0)与字符串版本("1.0")并存,前者用于二进制比较/协议字段,后者用于展示与日志。蓝牙协议侧,版本查询被定义为协议事件之一 APP_PROTOCOL_OTA_GET_APP_VERSION(见 app_protocol_event.h),与 APP_PROTOCOL_OTA_CHECK、APP_PROTOCOL_OTA_CHECK_CRC 并列,构成 OTA 前的版本协商流程:App 先查版本、再检查固件、最后校验 CRC 才发起升级。

Core Flow:一次典型补丁升级的完整时序

sequenceDiagram
    participant APP as 应用/协议层
    participant SDK as 升级框架(update)
    participant TGT as 升级目标(update_target)
    participant PARM as UPDATA_PARM 区
    participant UBOOT as uboot
    participant FLASH as Flash

    APP->>SDK: update_mode_api_v2(type, fill_hdl, jump_hdl)
    SDK->>SDK: 遍历 list_for_each_update_target 关闭驱动
    SDK->>TGT: driver_close()
    SDK->>PARM: 填 parm_type/magic/crc/file_path
    SDK->>SDK: priv_param_fill_hdl(&parm) 注入私有参数
    SDK->>PARM: 计算 parm_crc + ext_arg_crc
    SDK->>UBOOT: 跳转 uboot(参数区地址)
    UBOOT->>PARM: 校验 magic + crc
    alt 校验失败
        UBOOT->>PARM: parm_result = UPDATA_PARM_ERR
    else 校验通过
        UBOOT->>FLASH: 按 parm_type 擦写固件
        FLASH-->>UBOOT: 烧写结果
        UBOOT->>PARM: parm_result = UPDATA_SUCC / DEV_ERR / KEY_ERR
    end
    UBOOT->>UBOOT: 复位重启
    SDK->>PARM: update_result_get() 读取结果
    SDK->>SDK: update_result_deal() 处理/上报/清理
    SDK->>PARM: update_clear_result() 恢复 UPDATA_NON

流程要点:

  1. 升级发起前先遍历 update_target 段,调用各通道的 driver_close 关闭占用外设(UART/IO 等),避免跳转 uboot 后外设状态冲突。
  2. 参数区填充顺序固定:类型 → 魔数 → 路径 → 私有参数 → 整体 CRC;CRC 在最后一步计算,保证覆盖全部字段。
  3. uboot 侧先验 magic(是否初始化)再验 parm_crc(是否损坏),任一失败即回写 UPDATA_PARM_ERR 并复位,SDK 下次启动可感知。
  4. 烧写成功/失败都通过 parm_result 持久化,SDK 重启后 update_result_deal() 依据结果决定上报成功、提示重试或清理残留。

Usage Examples

1. 升级参数私有填充接口

SDK 提供通用私有参数填充函数,供各通道向 UPDATA_PARM 注入业务数据(见 update.h):

void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type));
void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len);
u16 update_result_get(void);
bool device_is_first_start();
int update_result_deal();
void update_result_set(u16 result);
void update_clear_result();
bool update_success_boot_check(void);

典型调用序列:update_mode_api_v2(UART_UPDATA, my_fill, my_jump) 中 my_fill 内调用 update_param_priv_fill(&parm, priv, len) 把产线参数写入 parm_priv[32];my_jump 在跳转前做最后的资源释放。

2. TWS OTA 控制与结果处理

TWS 协同升级的控制入口与事件处理接口(见 update_tws.h):

int tws_ota_init(void);
int tws_ota_close(void);
int tws_ota_open(struct __tws_ota_para *para);
void tws_ota_stop(u8 reason);
u16 tws_ota_enter_verify(void *priv);
u16 tws_ota_exit_verify(u8 *res, u8 *up_flg);
u16 tws_ota_updata_boot_info_over(void *priv);
int tws_ota_err_callback(u8 reason);
int tws_ota_data_send_m_to_s(u8 *buf, u16 len);
int tws_ota_sync_cmd(int reason);
u8 tws_ota_control(int type, ...);
void tws_ota_send_data_to_sibling(u8 opcode, u8 *data, u8 len);

tws_ota_enter_verify 返回 u16(校验阶段状态),tws_ota_exit_verify 通过 res/up_flg 输出校验结果与升级标志,供主耳决策是否继续;tws_ota_sync_cmd(SYNC_CMD_START_VERIFY) 可让双耳同时进入校验。

3. 版本注册与查询

库模块用宏注册版本查询函数(见 version.h),应用侧用宏定义版本号(见 tuya_ble_app_demo.h):

#define TY_APP_VER_NUM       0x0100
#define TY_APP_VER_STR	     "1.0"

TY_APP_VER_NUM(数值 0x0100 = 1.0)用于协议字段比较,TY_APP_VER_STR 用于日志与显示;两者必须同步维护,是版本维护的基本纪律。

Configuration Options

配置项类型默认/取值说明
OTA_TWS_SAME_TIME_ENABLE宏开关0/1使能 TWS 双耳同时 OTA(update_tws.h)
OTA_TWS_SAME_TIME_NEW宏开关0/1切换新旧版 TWS OTA 实现;为 1 时跳过旧实现编译
RCSP_ADV_EN / AI_APP_PROTOCOL宏开关0/1决定 TWS OTA 代码是否参与编译(与 App 协议使能相关)
USE_SDFILE_NEW宏开关0/1选择新/旧版 UPDATA_PARM 布局(新布局含 magic、ota_addr、扩展参数)
UPDATE_PRIV_PARAM_LEN常量32私有参数区长度(bytes),对应 parm_priv[32]
UPDATA_MAGIC常量0x5A00升级标志魔数,防 CRC==0 误判
UPDATE_PARAM_MAGIC常量0x5441参数区初始化魔数
EXT_LDO_TRIM_RES / EXT_JUMP_FLAG枚举0 / 1扩展参数类型:LDO 校准结果、跳转标志
TY_APP_VER_NUM / TY_APP_VER_STR宏0x0100 / "1.0"应用固件版本号(数值 + 字符串,须同步维护)
UPDATA_SD(control_type/io/baud 等)结构体—SD 升级通道的控制器、IO、检测方式、超时等参数
UPDATA_UART(tx/rx/baud/timeout)结构体—UART 升级通道的 IO 对接、波特率、超时(单位 10ms)

各应用目录(apps/spp_and_le/config/lib_update_config.c、apps/hid/config/lib_update_config.c、apps/mesh/lib_config/lib_update_config.c)为每个产品配置实际启用的升级通道与参数。

Failure Modes, Edge Cases & Concurrency

升级结果错误码

错误码触发场景处理建议
UPDATA_PARM_ERRuboot 校验 magic/CRC 失败,或 parm_type 非法检查参数区布局是否与 uboot 版本匹配(新旧 UPDATA_PARM 不可混用);重新发起升级
UPDATA_DEV_ERR烧写设备(Flash 等)异常检查 Flash 器件与供电;双 Bank 场景检查备份区
UPDATA_KEY_ERR固件签名/密钥校验失败确认固件包与芯片密钥匹配,防止刷入非法/错配固件

边界与并发关注点

  • CRC==0 歧义:UPDATA_MAGIC=0x5A00 专门防止"CRC 恰好为 0"被误判为未升级,任何新增校验逻辑都应保持该约定。
  • 枚举顺序冻结:UPDATA_TYPE 的取值是跨 SDK/uboot 的二进制协议,不可调整已有顺序,新增通道必须加在 USER_NORFLASH_UFW_UPDATA 之前(见 update.h),否则旧 uboot 会误解新 parm_type。
  • TWS 并发一致性:双耳同时烧写时,任何一耳失败必须通过 SYNC_CMD_UPDATE_ERR 通知对端,避免版本分裂;OTA_STOP_* 原因区分链路断开/完成/手机取消,决定是否允许后续重连续传。
  • 重启窗口:跳转 uboot 前必须调用 driver_close() 关闭外设,否则复位后外设状态可能干扰 uboot 的 IO 检测(尤其 SD/UART 通道共用 IO 时)。
  • 首次启动判定:device_is_first_start() 与 update_success_boot_check() 用于产线/OTA 后的首次启动识别,业务代码应在升级完成后正确处理,避免重复初始化或漏初始化。
  • 预编译库版本错配:.lib_version 段集中注册各库版本,启动校验不通过时应直接定位到版本不一致的库,而不是带病运行。

Performance & Operational Considerations

  • 升级帧率与波特率:UART 通道通过 UPDATA_UART.control_baud/control_timeout 配置(超时单位 10ms),UPDATA_SD.max_data_baud 配置 SD 读取速率;产线升级应在保证稳定性的前提下提高波特率以缩短工时。
  • 参数区写回是持久化:parm_result 依赖复位后仍有效的存储区,任何对 UPDATA_BEG 布局的修改都必须与 uboot 同步发布,属于 双端耦合变更,应走版本绑定发布。
  • 升级期间功耗与连接:空中 OTA(BT/BLE/SPP)期间应维持射频连接并防止进入低功耗休眠;TWS 场景需保证双耳链路质量,链路断开按 OTA_STOP_LINK_DISCONNECT 处理并支持重试。
  • 双 Bank 优势:DUAL_BANK_UPDATA + tws_ota_updata_boot_info_over() 支持备份区回滚,是降低 OTA 变砖风险的主要手段;升级完成须烧录 boot 信息(dual_bank_update_burn_boot_info_callback)才能切换启动。

Extension Points

  1. 新增升级通道:定义 struct update_target 并 REGISTER_UPDATE_TARGET 注册到 .update_target 段,框架自动遍历调用;注意 UPDATA_TYPE 新增枚举须遵守顺序约束。
  2. 私有参数注入:通过 update_mode_api_v2 的 priv_param_fill_hdl 回调 + update_param_priv_fill() 写入 parm_priv[32],扩展产线/业务参数无需改框架。
  3. 扩展参数段:注册新的 EXT_ARG_TYPE 并填充 ext_arg_len/ext_arg_crc,可携带任意结构化上下文(LDO trim、跳转标志等)。
  4. TWS 协同策略:通过 tws_ota_control(int type, ...) 与 tws_ota_send_data_to_sibling() 扩展双耳间的自定义同步命令/数据。
  5. 版本注册:新库模块用 version.h 的宏注册版本函数到 .lib_version 段,自动纳入启动版本校验。

Related Links

  • update.h(升级框架核心 API)
  • update_tws.h(TWS 双耳 OTA 协同)
  • update_tws_new.h(新版 TWS OTA)
  • rcsp_user_update.h(JL RCSP 协议 OTA 应用)
  • uart_update.h(UART 升级接口)
  • update_loader_download.h(Loader 下载接口)
  • version.h(库版本注册与校验框架)
  • app_protocol_event.h(OTA 协议事件,含版本查询)
  • 相关升级实现:apps/common/update/update.c、apps/common/update/uart_update.c、apps/common/update/uart_update_master.c、apps/common/update/testbox_update.c

API Reference

以下 API 均来自 update.h 与 update_tws.h,为 SDK 升级框架对外暴露的主要接口。

void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type))

统一升级入口:按类型分发到对应通道,填充参数区并执行跳转。

参数:

  • type (UPDATA_TYPE):升级通道,见 UPDATA_TYPE 枚举(0x5A00 起)。
  • priv_param_fill_hdl (函数指针):跳转前回调,用于向 UPDATA_PARM 注入私有参数;可为 NULL。
  • priv_update_jump_handle (函数指针):跳转相关回调;可为 NULL。

返回: 无。

说明: 跳转前框架会遍历 .update_target 段调用各 driver_close 关闭升级通道占用资源。

void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len)

向参数区 parm_priv[32] 填充私有数据(产线参数等)。

参数:

  • p (UPDATA_PARM *):目标参数区。
  • priv (void *):私有数据源。
  • priv_len (u16):数据长度,不得超过 UPDATE_PRIV_PARAM_LEN(32)。

返回: 无。

u16 update_result_get(void)

读取 uboot 回写的升级结果。

返回: UPDATA_RESULT 枚举值(UPDATA_NON/READY/SUCC/PARM_ERR/DEV_ERR/KEY_ERR)。

void update_result_set(u16 result)

主动写入升级结果(如 TWS 从机向主耳上报结果时)。

参数:

  • result (u16):UPDATA_RESULT 枚举值。

返回: 无。

int update_result_deal(void)

启动时处理上次升级结果:上报、补偿或清理。建议在系统启动早期调用。

返回: 处理结果(0 表示正常;非 0 表示异常,调用方按业务处理)。

void update_clear_result(void)

清除升级结果,恢复 UPDATA_NON 初始态。升级流程结束后调用,防止重复处理。

返回: 无。

bool device_is_first_start(void)

判断设备是否为首次启动(产线/出厂场景)。

返回: true 表示首次启动。

bool update_success_boot_check(void)

校验本次启动是否为"升级成功后的首次启动"。

返回: true 表示升级成功后的首次启动。

TWS 协同升级接口(见 update_tws.h)

函数说明
int tws_ota_init(void) / int tws_ota_close(void)TWS OTA 模块初始化/反初始化
int tws_ota_open(struct __tws_ota_para *para)携带升级参数打开 TWS OTA
void tws_ota_stop(u8 reason)按原因停止(OTA_STOP_* 枚举)
u16 tws_ota_enter_verify(void *priv)进入固件校验阶段,返回校验状态
u16 tws_ota_exit_verify(u8 *res, u8 *up_flg)退出校验,res 输出结果、up_flg 输出升级标志
u16 tws_ota_updata_boot_info_over(void *priv)双 Bank 场景 boot 信息烧录完成回调
int tws_ota_err_callback(u8 reason)错误回调,reason 为错误原因
int tws_ota_data_send_m_to_s(u8 *buf, u16 len)主耳向从耳转发升级数据
int tws_ota_sync_cmd(int reason)向对端同步命令(SYNC_CMD_* 枚举)
u8 tws_ota_control(int type, ...)可变参数控制口,SET/GET 类型/状态/结果
void tws_ota_send_data_to_sibling(u8 opcode, u8 *data, u8 len)向对端发送自定义数据帧
int tws_ota_get_data_from_sibling(u8 opcode, u8 *data, u8 len)接收对端数据帧

Throws/错误处理约定: 本 SDK 为 C 代码,不抛异常;错误通过返回值与 UPDATA_RESULT/OTA_STOP_*/OTA_UPDATE_ERR 等枚举表达。调用方应检查所有返回码,并在升级结果处理(update_result_deal)中统一走失败补偿路径。

Prev
OTA 升级机制
Next
升级工具链(BLE OTA / USB Dongle OTA)