升级补丁与版本维护
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 重启后读取结果并做后续处理(校验、上报、清理)。
该设计的关键工程动机:
- 升级通道隔离:升级源千差万别(SD 文件、UART 流、空中 OTA),但落盘逻辑统一由 uboot 完成,SDK 只负责"把文件/数据准备好"。
- 参数传递可靠性:SDK 与 uboot 之间通过固定内存地址(
UPDATA_BEG)和带 CRC/魔数的结构体通信,避免跳转后状态丢失。 - 失败可恢复:通过
UPDATA_RESULT状态码区分参数错误、设备错误、密钥错误等,配合update_result_deal()在下次启动时补偿处理。 - 多目标扩展性:
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_UPDATA | SD 卡控制器 0/1 升级 |
PC_UPDATA | PC 工具升级 |
UART_UPDATA | UART 串口升级 |
BT_UPDATA | 经典蓝牙 OTA |
BLE_APP_UPDATA / SPP_APP_UPDATA | BLE/SPP App OTA |
DUAL_BANK_UPDATA | 双 Bank 备份升级 |
BLE_TEST_UPDATA | BLE 测试升级 |
NORFLASH_UPDATA | NorFlash 升级 |
USER_LC_FLASH_UFW_UPDATA | 用户 LC Flash UFW 升级 |
USB_HID_UPDATA | USB 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
流程要点:
- 升级发起前先遍历
update_target段,调用各通道的driver_close关闭占用外设(UART/IO 等),避免跳转 uboot 后外设状态冲突。 - 参数区填充顺序固定:类型 → 魔数 → 路径 → 私有参数 → 整体 CRC;CRC 在最后一步计算,保证覆盖全部字段。
- uboot 侧先验
magic(是否初始化)再验parm_crc(是否损坏),任一失败即回写UPDATA_PARM_ERR并复位,SDK 下次启动可感知。 - 烧写成功/失败都通过
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_ERR | uboot 校验 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
- 新增升级通道:定义
struct update_target并REGISTER_UPDATE_TARGET注册到.update_target段,框架自动遍历调用;注意UPDATA_TYPE新增枚举须遵守顺序约束。 - 私有参数注入:通过
update_mode_api_v2的priv_param_fill_hdl回调 +update_param_priv_fill()写入parm_priv[32],扩展产线/业务参数无需改框架。 - 扩展参数段:注册新的
EXT_ARG_TYPE并填充ext_arg_len/ext_arg_crc,可携带任意结构化上下文(LDO trim、跳转标志等)。 - TWS 协同策略:通过
tws_ota_control(int type, ...)与tws_ota_send_data_to_sibling()扩展双耳间的自定义同步命令/数据。 - 版本注册:新库模块用
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)中统一走失败补偿路径。