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

    • AD23N SDK 概述与芯片平台
    • 工程结构与模块划分
  • 快速开始

    • 开发环境搭建与工具链
    • 编译构建指南
    • 烧录与固件升级工具
  • 应用框架与产品工作流

    • 应用入口与模式调度
    • 音乐播放应用
    • MIDI 解码与键盘演奏
    • 录音应用
    • LINEIN 与扩音应用
    • USB 从设备应用
    • 待机、软关机与空闲检测
    • 公共 UI 与 LED 显示
  • 音频子系统

    • 音频解码器框架
    • 音频编码器框架
    • 音效算法库
    • 音频管理与输出通路
  • 存储与文件系统

    • 文件系统层
    • NOR Flash 与虚拟机存储
    • 设备与设备管理
  • 系统服务与运行时

    • 消息机制与事件分发
    • 按键扫描与输入处理
    • 电源管理与低功耗控制
    • 定时器与系统任务
  • 外设驱动与平台

    • CPU 平台与启动流程
    • USB 协议栈与主机/设备驱动
    • SPI 与通用外设接口
  • 固件升级与构建工具

    • 固件升级机制
    • 编译后处理与镜像打包
    • 构建系统与命令行工具

固件升级机制

AD23N MCU SDK 的固件升级(Update)子系统,负责设备端接收并应用 .ufw 升级固件,覆盖 USB、UART、SD 卡、蓝牙 BLE/SPP、双 bank、norflash、测试盒等多种升级通道,并管理升级标志、结果回写、跳转 Loader 与 VM 数据恢复等完整生命周期。

Purpose and Scope

本文档介绍 AD23N SDK 中固件升级机制的整体架构与实现细节,包括:

  • 升级类型体系(UPDATA_TYPE / UPGRADE_TYPE)与各升级通道的职责划分
  • 升级状态机与标志位(UPDATA_RESULT、UPDATA_FLAG_ADDR、BOOT_STATUS_ADDR)
  • 核心升级流程(update.c):升级参数构造、跳转 Loader 前处理、升级结果处理与启动校验
  • UART 升级(uart_update.c)与设备升级(dev_update.c)的实现要点
  • 升级配置文件(app_config.c)中的全部开关项及含义
  • 失败模式、边界条件与并发/一致性注意事项

本文档不覆盖以下内容(由其他目录页负责):蓝牙协议栈(BT/BLE)具体链路、VM 存储子系统内部实现、Flash 驱动(SFC)细节、量产测试盒(TestBox)协议细节。

Overview

