杰理 SDK 文档中心
首页
首页
  • 项目概览

    • AD16N 系列芯片与 SDK 能力总览
  • 快速开始

    • 环境搭建与编译工具链
    • 编译构建指南
    • 烧录与固件升级
  • SDK 工程架构

    • SDK 目录结构与模块分层
    • 构建系统与批处理工具
    • BSP 板级支持包
  • mbox_flash 小音箱应用

    • 应用初始化与启动流程
    • 应用配置系统
    • 按键、UI 与用户交互
  • 音频子系统

    • 音频解码框架与调度
    • 音频格式解码器实现
    • MIDI 合成与播放
    • 音频编码与录音
    • EQ/DRC 与音效处理
    • DAC/ADC 音频接口与采样
  • 存储与文件系统

    • 媒体 IO 抽象层 MIO
    • 存储设备驱动
    • 文件系统支持
  • 平台系统库

    • 系统基础服务
    • CPU 平台与运行库
    • 固件升级与更新机制
    • 蓝牙与扩展连接接口
  • 电源与低功耗管理

    • 电源管理与低功耗设计
    • 锂电池充电管理
  • 硬件与文档参考

    • SDK 文档中心与版本发布记录
    • 芯片数据手册与硬件设计参考

固件升级与更新机制

本文档介绍 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 将升级能力抽象为两层:

  1. code_v1(update_v1.h)——面向"文件型"升级:从 SD 卡 / U 盘等可移动介质读取 .ufw 打包文件,解析文件头、校验 CRC、核对芯片型号,再通过参数区将升级意图传递给 uboot 执行。这是传统且最常用的升级路径,典型场景是用户把固件拷贝到 TF 卡后上电升级。
  2. 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_ENconst int1使能 APP 内 OTA 升级功能
CONFIG_UPDATE_TESTBOX_UART_ENconst int1使能 TestBox UART 升级通道
CONFIG_UPDATE_TESTBOX_BLE_ENconst int1使能 TestBox BLE 升级通道
CONFIG_UPDATE_STORAGE_DEV_ENconst 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宏0x5441UPDATA_PARM 参数区魔数
UPDATE_PRIV_PARAM_LEN宏32私有参数区长度(字节)
UPDATA_PARM_SIZE宏256UPDATA_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_ERROR0成功
SFC_OPEN_ERROR1SPI Flash 控制器打开失败
DEVIVE_OPEN_ERROR2设备打开失败
FAT_MOUNT_ERROR3FAT 文件系统挂载失败
FILE_OPEN_ERROR4文件打开失败
FIND_FLASH_BIN_ERROR5未找到 Flash bin
FIND_FLASH2_BIN_ERROR6未找到第二 Flash bin
NO_NEED_UPGRADE7版本相同,无需升级
FIND_OTA_ERROR8未找到 OTA 资源
FIND_LOADER0_ERROR9未找到 loader0
FLASH_SIZE_ERROR10Flash 容量不足/不匹配
DEVIVE_IDX_ERROR11设备索引错误

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

  1. 新增升级通道:在 UPDATA_TYPE 枚举中追加新类型——必须加在 USER_NORFLASH_UFW_UPDATA 之前(保持与 uboot 的数值兼容),并在 update_mode_api_v2() 分发逻辑中实现对应处理;硬件参数可用 UPDATA_SD/UPDATA_UART 结构或自定义结构承载,通过 dev_update_get_parm() 获取。
  2. 升级前驱动关闭:模块实现 struct update_target{name, driver_close} 后用 REGISTER_UPDATE_TARGET(target) 宏注册,框架经 list_for_each_update_target 自动遍历执行,无需改动框架代码。
  3. 私有参数注入:通过 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)。
  4. 进入升级前钩子:update_enter_cb_register() 注册进入升级模式前的回调,update_enter_callback 为全局回调指针;UPDATA_KEEP_IO_ENABLE 控制是否在升级期间保持 IO 功能(update_keep_io())。
  5. 升级结果业务化: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 页。

Prev
CPU 平台与运行库
Next
蓝牙与扩展连接接口