固件升级与更新机制
本文档介绍 AD16N(GP-MCU)SDK 的固件升级与更新机制,涵盖升级文件(UFW)格式、升级通道(USB/SD/UART/BLE/NOR Flash 等)、SDK 与 uboot 之间的参数传递协议、V1/V2 两代升级库的 API 以及应用侧的触发与结果处理流程。
Purpose and Scope
本页面覆盖 AD16N SDK 中与固件升级相关的完整能力:
- 升级文件格式:
ufw_syd_head_v1、ufw_file_head_v1、jlfs_file_head等头部结构及其校验字段; - 升级协议与参数传递:
UPDATA_PARM(magic =0x5441)、UPDATA_EXT_PARM、UPDATA_RESULT结果码,以及 SDK 与 uboot 之间的握手约定; - 升级通道与类型:
UPDATA_TYPE枚举中定义的 USB、SD0/SD1、PC、UART、BT、BLE、双 bank、TestBox、NOR Flash 等全部升级入口; - V1/V2 升级库 API:
check_ufw_file、try_to_upgrade、update_mode_api_v2、app_update_init、app_update_handle等; - 应用侧集成点:
update_app()入口、DEVICE_TRY_TO_UPDATE触发宏、升级结果回读与首次启动判断。
以下主题不属于本页面范围,相关文档请参考其他页面:芯片启动与 uboot 内部实现(Boot 流程页)、FAT 文件系统挂载(存储页)、BLE 连接管理(蓝牙页)、量产测试工具协议(TestBox 页)。
Overview
固件升级是嵌入式产品生命周期管理的核心能力。AD16N SDK 将升级能力抽象为两层:
- code_v1(
update_v1.h)——面向"文件型"升级:从 SD 卡 / U 盘等可移动介质读取.ufw打包文件,解析文件头、校验 CRC、核对芯片型号,再通过参数区将升级意图传递给 uboot 执行。这是传统且最常用的升级路径,典型场景是用户把固件拷贝到 TF 卡后上电升级。 - code_v2(
update.h)——面向"通道型"升级:以UPDATA_TYPE枚举抽象出所有升级媒介(USB/UART/BT/BLE/NOR Flash/TestBox 等),通过update_mode_api_v2()统一发起,配合UPDATA_PARM(256 字节)携带文件路径、私有参数与扩展参数跳转 uboot;升级结果通过固定内存标志位回传,SDK 侧以update_result_get()/update_result_deal()读取。
两层共用一个设计哲学:SDK 只负责"准备与传递",真正的烧写动作由 uboot 完成。SDK 与 uboot 之间的契约是位于固定地址的标志位(UPDATA_FLAG_ADDR / BOOT_STATUS_ADDR)和一块 256 字节的 UPDATA_PARM 参数区,其中 parm_crc 与 magic(0x5441)用于保证参数区内容完整、可信。这种"检查-传参-跳转-回读"的四段式流程,保证了升级失败时设备可以回退到可恢复状态(如再次上电重试),而不是变砖。
Architecture
flowchart TD
subgraph sg_App["应用层 (App)"]
UpdateApp["update_app()<br/>升级应用入口"]
DeviceApp["device_app.c<br/>DEVICE_TRY_TO_UPDATE → try_to_upgrade()"]
end
subgraph sg_V1["升级库 code_v1 (update_v1.h)"]
CheckUFW["check_ufw_file()<br/>校验 UFW 文件头/CRC/芯片型号"]
TryUpgrade["try_to_upgrade()<br/>try_to_upgrade_api()"]
end
subgraph sg_V2["升级库 code_v2 (update.h)"]
ModeV2["update_mode_api_v2()<br/>通道型升级入口"]
AppHandle["app_update_init()<br/>app_update_handle()"]
TargetMgr["REGISTER_UPDATE_TARGET<br/>update_target 段注册表"]
end
subgraph sg_Contract["SDK↔uboot 契约"]
Parm["UPDATA_PARM (256B)<br/>magic=0x5441, parm_crc, parm_result"]
Flag["UPDATA_FLAG_ADDR / BOOT_STATUS_ADDR<br/>升级标志与结果"]
end
subgraph sg_Uboot["uboot 侧"]
Uboot["uboot 执行烧写"]
OTA["OTA Loader"]
end
subgraph sg_Media["升级介质"]
SD[(SD Card / U-Disk)]
Flash[(NOR Flash)]
Chan["UART / BT / BLE / TestBox / PC"]
end
UpdateApp --> CheckUFW
UpdateApp --> ModeV2
DeviceApp --> TryUpgrade
TryUpgrade --> CheckUFW
CheckUFW --> SD
ModeV2 --> AppHandle
ModeV2 --> TargetMgr
ModeV2 --> Chan
ModeV2 --> Flash
AppHandle --> Parm
TryUpgrade --> Parm
Parm --> Flag
Flag --> Uboot
Uboot --> OTA
OTA --> Flash
架构说明:应用层通过两条路径进入升级库——update_app() / device_app.c 的 DEVICE_TRY_TO_UPDATE 宏走 V1 文件型路径(try_to_upgrade → check_ufw_file),或直接调用 V2 通道型入口 update_mode_api_v2()。无论哪条路径,最终都通过 UPDATA_PARM 与固定内存标志位把升级指令交给 uboot;uboot 完成烧写后将 parm_result 写回标志区,SDK 重启后据此判断成功或失败。REGISTER_UPDATE_TARGET 允许各模块把自己的 update_target{name, driver_close} 注册到 .update_target 链接段,升级前统一关闭相关驱动(如 BLE 连接),避免升级过程中外设干扰。
核心数据模型
UFW 打包文件格式(code_v1)
V1 升级以 .ufw 文件为载体。文件由"总头部 + 若干子文件头 + 数据区"构成。总头部 ufw_syd_head_v1 描述整个固件包:
struct ufw_syd_head_v1 {
u16 Crc; // crc16 for this struct
u16 CrcOfSydFileHead;
u32 FileLength; // file length
u16 FileCount; // 此FW文件中包含的子文件个数
u16 Version;
u16 HeadAlignmentSize;
u16 Res;
char szChipName[16]; // 此FW文件对应的芯片类型,如AC690X,AC691X
u32 Res2[4];
u32 Res3[4];
} _GNU_PACKED_;
Source: update_v1.h
Crc 是对本结构体自身的 CRC16 校验,CrcOfSydFileHead 则校验文件头区域;szChipName 用于防呆——固件包与当前芯片型号不符时直接拒绝升级。FileCount 表明包内包含的烧写对象个数(如 app、loader、资源分区等)。
每个子文件由 ufw_file_head_v1 描述:
struct ufw_file_head_v1 {
u8 FileType; // 文件类型
u8 Res; // 保留
u16 Index; // 文件索引号
u16 Crc; // 明文数据的校验码
u16 Version; // 本结构体版本
u32 Addr; // 地址
u32 Length; // 数据长度
u32 AllLength; // 数据长度+尾部对齐的数据长度
u32 EncryptedAddr; // 加密数据的地址偏移(相对u32Addr的地址)
u32 EncryptedLength; // 加密的数据长度
union {
// 当文件类型是FILE_TYPE_FW_RESERVE_ZONE_FILE有效
struct {
u32 ReserveZoneAddress;
u32 ReserveZoneLength;
char szReserveZoneName[12];
} _GNU_PACKED_;
struct {
u32 Res0; // 保留
u32 Res1[4]; // 保留
u32 Res2[4]; // 保留
} _GNU_PACKED_;
};
char name[16];
} _GNU_PACKED_;
Source: update_v1.h
设计要点:Addr/Length 给出烧写目标地址与长度,EncryptedAddr/EncryptedLength 支持加密固件(密文相对 Addr 的偏移);union 成员在文件类型为 FILE_TYPE_FW_RESERVE_ZONE_FILE(保留分区文件)时携带保留区地址、长度与名称。_GNU_PACKED_ 保证结构体在交叉编译下按 1 字节对齐,头部布局与上位机打包工具严格一致。
另外还有 struct jlfs_file_head(head_crc/data_crc/addr/len/attr/index/name),用于 JLFS 文件系统场景下的升级文件定位,以及 struct data_info(addr/len/run_addr)用于描述运行地址信息。
SDK 与 uboot 的参数契约:UPDATA_PARM
V1 与 V2 都依赖 UPDATA_PARM 在 SDK 与 uboot 之间传递升级意图。V2 版本(256 字节,UPDATA_PARM_SIZE)如下:
#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;
Source: update.h
parm_crc:参数区校验和,防止参数被破坏;parm_type:SDK 传给 uboot 的UPDATA_TYPE通道号;parm_result:uboot 回写给 SDK 的UPDATA_RESULT结果码(方向相反,构成双向通道);magic:固定0x5441(UPDATE_PARAM_MAGIC),uboot 据此判断参数区有效;file_path/file_patch:升级文件路径(SD 卡升级场景);parm_priv:32 字节私有参数区,配合update_param_priv_fill()填充;ota_addr:OTA 地址;ext_arg_len/ext_arg_crc:扩展参数长度与校验。
V1 版本(update_v1.h 中的 struct UPDATA_PARM)结构相近,额外包含 ota_loader_patch[32](SD 升级用),并配套 struct UPDATA_EXT_PARM 携带 SD 控制器 IO 与速率、PORTA/PORTB 的 IO 上下拉/驱动配置,用于 uboot 在升级前恢复 SD 卡的物理 IO 状态。
扩展参数通过 ext_arg_t{type, len, data} 三元组组织,EXT_ARG_TYPE 枚举定义了 EXT_LDO_TRIM_RES(LDO 校准)、EXT_JUMP_FLAG(跳转标志)、EXT_KEEP_ROMIO_INFO(保留 ROM IO 信息)等类型。
升级标志位与结果码
#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 的情况
Source: update.h
UPDATA_BEG/UPDATA_SIZE 是链接脚本导出的符号(见 update.ld),BOOT_STATUS_ADDR 指向升级区起始处,UPDATA_FLAG_ADDR 指向其后 8 字节处。UPDATA_MAGIC(0x5A00)特意取非零值,避免"CRC 恰好为 0"与"未初始化内存"混淆。
结果码枚举:
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;
Source: update.h
UPDATA_NON 与 UPDATA_MAGIC 同值,表示"无升级请求";UPDATA_READY 表示参数已就绪;UPDATA_SUCC 表示成功;UPDATA_PARM_ERR/UPDATA_DEV_ERR/UPDATA_KEY_ERR 分别表示参数错误、设备错误、密钥/校验错误。V1 的 try_to_upgrade() 则返回另一组错误码(见下文 API 参考)。
升级类型与通道(UPDATA_TYPE)
typedef enum {
USB_UPDATA = UPDATA_MAGIC, //0x5A00
SD0_UPDATA, //0x5A01
SD1_UPDATA,
PC_UPDATA,
UART_UPDATA,
BT_UPDATA,
BLE_APP_UPDATA,
SPP_APP_UPDATA,
DUAL_BANK_UPDATA,
BLE_TEST_UPDATA,
NORFLASH_UPDATA,
TESTBOX_UART_UPDATA,
TESTBOX_UART_UPDATA_2,
//NOTE:以上的定义不要调整,新升级方式在此添加,注意加在USER_NORFLASH_UFW_UPDATA之前;
USER_NORFLASH_UFW_UPDATA,
USER_LC_FLASH_UFW_UPDATA,
USB_HID_UPDATA,
NON_DEV = 0xFFFF,
} UPDATA_TYPE;
Source: update.h
枚举值即 parm_type 的实际取值,从 0x5A00 连续递增,NON_DEV = 0xFFFF 表示无效设备。注意头文件中的约束注释:既有取值顺序不可调整(uboot 侧按数值分发),新增升级方式必须加在 USER_NORFLASH_UFW_UPDATA 之前,这是 SDK 与 uboot 二进制兼容的硬性要求。
配套的硬件/参数描述结构包括:
UPDATA_SD:SD 控制器选择(SD_CONTROLLER_0/1)、IO 方案(SD0_IO_A~SD0_IO_F)、在线检测方式、最大波特率、超时、io_det_func检测回调、供电控制等;UPDATA_UART:control_io_tx/control_io_rx/control_baud/control_timeout(超时单位 10ms),共 12 字节;UPDATE_UDISK:U 盘场景的 LDO trim 参数。
另外 UPGRADE_TYPE 枚举(UPGRADE_USB_HARD_KEY、UPGRADE_USB_SOFTKEY、UPGRADE_UART_SOFT_KEY、UPGRADE_UART_ONE_WIRE_HARD_KEY)描述进入升级模式的触发方式:硬按键(如按住某键上电)或软指令(如 APP 内触发),用于与 UPDATA_TYPE 的通道类型配合使用。
核心流程
升级全流程(端到端)
sequenceDiagram
participant App as 应用层<br/>update_app()/device_app.c
participant V1 as code_v1<br/>try_to_upgrade()
participant FS as 文件系统<br/>(FAT/SD/U-Disk)
participant V2 as code_v2<br/>update_mode_api_v2()
participant Uboot as uboot<br/>烧写执行
App->>V1: try_to_upgrade(dev_name, ufw_path, check)
activate V1
V1->>FS: 挂载介质、打开 .ufw 文件
FS-->>V1: 文件句柄
V1->>V1: check_ufw_file() 校验总头/子文件头<br/>CRC、芯片型号、版本
alt 校验通过 (NO_ERROR)
V1->>V2: 构造 UPDATA_PARM<br/>parm_type=UPDATA_TYPE, magic=0x5441
V2->>Uboot: 写入标志位并跳转
activate Uboot
Uboot->>Uboot: 解析参数区、执行烧写
Uboot-->>V2: parm_result 写回 (UPDATA_SUCC/ERR)
deactivate Uboot
else 无需升级 (NO_NEED_UPGRADE)
V1-->>App: 跳过,正常启动
else 校验失败 (FILE_OPEN_ERROR 等)
V1-->>App: 返回错误码
end
deactivate V1
App->>V2: update_result_get()/update_result_deal()<br/>重启后回读结果
升级路径决策流程
flowchart TD
Start([升级请求]) --> Trigger{"触发方式?"}
Trigger -->|"文件型<br/>(SD/U盘/PC)"| V1Path["code_v1: try_to_upgrade / try_to_upgrade_api"]
Trigger -->|"通道型<br/>(UART/BT/BLE/NOR/TestBox)"| V2Path["code_v2: update_mode_api_v2"]
V1Path --> Check["check_ufw_file() 解析并校验 UFW 文件"]
Check --> R1{"结果?"}
R1 -->|"NO_ERROR"| Jump["构造 UPDATA_PARM<br/>写入标志区"]
R1 -->|"NO_NEED_UPGRADE"| Skip["跳过升级<br/>正常启动"]
R1 -->|"FILE_OPEN_ERROR /<br/>FAT_MOUNT_ERROR /<br/>FLASH_SIZE_ERROR 等"| Err["返回错误码给调用方"]
V2Path --> Parm["update_param_priv_fill() 填充私有参数<br/>ext_arg 扩展参数"]
Parm --> Jump
Jump --> Boot["跳转 uboot 执行烧写"]
Boot --> Result{"重启后 update_result_get()"}
Result -->|"UPDATA_SUCC"| Succ["升级成功<br/>update_result_deal()"]
Result -->|"UPDATA_PARM_ERR /<br/>UPDATA_DEV_ERR /<br/>UPDATA_KEY_ERR"| Fail["失败处理<br/>可重新进入升级"]
Result -->|"UPDATA_NON"| Normal["无升级请求<br/>正常启动"]
V1 文件型升级详解
V1 路径以 try_to_upgrade(char *dev_name, char *up_file_path, bool check) 为核心。check 参数控制两种行为:check == true 时仅执行"检查"(检查介质上的升级文件是否存在且合法,用于开机时决定是否进入升级模式),check == false 时执行完整升级。try_to_upgrade_api() 是供 API 直接调用的变体。底层 check_ufw_file() 完成 ufw_syd_head_v1 → 子文件头链的遍历,核对 szChipName 与 Crc。
应用侧集成示例(device_app.c,通过 TFG_DEV_UPGRADE_SUPPORT 宏使能):
#if TFG_DEV_UPGRADE_SUPPORT
#if defined(UPDATE_V2_EN) && (1 == UPDATE_V2_EN)
...
#define DEVICE_TRY_TO_UPDATE(device_name, ufw_file_name, check) try_to_upgrade(device_name, ufw_file_name, check)
...
err = DEVICE_TRY_TO_UPDATE((char *)device_name[update_dev], TFG_UPGRADE_FILE_NAME, check);
if ((1 == check) && (NO_ERROR == err)) {
Source: device_app.c
该代码位于 device_app.c 的 try_to_update() 中:依次遍历候选设备(device_name[update_dev]),对每个设备用固定文件名 TFG_UPGRADE_FILE_NAME 尝试升级;当 check == 1 且返回 NO_ERROR 时,说明介质上存在可用升级文件,从而通知系统进入升级流程。UPDATE_V2_EN 宏用于在 V1/V2 设备升级实现之间切换。
V2 通道型升级详解
V2 路径的统一入口是:
void update_mode_api_v2(UPDATA_TYPE type, void (*priv_param_fill_hdl)(UPDATA_PARM *p), void (*priv_update_jump_handle)(int type));
Source: update.h
调用方传入通道类型 type、私有参数填充回调 priv_param_fill_hdl(在跳转前向 UPDATA_PARM 写入私有数据,可配合 update_param_priv_fill())以及跳转处理回调 priv_update_jump_handle。app_update_init()/app_update_handle(int msg) 负责在应用消息循环中驱动升级状态机;update_enter_cb_register() 注册进入升级前的回调,common_update_before_jump_reset_handle() 统一处理跳转前的外设复位。
升级状态机
stateDiagram-v2
[*] --> UPDATE_TASK_INIT : app_update_init()
UPDATE_TASK_INIT --> UPDATE_CH_INIT : 检测到升级通道/文件
UPDATE_CH_INIT --> UPDATE_CH_SUCESS_REPORT : 升级执行完成
UPDATE_CH_SUCESS_REPORT --> UPDATE_CH_EXIT : 上报结果
UPDATE_CH_EXIT --> [*] : 复位/退出升级模式
UPDATE_CH_INIT --> UPDATE_TASK_INIT : 校验失败,重新检测
UPDATE_STATE_T 枚举(UPDATE_TASK_INIT、UPDATE_CH_INIT、UPDATE_CH_SUCESS_REPORT、UPDATE_CH_EXIT)定义了升级任务从初始化、通道初始化、成功上报到退出的生命周期。device_is_first_start() 用于区分"升级后首次启动"与普通启动,配合 update_success_boot_check() 判断是否升级成功,这是实现"升级成功后只播报一次提示音/只清一次标志"这类业务逻辑的关键。
Usage Examples
示例 1:升级应用入口
升级应用模块(mbox_flash/update_app)对外只暴露一个入口函数,整个升级逻辑(介质检测、文件校验、跳转)封装在其内部实现:
#ifndef __UPDATE_APP_H__
#define __UPDATE_APP_H__
#include "typedef.h"
void update_app(void);
#endif
Source: update_app.h
示例 2:V1 文件型升级 API
在应用层对指定设备执行升级或仅做升级检查:
u32 check_ufw_file(char *dev_name, char *up_file_path);
u32 try_to_upgrade(char *dev_name, char *up_file_path, bool check);
u32 try_to_upgrade_api(char *dev_name, char *up_file_path, bool check);
Source: update_v1.h
典型用法(参照 device_app.c 的 DEVICE_TRY_TO_UPDATE 宏)为:先以 check = true 探测介质,若返回 NO_ERROR 则进入升级模式,再以 check = false 执行真实升级。
示例 3:V2 通道型升级入口与结果回读
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);
int update_result_deal();
void update_result_set(u16 result);
void update_clear_result();
bool update_success_boot_check(void);
Source: update.h
示例 4:注册升级目标(驱动关闭回调)
升级跳转前需要关闭可能干扰烧写的驱动(如 BLE 连接)。框架提供链接段注册机制,各模块用 REGISTER_UPDATE_TARGET 宏把自己的 update_target{name, driver_close} 放到 .update_target 段,框架通过 list_for_each_update_target 遍历执行:
#define REGISTER_UPDATE_TARGET(target) \
const struct update_target target sec(.update_target)
#define list_for_each_update_target(p) \
for (p = update_target_begin; p < update_target_end; p++)
Source: update.h
get_ble_connect_handle() 可查询当前 BLE 连接句柄,供 driver_close 回调决定是否等待/断开连接后再跳转。
示例 5:设备级升级参数接口(dev_update)
void *dev_update_get_parm(int type);
u16 dev_update_check(char *logo);
Source: dev_update.h
dev_update_get_parm() 按类型获取升级参数(如 UPDATA_SD/UPDATA_UART 配置),dev_update_check() 以厂商 logo 字符串校验设备是否支持升级。
Configuration Options
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
CONFIG_UPDATE_APP_OTA_EN | const int | 1 | 使能 APP 内 OTA 升级功能 |
CONFIG_UPDATE_TESTBOX_UART_EN | const int | 1 | 使能 TestBox UART 升级通道 |
CONFIG_UPDATE_TESTBOX_BLE_EN | const int | 1 | 使能 TestBox BLE 升级通道 |
CONFIG_UPDATE_STORAGE_DEV_EN | const int | 外部声明 | 使能存储设备(SD/U盘)升级通道 |
TFG_DEV_UPGRADE_SUPPORT | 宏 | 按工程配置 | 使能设备升级检测逻辑(device_app.c) |
TFG_UPGRADE_FILE_NAME | 宏 | 按工程配置 | 升级文件固定文件名(如 update.ufw) |
UPDATE_V2_EN | 宏 | 按工程配置 | 选择 V2 设备升级实现(device_app.c) |
UPDATA_KEEP_IO_ENABLE | 宏 | 0 | 升级时是否保持 IO 功能(update.h) |
UPDATA_MAGIC | 宏 | 0x5A00 | 升级标志魔数,防 CRC=0 歧义 |
UPDATE_PARAM_MAGIC | 宏 | 0x5441 | UPDATA_PARM 参数区魔数 |
UPDATE_PRIV_PARAM_LEN | 宏 | 32 | 私有参数区长度(字节) |
UPDATA_PARM_SIZE | 宏 | 256 | UPDATA_PARM 总大小(字节) |
前三个配置项的定义见 app_config.c;其余宏与外部符号见 update.h 与 update.h。
API Reference
code_v1(update_v1.h)
u32 check_ufw_file(char *dev_name, char *up_file_path)
检查指定设备上 up_file_path 路径的 UFW 升级文件是否合法(文件头 CRC、芯片型号、版本)。
参数:
dev_name(char *):设备名(如 SD 卡设备节点);up_file_path(char *):升级文件路径。
返回: u32,NO_ERROR(0)表示合法;否则返回下列错误码之一。
错误码(enum,见 update_v1.h):
| 错误码 | 值 | 含义 |
|---|---|---|
NO_ERROR | 0 | 成功 |
SFC_OPEN_ERROR | 1 | SPI Flash 控制器打开失败 |
DEVIVE_OPEN_ERROR | 2 | 设备打开失败 |
FAT_MOUNT_ERROR | 3 | FAT 文件系统挂载失败 |
FILE_OPEN_ERROR | 4 | 文件打开失败 |
FIND_FLASH_BIN_ERROR | 5 | 未找到 Flash bin |
FIND_FLASH2_BIN_ERROR | 6 | 未找到第二 Flash bin |
NO_NEED_UPGRADE | 7 | 版本相同,无需升级 |
FIND_OTA_ERROR | 8 | 未找到 OTA 资源 |
FIND_LOADER0_ERROR | 9 | 未找到 loader0 |
FLASH_SIZE_ERROR | 10 | Flash 容量不足/不匹配 |
DEVIVE_IDX_ERROR | 11 | 设备索引错误 |
u32 try_to_upgrade(char *dev_name, char *up_file_path, bool check)
对指定设备执行升级。check = true 时仅检查,false 时执行升级。
u32 try_to_upgrade_api(char *dev_name, char *up_file_path, bool check)
try_to_upgrade 的 API 变体,供上层模块直接调用。
code_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))
以指定通道类型发起 V2 升级。
参数:
type(UPDATA_TYPE):升级通道,见UPDATA_TYPE枚举;priv_param_fill_hdl:跳转前填充私有参数的回调,可为空;priv_update_jump_handle:跳转处理回调,可为空。
u16 update_result_get(void)
返回: 当前升级结果码(UPDATA_RESULT),重启后读取标志区得到。
int update_result_deal(void)
处理升级结果(如播报提示、清理标志),返回处理状态。
void update_result_set(u16 result)
主动写入升级结果码(一般由 uboot 侧或内部使用)。
void update_clear_result(void)
清除升级结果标志,防止重复处理。
bool device_is_first_start(void)
返回: 是否为升级后的首次启动(用于区分冷启动与升级重启)。
bool update_success_boot_check(void)
返回: 本次启动是否判定为升级成功。
void update_enter_cb_register(void *callback)
注册进入升级模式前的回调(如保存现场、断开连接)。
void app_update_handle(int msg)
在应用消息循环中驱动升级状态机(对应 UPDATE_STATE_T 状态)。
int app_update_init(void)
初始化升级任务,返回初始化结果。
void update_keep_io(UPDATA_PARM *p)
根据 UPDATA_KEEP_IO_ENABLE 决定升级时是否保持 IO 功能,并将相关状态写入参数区。
u32 update_get_latch_romio(void *data)
获取锁存的 ROM IO 配置(与 EXT_KEEP_ROMIO_INFO 扩展参数配合)。
void common_update_before_jump_reset_handle(void)
跳转 uboot 前的统一外设复位处理。
int get_ble_connect_handle(void)
返回: 当前 BLE 连接句柄,供升级前判断连接状态。
dev_update.h
void *dev_update_get_parm(int type)
参数: type——设备参数类型(如 UPDATA_SD/UPDATA_UART)。
返回: 指向对应参数结构体的指针。
u16 dev_update_check(char *logo)
参数: logo——厂商/设备标识字符串。
返回: 校验结果(0 表示不支持,非 0 表示支持)。
失败模式、边界情况与并发
升级文件/介质异常
V1 错误码体系(SFC_OPEN_ERROR、DEVIVE_OPEN_ERROR、FAT_MOUNT_ERROR、FILE_OPEN_ERROR、FLASH_SIZE_ERROR 等)覆盖了从 Flash 控制器、介质枚举、文件系统挂载到文件打开的完整故障链。设计意图是让应用层能精确区分"介质不存在""文件损坏""容量不足"等场景,从而给出不同的用户提示或重试策略。NO_NEED_UPGRADE 是重要的幂等保护:当介质上的固件版本与当前版本一致时直接返回,避免无意义的重复烧写。
芯片型号与版本防呆
ufw_syd_head_v1.szChipName(如 AC690X、AC691X)与 check_ufw_file() 的型号核对,防止把其他芯片的固件包烧入本机。ufw_file_head_v1.Crc/ufw_syd_head_v1.Crc/CrcOfSydFileHead 的三层 CRC 校验(总头自校验、文件头校验、明文数据校验)确保任何传输损坏都会在校验阶段被拦截,而不是烧写阶段才暴露。
参数区可信性保障
UPDATA_PARM 的 parm_crc 与 magic = 0x5441 构成双重防线:uboot 在跳转后先验 magic 再验 CRC,任一不符即返回 UPDATA_PARM_ERR。UPDATA_MAGIC = 0x5A00 特意选非零值,将"未初始化内存"(通常为 0)与"有效升级标志"区分开——这是嵌入式升级协议中常见的"哨兵值"设计,防止误判升级请求。UPDATA_TYPE 枚举值从 0x5A00 起连续分配,与 UPDATA_RESULT 复用同一数值空间(UPDATA_NON == UPDATA_MAGIC),uboot 与 SDK 都依赖这一约定,因此头文件明确禁止调整既有枚举顺序。
失败恢复与一致性
升级失败后 parm_result 为 UPDATA_DEV_ERR/UPDATA_KEY_ERR 等非成功值,SDK 重启后通过 update_result_get()/update_result_deal() 感知并进入恢复路径(可再次进入升级模式重试)。update_clear_result() 保证结果只处理一次;device_is_first_start()/update_success_boot_check() 用于区分升级重启与普通启动,避免把正常启动误判为升级成功。DUAL_BANK 通道(DUAL_BANK_UPDATA)则为双 bank 方案提供备份回退能力,进一步降低变砖风险。
并发与时序注意点
- 升级过程中必须关闭可能并发访问 Flash 的外设(BLE、音频、存储服务)。框架通过
update_target{name, driver_close}注册表统一执行关闭动作,跳转前由common_update_before_jump_reset_handle()复位外设; UPDATA_PARM与标志位是全局共享内存,跳转前应确保无其他任务写入;ext_arg_len/ext_arg_crc对扩展参数做独立校验,避免越界读;- SD 升级场景的 IO 配置(
UPDATA_EXT_PARM的porta_*/portb_*上下拉与驱动能力)必须与 uboot 侧一致,否则 uboot 重新初始化 SD 时可能因 IO 状态不一致导致读写失败。
性能与运维注意事项
- 升级耗时主要取决于介质与通道带宽:SD/U 盘走 FAT 文件流读取,UART/BLE 通道由
UPDATA_UART.control_baud与UPDATA_SD.max_data_baud控制速率,control_timeout(单位 10ms)约束单次交互超时; - 校验开销:
check_ufw_file()在升级前完成全部头部与 CRC 校验,属于 O(文件数) 的轻量操作,不会显著影响启动时间;数据区校验由 uboot 在烧写时按块执行; - 升级过程不可断电:由于 SDK 已跳转 uboot,SDK 侧无法提供应用层断电保护,量产与用户指引应强调升级期间保持供电;如需更强保护,应选用
DUAL_BANK_UPDATA双 bank 方案; - 日志与可观测性:通过
update_result_deal()的返回值可将升级结果(成功/参数错误/设备错误/密钥错误)上报到业务层(如语音提示、指示灯、TestBox 回传),便于产线定位问题。
Extension Points
- 新增升级通道:在
UPDATA_TYPE枚举中追加新类型——必须加在USER_NORFLASH_UFW_UPDATA之前(保持与 uboot 的数值兼容),并在update_mode_api_v2()分发逻辑中实现对应处理;硬件参数可用UPDATA_SD/UPDATA_UART结构或自定义结构承载,通过dev_update_get_parm()获取。 - 升级前驱动关闭:模块实现
struct update_target{name, driver_close}后用REGISTER_UPDATE_TARGET(target)宏注册,框架经list_for_each_update_target自动遍历执行,无需改动框架代码。 - 私有参数注入:通过
update_mode_api_v2()的priv_param_fill_hdl回调 +update_param_priv_fill()向UPDATA_PARM.parm_priv(32 字节)写入私有数据;更复杂的扩展使用ext_arg_t{type, len, data}扩展参数链,类型在EXT_ARG_TYPE中登记(已有EXT_LDO_TRIM_RES、EXT_JUMP_FLAG、EXT_KEEP_ROMIO_INFO)。 - 进入升级前钩子:
update_enter_cb_register()注册进入升级模式前的回调,update_enter_callback为全局回调指针;UPDATA_KEEP_IO_ENABLE控制是否在升级期间保持 IO 功能(update_keep_io())。 - 升级结果业务化:
update_result_set()/update_result_get()/update_result_deal()组合允许业务层自定义结果的写入、读取与消费逻辑,例如上报 TestBox 或驱动提示音。
相关链接
- update_app.h(升级应用入口)
- update_v1.h(V1 升级协议与 API)
- update.h(V2 升级框架与参数契约)
- dev_update.h(设备升级参数接口)
- update.ld(升级区链接脚本)
- device_app.c(升级触发逻辑)
- app_config.c(升级功能开关)
相关页面:芯片启动与 uboot 流程参见 Boot 相关文档;FAT 文件系统与存储设备参见存储管理页;BLE 升级前连接处理参见蓝牙页;量产烧录工具协议参见 TestBox 页。