在嵌入式音频 SoC(如 AD23N)上,固件升级是产品出厂烧录和售后维护的关键能力。SDK 将升级能力抽象为统一的升级入口 + 多种传输通道:

  • 无论固件来自 USB 优盘、SD 卡、UART 串口、BLE/SPP 蓝牙还是 norflash 备份区,最终都要写入片内/片外 Flash 的代码区,并保证掉电安全(通过 CRC 校验与升级标志)。
  • 升级文件为 .ufw 格式,设备按短文件名(8+3)规则在挂载的文件系统中查找(默认匹配 /*.ufw),文件路径仅支持两层目录。
  • 升级完成后,Loader 引导阶段根据 UPDATA_FLAG_ADDR 处的标志判断升级结果,决定是否回写 VM 数据或进入正常启动。

设计上,升级状态被持久化到 Flash 的固定地址(UPDATA_BEG 区域),因此意外掉电后设备仍能通过标志恢复正确状态——这是整个机制可靠性的核心。

Architecture

下图展示了固件升级子系统的总体架构:上层应用通过多种方式触发升级,统一进入 update.c 的升级流程,最终跳转到 Loader(LOADER.BIN)完成 Flash 写入。

flowchart TD
    subgraph sg_Trigger["升级触发层"]
        USB["USB 优盘/PC<br/>(USB_UPDATA)"]
        SD["SD 卡<br/>(SD0/SD1_UPDATA)"]
        UART["UART 串口<br/>(UART_UPDATA)"]
        BT["蓝牙 BLE/SPP<br/>(BLE_APP/SPP_APP_UPDATA)"]
        DUAL["双 Bank<br/>(DUAL_BANK_UPDATA)"]
        NOR["Norflash<br/>(NORFLASH_UPDATA)"]
        TB["测试盒<br/>(TESTBOX_UART_UPDATA)"]
    end

    subgraph sg_UpdateCore["升级核心 update.c"]
        ENTRY["升级入口<br/>update_module_init / 各通道初始化"]
        PARAM["参数构造<br/>update_param_priv_fill / ext_fill"]
        RESULT["结果处理<br/>update_result_set / update_result_deal"]
        JUMP["跳转前处理<br/>update_before_jump_common_handle"]
    end

    subgraph sg_Boot["启动/引导层"]
        BOOT_CHECK["启动校验<br/>update_success_boot_check"]
        LOADER["Loader<br/>(LOADER.BIN)"]
        VM["VM 数据恢复<br/>vm_need_recover"]
    end

    subgraph sg_Storage["存储层"]
        FLAG["升级标志区<br/>UPDATA_FLAG_ADDR / BOOT_STATUS_ADDR"]
        UFW["升级文件<br/>/*.ufw"]
    end

    USB --> ENTRY
    SD --> ENTRY
    UART --> ENTRY
    BT --> ENTRY
    DUAL --> ENTRY
    NOR --> ENTRY
    TB --> ENTRY

    ENTRY --> PARAM
    PARAM --> JUMP
    JUMP --> LOADER
    LOADER --> FLAG
    BOOT_CHECK --> FLAG
    BOOT_CHECK --> VM
    UFW --> LOADER

架构要点说明:

  • 触发层:每种传输通道对应一个 UPDATA_TYPE 枚举值(从 USB_UPDATA = 0x5A00 起编号),升级时该值作为参数传入核心流程,用于区分升级来源并在跳转前做对应的硬件/协议清理(例如关闭蓝牙、关闭 WiFi 检测、保持 IO 状态等)。
  • 核心层:update.c 是唯一入口,负责构造 UPDATA_PARM 参数、填充私有数据(update_param_priv_fill)与扩展数据(update_param_ext_fill),最终调用 update_before_jump_common_handle 完成跳转 Loader 前的公共处理。
  • 引导层:Loader 负责实际擦写 Flash;上电后 update_success_boot_check 读取标志区判断上次升级是否成功,若成功则通过 vm_need_recover 触发 VM 数据恢复,避免升级过程中因 flash 布局变化导致用户数据丢失。
  • 存储层:升级标志区位于 UPDATA_BEG 起始地址偏移 0x08 处(UPDATA_FLAG_ADDR),UPDATA_MAGIC = 0x5A00 用于区分"未升级"与"CRC 恰好为 0"的边界情况。

升级类型体系

触发方式(UPGRADE_TYPE)

UPGRADE_TYPE 枚举定义在 update.h,描述了用户可感知的升级触发方式:

枚举值含义
UPGRADE_USB_HARD_KEYUSB 硬件按键强制升级(进入升级模式不依赖应用状态)
UPGRADE_USB_SOFTKEYUSB 软件按键升级(由应用检测到升级请求后触发)
UPGRADE_UART_SOFT_KEYUART 软件按键升级
UPGRADE_UART_ONE_WIRE_HARD_KEYUART 单线硬件按键升级

升级通道(UPDATA_TYPE)

UPDATA_TYPE 枚举定义在 update.h,是升级流程内部使用的通道标识,起始值故意与 UPDATA_MAGIC (0x5A00) 一致:

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

设计意图: 起始值复用 UPDATA_MAGIC 是为了让"升级通道类型"与"升级结果标志"共享同一套取值空间,便于用 g_updata_flag 同时记录"从哪个通道升级"与"升级是否成功"。NON_DEV = 0xFFFF 表示无效设备。代码注释明确要求:已有定义顺序不可调整,新增通道必须加在 USER_NORFLASH_UFW_UPDATA 之前,以保持与 Boot/Loader 侧的兼容。

升级结果(UPDATA_RESULT)

typedef enum {
    UPDATA_NON = UPDATA_MAGIC,   // 0x5A00:无升级/初始态
    UPDATA_READY,                // 升级就绪
    UPDATA_SUCC,                 // 升级成功
    UPDATA_PARM_ERR,             // 参数错误
    UPDATA_DEV_ERR,              // 设备错误
    UPDATA_KEY_ERR,              // 密钥错误
} UPDATA_RESULT;

Source: update.h

升级结果被写入 UPDATA_FLAG_ADDR,Boot/Loader 上电后据此判断本次升级是否成功。UPDATA_NON 与 UPDATA_MAGIC 相同,专门处理"CRC 校验值恰好为 0"的退化场景——用魔法数替代 CRC==0 作为初始态标记。

通道参数结构

  • UPDATA_UART(12 字节):control_io_tx、control_io_rx、control_baud、control_timeout(超时单位 10ms),见 update.h。
  • UPDATA_SD:SD 控制器(SD_CONTROLLER_0/1)与 IO 组合选择(SD0_IO_A~SD0_IO_F、SD1_IO_A/B),以及检测函数、在线检测方式、最大波特率等,见 update.h。
  • UPDATE_UDISK:U 盘参数,内含 rx_ldo_trim / tx_ldo_trim 的联合体,用于调试 LDO 微调,见 update.h。

核心流程实现分析(update.c)

升级文件与关键宏

#define LOADER_NAME		"LOADER.BIN"
#define DEVICE_UPDATE_KEY_ERR  BIT(30)
#define DEVICE_FIRST_START     BIT(31)
...
const char updata_file_name[] = "/*.ufw";
static u32 g_updata_flag = 0;
static volatile u8 ota_status = 0;
static succ_report_t succ_report;

Source: update.c

  • LOADER.BIN 是升级引导程序,应用代码在升级前跳转到 Loader 执行实际擦写。
  • DEVICE_UPDATE_KEY_ERR(bit30)与 DEVICE_FIRST_START(bit31)是 g_updata_flag 高位标志,分别表示"设备升级密钥错误"与"设备首次启动"。
  • updata_file_name = "/*.ufw" 表明升级文件必须放在根目录(通配符匹配),且为短文件名 8+3 格式、仅支持两层目录。

升级标志与 VM 恢复

bool vm_need_recover(void)
{
    log_info(">>>[test]:g_updata_flag = 0x%x\n", g_updata_flag);
    return ((g_updata_flag & 0xffff) == UPDATA_SUCC) ? true : false;
}

u16 *get_updata_flag_addr()
{
    return UPDATA_FLAG_ADDR;
}

Source: update.c

vm_need_recover 检查 g_updata_flag 低 16 位是否等于 UPDATA_SUCC——当升级成功标志被置位时,说明升级过程中可能发生了 Flash 布局/VM 偏移变化,需要恢复 VM 数据。这是升级与用户数据保持(support_vm_data_keep 配置)协作的关键接口。

升级结果设置与启动校验

void update_result_set(u16 result)
void update_clear_result()
bool update_success_boot_check(void)

Source: update.c

  • update_result_set(u16 result):将升级结果写入标志区,供 Boot/Loader 读取。
  • update_clear_result():清除升级结果,进入正常启动态。
  • update_success_boot_check():上电时调用,读取标志区判断上次升级是否成功,成功则执行后续的 VM 恢复与成功上报(succ_report)。

结果处理与硬件清理

int update_result_deal();
void update_close_hw(void *filter_name);
static void update_before_jump_common_handle(UPDATA_TYPE up_type)

Source: update.c

  • update_result_deal():升级完成后的收尾处理,根据结果执行成功/失败分支(例如成功时关闭 UI、失败时回滚标志)。
  • update_close_hw(void *filter_name):按名称过滤关闭外设硬件,保证跳转 Loader 时外设处于安全状态。
  • update_before_jump_common_handle(UPDATA_TYPE up_type):跳转 Loader 前的公共处理——关闭蓝牙控制器(ll_hci_destory / hci_controller_destory)、关闭 RAM 保护(ram_protect_close)、关闭全部中断(hwi_all_close)、关闭 WiFi 检测(wifi_det_close,弱符号实现)、最后 chip_reset() 复位进入 Loader。该函数接收 up_type,以便按通道做差异化处理(例如 UART 单线升级需要保持 IO 状态)。

升级参数填充

void update_param_priv_fill(UPDATA_PARM *p, void *priv, u16 priv_len);
void update_param_ext_fill(UPDATA_PARM *p, u8 ext_type, u8 *ext_data, u8 ext_len);

Source: update.c

  • update_param_priv_fill:将通道私有参数(如 UART 波特率、SD IO 配置)填入 UPDATA_PARM。注释特别说明:ota.bin 放 exflash 升级时,parm_priv 存放 norflash 参数,对应实际升级方式的参数必须放在 norflash 参数之后——这反映了 norflash 升级与其他通道参数在内存布局上的约束。
  • update_param_ext_fill:填充扩展类型数据,为不同升级方式传递附加信息。

Core Flow — 升级全流程

下图展示一次典型升级(以 UART 软件按键为例)从触发到完成的完整时序:

sequenceDiagram
    participant App as 应用层
    participant Uart as uart_update.c
    participant Core as update.c 核心
    participant Loader as Loader (LOADER.BIN)
    participant Flash as Flash 标志区/代码区
    participant VM as VM 数据

    App->>Uart: 检测到升级请求 (UPGRADE_UART_SOFT_KEY)
    Uart->>Core: 构造 UPDATA_PARM (UART_UPDATA, 波特率/IO/超时)
    Core->>Core: update_param_priv_fill / update_param_ext_fill
    Core->>Core: update_before_jump_common_handle(up_type)
    Core->>Loader: 关闭外设/中断 → chip_reset() 跳转
    Loader->>Flash: 校验 .ufw (CRC/VID) 并擦写代码区
    Loader->>Flash: 写入升级结果 (update_result_set: UPDATA_SUCC)
    Loader->>Loader: 复位重启
    Loader->>Core: 上电 update_success_boot_check() 读标志
    Core->>VM: vm_need_recover() == true → 恢复 VM 数据
    Core->>App: 正常启动,上报升级成功

流程要点:

  1. 触发:应用层通过 UART 收到升级命令(软件按键)后,uart_update.c 初始化 UPDATA_UART 参数(TX/RX IO、波特率、超时)。
  2. 参数构造:update.c 将通道参数通过 update_param_priv_fill 填入 UPDATA_PARM,必要时追加扩展数据。
  3. 跳转准备:update_before_jump_common_handle 依次销毁蓝牙协议栈、关闭 RAM 保护、关闭全部中断、关闭 WiFi 检测,然后 chip_reset() 复位。
  4. Loader 执行:Loader 读取 /*.ufw 文件,校验 CRC 与 VID(VID 策略由 ufw_vid_need_to_be_different 配置),写入 Flash 代码区,最后把 UPDATA_SUCC 写入 UPDATA_FLAG_ADDR。
  5. 启动校验:复位后应用调用 update_success_boot_check,若标志为 UPDATA_SUCC 则触发 vm_need_recover 恢复 VM 数据,保证用户配置不丢失。

UART 升级实现(uart_update.c)

uart_update.c 位于 sdk/app/bsp/common/uart_update/uart_update.c,实现了 UART 通道的升级收发逻辑。其头文件 uart_update.h 提供了对外接口。

关键职责:

  • 维护 UART 接收缓冲区与分包解析,将上位机发送的升级数据流按 Loader 协议分包。
  • 对接 UPDATA_UART 参数(TX/RX IO、波特率、超时),在升级期间接管串口。
  • 支持软件按键(UPGRADE_UART_SOFT_KEY)与单线硬件按键(UPGRADE_UART_ONE_WIRE_HARD_KEY)两种触发方式。

设备升级实现(dev_update.c)

dev_update.c 位于 sdk/app/bsp/common/update/dev_update.c,头文件为 dev_update.h。它负责设备级升级的统一入口,其配置项由 app_config.c 提供:

  • dev_update_use_eeprom:升级状态区域选择——0 使用 VM 区,1 使用 EEPROM 区。
  • dev_update_keep_io_status:升级过程中是否保持 IO 状态(防止升级瞬间外设掉电)。
  • dev_update_power_io:升级时使用的电源引脚,-1 表示不控制。

这些配置在 app_config.c 中集中定义,是产品定制升级行为的主要旋钮。

配置选项

升级相关配置集中在 app_config.c 的 "update Configuration" 区块,产品定制时直接修改这些全局常量:

配置项类型默认值说明
dev_update_use_eepromu80升级状态区域:0=VM 区,1=EEPROM 区
dev_update_keep_io_statusu80设备升级时是否保持 IO 状态
dev_update_power_iou8-1升级时用到的电源引脚(-1 表示不控制)
ufw_vid_need_to_be_differentu80.ufw 文件 VID 校验策略:0=相同,1=不同,2=文件 VID > 设备 VID,3=文件 VID < 设备 VID
support_norflash_update_enint0是否支持 norflash(外置 Flash)升级
support_ota_tws_same_time_newint0是否支持 TWS 双耳同时 OTA(新方案)
CONFIG_UPDATE_STORAGE_DEV_ENint1是否使能存储设备(USB/SD)升级通道
CONFIG_UPDATE_TESTBOX_UART_ENint0是否使能测试盒 UART 升级
CONFIG_UPDATE_APP_OTA_ENint0是否使能 APP OTA(蓝牙)升级
CONFIG_UPDATE_TESTBOX_BLE_ENint0是否使能测试盒 BLE 升级
support_dual_bank_update_enint0是否支持双 Bank(A/B 分区)升级
support_vm_data_keepint0升级时是否保留 VM 数据
FLASH_ALIGNED_MODEint16Flash 对齐系数,仅填 1 或 16,实际对齐 = align × 256 字节

设计意图: 这些开关以编译期常量形式存在,保证升级路径在运行时零配置开销;同时用 CONFIG_UPDATE_*_EN 系列控制哪些升级通道被链接进固件,减小未使用通道的代码体积。FLASH_ALIGNED_MODE 对齐策略直接影响 Loader 擦写时的扇区边界计算,修改后必须与 Flash 型号的 sector size 匹配。

API 参考

以下接口来自 update.c(定义于 update.h):

bool vm_need_recover(void)

判断升级成功后是否需要恢复 VM 数据。返回 true 表示低 16 位升级标志为 UPDATA_SUCC,说明发生过升级且 Flash 布局可能变化。

u16 *get_updata_flag_addr(void)

返回升级标志区地址(UPDATA_FLAG_ADDR,即 UPDATA_BEG + 0x08),供 Boot/Loader 读写升级状态。

void update_result_set(u16 result)

将升级结果(UPDATA_RESULT 枚举值)写入标志区。Loader 在升级完成后调用,写入 UPDATA_SUCC / UPDATA_DEV_ERR / UPDATA_KEY_ERR 等。

void update_clear_result(void)

清除升级结果标志,使设备进入正常启动态。

bool update_success_boot_check(void)

上电启动时调用,读取标志区校验上次升级结果,成功则触发 VM 恢复与成功上报。

int update_result_deal(void)

升级完成后的收尾处理:根据结果执行成功/失败分支、关闭 UI 提示等。

void update_close_hw(void *filter_name)

按名称过滤关闭外设硬件,确保跳转 Loader 前外设安全。filter_name 为需要保留的外设名称列表。

static void update_before_jump_common_handle(UPDATA_TYPE up_type)

跳转 Loader 前的公共处理:销毁蓝牙控制器、关闭 RAM 保护、关闭全部中断、关闭 WiFi 检测,最后复位进入 Loader。

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

向 UPDATA_PARM 填充通道私有参数(如 UART/SD/norflash 参数)。注意:norflash 升级时私有参数需排在通道参数之前。

void update_param_ext_fill(UPDATA_PARM *p, u8 ext_type, u8 *ext_data, u8 ext_len)

向 UPDATA_PARM 填充扩展类型数据,用于传递附加升级信息。

失败模式、边界情况与并发

失败模式

场景检测方式处理策略
升级文件 CRC 错误Loader 校验 CRC写入 UPDATA_PARM_ERR,保持旧固件
升级文件 VID 不匹配ufw_vid_need_to_be_different 策略写入 UPDATA_KEY_ERR,拒绝升级
设备错误(擦写失败/掉盘)Loader 擦写返回错误写入 UPDATA_DEV_ERR,设备回退旧固件
升级密钥错误DEVICE_UPDATE_KEY_ERR (bit30)置位标志位,阻止无效固件写入
升级中途掉电UPDATA_FLAG_ADDR 标志未置 SUCC上电后 update_success_boot_check 判定失败,重新进入可升级状态

边界情况

  • CRC == 0:升级标志初始值使用 UPDATA_MAGIC (0x5A00) 而非 0,避免与"CRC 恰好为 0"混淆(见 update.h)。
  • 首次启动:DEVICE_FIRST_START (bit31) 用于区分设备首次上电与升级后重启,避免误触发 VM 恢复。
  • 文件系统限制:升级文件必须是短文件名(8+3)且最多两层目录,路径硬编码为 /*.ufw——这是 Loader 端文件系统实现的约束。
  • 对齐约束:FLASH_ALIGNED_MODE 只允许 1 或 16,实际按 ×256 字节对齐,违反约定会导致擦写越界。

并发/时序一致性

  • 升级是单线程独占流程:进入升级流程后通过 hwi_all_close 关闭全部中断、销毁蓝牙协议栈,避免升级期间外设事件干扰擦写时序。
  • g_updata_flag 是全局状态,但只在升级流程(单线程)与 Boot 启动早期读取,不存在多任务竞争;ota_status 声明为 volatile 供中断/任务间传递升级进度。
  • wifi_det_close 为弱符号(__attribute__((weak)))实现,未启用 WiFi 的工程可被链接器自动替换为空操作,保证升级核心不依赖特定外设。

扩展点

  1. 新增升级通道:在 UPDATA_TYPE 枚举的 USER_NORFLASH_UFW_UPDATA 之前追加新值,并在 update_before_jump_common_handle 中按 up_type 增加差异化处理(如新增硬件外设的关闭逻辑)。
  2. 自定义升级文件路径:修改 updata_file_name(当前为 /*.ufw)可调整升级文件查找规则,但必须满足短文件名与两层目录约束。
  3. 升级结果回调:succ_report_t succ_report 结构用于升级成功上报,可在 update_result_deal 成功后扩展上报内容(如版本号、升级耗时)。
  4. 产品定制开关:通过 app_config.c 的升级配置项即可启用/禁用各通道(存储设备、测试盒 UART/BLE、APP OTA、双 Bank、norflash),无需修改核心代码。

Related Links

  • UART 升级实现 uart_update.c
  • 设备升级实现 dev_update.c
  • 升级公共头文件 update.h
  • 升级配置 app_config.c
  • USB 升级器说明文档
Next
编译后处理与镜像打包