杰理 SDK 文档中心
首页
首页
  • 项目概览与快速开始

    • 项目概述与芯片支持
    • 环境搭建与工具链
    • 工程与构建系统
    • 烧录与升级工具
    • 文档与硬件资料
  • 系统架构与芯片平台

    • 芯片平台与启动流程
    • 预编译库与头文件体系
    • 消息、定时器与中断服务
    • 通用外设驱动
  • 存储与文件系统

    • 文件系统实现
    • 存储设备驱动
    • VM 参数存储系统
  • 音频处理

    • 音频解码器
    • 音频编码器
    • MIDI 合成与播放
    • 音效、变速变调与降噪
  • 语音玩具应用

    • 应用框架与状态机
    • 音乐播放与外部音源
    • MIDI 乐器模式
    • 录音应用
    • 待机、电源管理与 USB 从机
  • 小音箱应用

    • 应用框架与模式管理
    • 播放源:音乐、FM、录音与 LineIn
  • 应用层与示例工程

    • 通用 MCU 应用
  • 固件更新与补丁

    • 固件升级机制
    • AD14N 主动降噪补丁

芯片平台与启动流程

本文档介绍 fw-AD15N(杰理 AD 系列 MCU SDK)所支持的芯片平台家族、平台化构建系统,以及从芯片复位到应用就绪的启动流程,并深入分析双 Bank Flash 启动/OTA 升级机制的实现。

Purpose and Scope

本页覆盖:

  • AD 系列芯片平台家族(AC104N / AD14N / AD15N / AD17N / AD18N)及其产品形态(mcu、voice_toy、mbox_mg)
  • 平台化构建系统:sdk/Makefile 与各平台 Makefile.<platform>_<product>、Code::Blocks 工程文件的组织方式
  • 启动流程:内存布局(链接器符号驱动的堆初始化)、BSP 初始化、应用主循环
  • 双 Bank Flash 启动与在线升级(OTA)流程,基于 sdk/app/bsp/common/dual_bank_demo.c 的真实实现

本页不展开的具体子系统(各有独立文档页):

  • 音频解码/编码子系统(sdk/app/bsp/common/decoder/、encoder/)→ 见"音频编解码框架"页
  • 音效与降噪(sound_effect_list/ans_api.c、ANC 补丁)→ 见"音效处理与 ANC 降噪"页
  • 文件系统与存储管理(jlfs、flash 驱动)→ 见"存储与文件系统"页

Overview

该 SDK 是杰理(Jieli)针对其 AD/AC 系列语音 MCU 提供的嵌入式软件开发套件,同一套应用代码通过平台化 Makefile 面向多颗芯片构建。不同芯片平台共享 sdk/app/bsp/ 下的应用与 BSP 公共代码,差异主要体现在芯片外设寄存器、Flash 布局与链接脚本上。

从仓库结构可以观察到清晰的分层设计意图:

  • 平台层(platform):由 sdk/Makefile 分派到 Makefile.ad15n_mcu、Makefile.ad18n_voice_toy 等平台级构建文件,每颗芯片 + 产品形态组合对应一个独立构建目标;
  • BSP 层:sdk/app/bsp/ 下的公共驱动与中间件(decoder、encoder、sound_effect_list、文件系统、双 Bank 升级 demo);
  • 启动支撑:sdk/app/post_build/sh54/voice_toy/app_ld.c 等后处理脚本负责应用链接与镜像生成;
  • 维护通道:patch/ 目录存放针对特定芯片的补丁(如 AD14N 的 ANC 降噪补丁),体现"平台基座 + 差异化补丁"的维护策略。

启动流程的核心问题是:芯片上电后,如何把代码从 Flash 引导进 RAM、初始化内存堆、配置时钟与外设,最终进入应用主循环。本 SDK 中,堆内存的边界由链接脚本符号 _free_start / _free_end 提供(见 sdk/app/bsp/lib/my_malloc.c),而 OTA 升级则依赖双 Bank Flash 布局(见 sdk/app/bsp/common/dual_bank_demo.c),这两者是理解本 SDK 启动体系的关键。

Architecture

flowchart TD
    subgraph sg_Chip["芯片平台层 (Chip Platforms)"]
        AC104N["AC104N"]
        AD14N["AD14N"]
        AD15N["AD15N"]
        AD17N["AD17N"]
        AD18N["AD18N"]
    end

    subgraph sg_Build["构建系统 (Build System)"]
        MK["sdk/Makefile"]
        MKP["make_prompt.bat"]
        WS["default.workspace"]
        CBP["*.cbp 工程文件"]
    end

    subgraph sg_BSP["BSP 与应用层 (sdk/app/bsp/)"]
        COMMON["common/ 公共中间件"]
        LIB["lib/ 库与内存管理"]
        DEC["decoder/ 音频解码"]
        ENC["encoder/ 音频编码"]
        FX["sound_effect_list/ 音效与降噪"]
        DUAL["dual_bank_demo.c 双Bank升级"]
    end

    subgraph sg_Post["构建后处理 (post_build)"]
        ALD["sh54/voice_toy/app_ld.c"]
    end

    subgraph sg_Patch["补丁通道 (patch/)"]
        PATCH["AD14N ANC 降噪补丁"]
    end

    MK -->|"分派平台构建"| MKP
    MK -->|"定义平台宏与链接"| CBP
    MK -->|"选择芯片/产品"| AC104N
    MK -->|"选择芯片/产品"| AD14N
    MK -->|"选择芯片/产品"| AD15N
    MK -->|"选择芯片/产品"| AD17N
    MK -->|"选择芯片/产品"| AD18N
    WS --> MK
    AC104N --> COMMON
    AD14N --> COMMON
    AD15N --> COMMON
    AD17N --> COMMON
    AD18N --> COMMON
    COMMON --> DEC
    COMMON --> ENC
    COMMON --> FX
    COMMON --> DUAL
    COMMON --> LIB
    ALD -->|"生成最终镜像"| CBP
    PATCH -->|"按芯片差异合入"| COMMON

架构说明:

  • 芯片平台层:AC104N / AD14N / AD15N / AD17N / AD18N 是同一 SDK 支持的芯片家族,由构建系统按目标选择。它们共享应用代码,差异封装在芯片级驱动、链接脚本和 Makefile 中——这是"一套代码、多芯片复用"的平台化设计。
  • 构建系统层:sdk/Makefile 是总入口,通过 make_prompt.bat 辅助调用;每个平台/产品组合有独立 Makefile(如 Makefile.ad15n_mcu、Makefile.ad18n_voice_toy)和 Code::Blocks 工程(AD15N_mcu.cbp 等),default.workspace 将多个工程组织为工作区。
  • BSP/应用层:sdk/app/bsp/common/ 承载跨芯片复用的中间件——解码器、编码器、音效、双 Bank 升级;lib/ 提供内存管理等基础库。
  • 构建后处理:post_build/sh54/voice_toy/app_ld.c 在链接阶段对应用镜像做定制处理(如地址重定位、镜像封装),是启动流程与 Flash 布局的直接关联点。
  • 补丁通道:patch/AD14N_20250303_ANC_noise_reduction_patch/ 展示了对特定芯片的差异化能力(ANC 降噪)以补丁形式合入公共代码的维护方式,避免污染其他平台基线。

说明:以上分层来自对仓库目录结构与构建文件命名的分析;各芯片寄存器级差异、Boot ROM 内部细节不在本 SDK 源码内,属于芯片手册范畴,本文档不臆测其内部实现。

芯片平台家族与构建系统

平台矩阵

仓库根目录的构建文件直接揭示了平台矩阵:每个 Makefile.<platform>_<product> 对应一个"芯片 + 产品形态"的组合,Code::Blocks 工程(.cbp)与之对应。

芯片平台产品形态Makefile工程文件
AC104Nmbox_mg(音箱管理)Makefile.ac104n_mbox_mgAC104N_mbox_mg.cbp
AD14Nmcu(语音 MCU)Makefile.ad14n_mcuAD14N_mcu.cbp
AD14Nvoice_toy(语音玩具)Makefile.ad14n_voice_toyAD14N_voice_toy.cbp
AD15NmcuMakefile.ad15n_mcuAD15N_mcu.cbp
AD15Nvoice_toyMakefile.ad15n_voice_toyAD15N_voice_toy.cbp
AD17NmcuMakefile.ad17n_mcuAD17N_mcu.cbp
AD17Nvoice_toyMakefile.ad17n_voice_toyAD17N_voice_toy.cbp
AD18NmcuMakefile.ad18n_mcuAD18N_mcu.cbp
AD18Nvoice_toyMakefile.ad18n_voice_toyAD18N_voice_toy.cbp

设计意图:mcu 与 voice_toy 两种产品形态在 Flash 分区、外设配置(如按键/点灯/马达驱动)上存在差异,但共享同一套解码器与 BSP 代码。将平台差异前置到构建层,使应用代码可以保持"一次编写、多平台编译",这是杰理 SDK 典型的平台化(board-level porting)策略。default.workspace 与 sdk/Makefile 作为总入口,避免开发者直接记忆每个平台的构建细节。

构建入口与镜像生成

make_prompt.bat 是 Windows 下的构建辅助脚本,配合 sdk/Makefile 使用。构建产物经过 post_build/sh54/voice_toy/app_ld.c 的后处理——该文件位于 post_build 目录,表明它是在常规编译链接之后运行的定制链接/封装步骤,负责生成最终烧录镜像(如设置应用入口地址、附加文件系统元数据)。

这条链路对启动流程的意义在于:烧录进 Flash 的镜像布局(入口地址、Bank 划分)由构建期决定,而运行时启动流程必须与之一致——这正是"芯片平台与启动流程"这一主题把构建系统纳入讨论范围的原因。

内存布局与堆初始化

my_malloc.c 通过外部链接器符号定义堆边界:

extern const u8 _free_start[];
extern const u8 _free_end[];

Source: my_malloc.c

设计意图:堆的起始与结束地址不由源码硬编码,而是由链接脚本根据该平台的 RAM 布局自动生成 _free_start/_free_end 符号。这意味着:

  • 换一颗芯片(如 AD14N → AD18N)时,RAM 大小不同,只要链接脚本正确,堆区间自动跟随调整,无需改动内存管理代码;
  • 启动流程中必须先完成 BSS/数据段搬运与堆初始化,后续 malloc/my_free 才能工作——dual_bank_demo.c 中大量使用 my_free(见下文),也印证了内存管理是启动后的基础服务。

说明:_free_start/_free_end 的具体地址值由链接脚本(.ld/link 文件)决定,链接脚本文件未在本页证据范围内直接读取,其符号语义(堆上下界)可从 my_malloc.c 的 extern 声明确认。

启动流程

总体阶段划分

根据 SDK 的代码结构与构建产物组织方式,启动流程可归纳为以下阶段(阶段 0/1 属于芯片内部行为,阶段 2 起对应本 SDK 源码):

sequenceDiagram
    participant ROM as "芯片 BootROM"
    participant FLASH as "SPI Flash"
    participant LDS as "链接脚本/镜像"
    participant HEAP as "my_malloc 堆"
    participant BSP as "BSP 公共中间件"
    participant APP as "应用主循环"

    ROM->>FLASH: 上电复位,读取启动镜像头部
    ROM->>LDS: 按镜像布局搬运代码/数据段
    LDS->>HEAP: 生成 _free_start/_free_end 堆边界符号
    ROM->>BSP: 跳转到应用入口 (app_ld 重定位后地址)
    BSP->>BSP: 时钟/外设/Flash 控制器初始化
    BSP->>HEAP: 初始化堆,启用 malloc/my_free
    BSP->>BSP: 注册文件系统 (jlfs) 与设备 (flash_dev)
    BSP->>APP: 进入应用主循环
    APP->>APP: 处理解码/升级/音效等业务

各阶段说明:

  1. 芯片复位与 BootROM 引导(阶段 0):芯片上电后由内部 BootROM 从 SPI Flash 读取启动镜像。镜像的布局(入口地址、代码段位置)由构建期 post_build/sh54/voice_toy/app_ld.c 的定制链接决定——这正是该文件位于构建后处理链中的原因。
  2. 镜像搬运与内存初始化(阶段 1):链接脚本为各内存段(data、bss、堆)确定地址,并导出 _free_start/_free_end 等符号供运行时使用。
  3. BSP 初始化(阶段 2):应用入口执行后,先完成堆初始化与设备注册。dual_bank_demo.c 中 flash_dev、vfs_read、dev_ioctl、dev_byte_write 等 API 的可用,说明 Flash 设备驱动、VFS(虚拟文件系统)与 jlfs 文件系统都已在应用代码运行前就绪。
  4. 应用主循环(阶段 3):进入业务逻辑,包括音频解码(decoder_api.c、decoder_msg_tab.c、decoder_point.c)、音效(ans_api.c)以及本页重点的 OTA 升级服务。

启动流程中的看门狗与长耗时操作

启动与初始化阶段常包含耗时操作(如 Flash 擦写),SDK 在 dual_bank_demo.c 的擦除循环中显式调用 wdt_clear():

for (u32 i = 0; i < (file_size / flash_alignsize); i ++) {
    erase_err = dev_ioctl(flash_dev, flash_erase_cmd, upgrade_start_addr + i * flash_alignsize);
    if (erase_err) {
        log_error("erase %x", upgrade_start_addr + i * flash_alignsize);
        break;
    }
    wdt_clear();
    log_info("erase %x", upgrade_start_addr + i * flash_alignsize);
}

Source: dual_bank_demo.c

设计意图:Flash 扇区擦除是毫秒级甚至更长的阻塞操作,若看门狗在启动/升级期间超时,系统会被强制复位,导致升级中断、固件损坏。在循环内每个扇区擦除后 wdt_clear(),既保证看门狗不误触发,又保留了出错即 break 的快速失败路径——这是嵌入式启动代码中"长任务 + 看门狗喂狗"的标准范式。

双 Bank 启动与 OTA 升级机制

为什么需要双 Bank

语音 MCU 的 OTA 升级必须解决"升级失败导致变砖"的问题。双 Bank(dual bank)方案将 Flash 划分为两个 Bank:当前运行的固件位于 Bank A,新固件下载到 Bank B;校验通过后切换启动 Bank。这样即使升级中途断电,设备仍可从旧 Bank 正常启动。本 SDK 的 dual_bank_demo.c 正是这一机制的参考实现,其依赖的 jlfs_* 系列 API 表明 Bank 信息由 jlfs(杰理文件系统)统一管理。

升级流程实现分析

dual_bank_demo.c 的完整升级流程如下:

flowchart TD
    Start([升级任务启动]) --> GetBank["jlfs_get_idle_bank_info<br/>(查询空闲 Bank 地址与大小)"]
    GetBank --> LogAddr["log_info 打印升级地址/大小"]
    LogAddr --> OpenFile["vfs 打开升级固件文件<br/>(file_attr.fsize - 4096 为有效数据)"]
    OpenFile --> Loop{"逐块处理<br/>(flash_alignsize 对齐)"}
    Loop -->|"擦除"| Erase["dev_ioctl flash_erase_cmd<br/>擦除失败则 log_error + break"]
    Erase --> Wdt["wdt_clear 喂狗"]
    Wdt --> Write["dev_byte_write 写入<br/>chip_crc16_with_init 累计 CRC"]
    Write --> Loop
    Loop -->|"全部写完"| ReadBack["dev_byte_read 回读校验"]
    ReadBack --> UpdateInfo["jlfs_updata_dual_bank_info<br/>(记录地址与 CRC)"]
    UpdateInfo --> CheckInfo{"jlfs_check_dual_bank_info<br/>校验 Bank 信息?"}
    CheckInfo -->|"成功"| SetSFC["IOCTL_SET_SFC_READ 切换启动 Bank"]
    CheckInfo -->|"失败"| Fallback["IOCTL_ERASE_SECTOR 擦除失败 Bank<br/>回退到旧 Bank"]
    SetSFC --> Done([升级完成,重启后从新 Bank 启动])
    Fallback --> Done

关键实现细节(对应源码行):

  1. 获取空闲 Bank:jlfs_get_idle_bank_info(&upgrade_start_addr, &bank_size) 返回当前未运行的 Bank 的起始地址与容量,升级目标不是硬编码地址,而是由文件系统动态分配——这保证双 Bank 布局变化时应用代码无需修改。

  2. 对齐擦写:擦除以 flash_alignsize 为粒度进行,写入前先擦除对应扇区;dev_ioctl(flash_dev, flash_erase_cmd, ...) 与 dev_byte_write(flash_dev, tmp_buf, ...) 构成"擦-写"配对,遵循 NOR Flash 必须先擦后写的物理约束。

  3. CRC 累计校验:写入过程中以 chip_crc16_with_init(tmp_buf, cnt, data_crc) 逐块累计 CRC16,最后通过 jlfs_updata_dual_bank_info(upgrade_start_addr, data_crc) 把地址与 CRC 一并写入 Bank 信息区;启动时据此判断新固件完整性。

  4. 回读验证与回退:写入完成后 dev_byte_read 回读对比;jlfs_check_dual_bank_info 校验 Bank 信息,失败时 IOCTL_ERASE_SECTOR 擦除损坏的 Bank 头部并回退旧 Bank——这是防"变砖"的最后一道防线。

  5. 切换启动 Bank:dev_ioctl(flash_dev, IOCTL_SET_SFC_READ, 1) 设置 SFC(SPI Flash 控制器)读取模式/启动 Bank,配合 jlfs_updata_dual_bank_info 的记录,重启后 BootROM 从新 Bank 引导。

升级代码示例

以下摘自 dual_bank_demo.c,展示"获取空闲 Bank → 擦写 → 记录 CRC → 校验"的核心段落:

u32 upgrade_start_addr = 0;
u32 bank_size;
...
res = jlfs_get_idle_bank_info(&upgrade_start_addr, &bank_size);
log_info("addr %x,size %x\n", upgrade_start_addr, bank_size);

Source: dual_bank_demo.c

dev_byte_write(flash_dev, tmp_buf, upgrade_start_addr + offset, cnt);
...
data_crc = chip_crc16_with_init(tmp_buf, cnt, data_crc);

Source: dual_bank_demo.c

jlfs_updata_dual_bank_info(upgrade_start_addr, data_crc);
...
u32 head_check_res = jlfs_check_dual_bank_info(upgrade_start_addr);
if (head_check_res) {
    ...
    dev_ioctl(flash_dev, IOCTL_ERASE_SECTOR, upgrade_start_addr);
}

Source: dual_bank_demo.c

设计意图:将"Bank 管理"(哪些地址空闲、CRC 存哪里)交给 jlfs,应用只需关心"把文件写到哪、写完校验",职责分离清晰;CRC16 采用 _with_init 形式允许流式累计,无需为整个固件分配大缓冲区,适配 MCU 有限 RAM 的约束。

配置选项

平台选择与构建配置集中在 sdk/ 根目录,通过 Makefile/工程文件声明,而非运行时配置:

配置项位置可选值/类型说明
芯片平台sdk/Makefile.<platform>_<product>AC104N / AD14N / AD15N / AD17N / AD18N决定芯片驱动、链接脚本与 Flash 布局
产品形态同上mcu / voice_toy / mbox_mg决定外设配置与应用集
工程组织sdk/default.workspace工作区文件组织各 .cbp 工程
构建辅助sdk/make_prompt.bat批处理脚本Windows 下调用 Makefile 的入口
补丁合入patch/ 目录按芯片/日期命名(如 AD14N_20250303_ANC_noise_reduction_patch)向公共代码合入差异化能力
堆边界链接脚本符号_free_start / _free_end(const u8 数组)运行时由 my_malloc.c 引用,地址由链接期决定
升级粒度dual_bank_demo.cflash_alignsizeFlash 擦写对齐粒度,决定循环迭代次数

注:以上均为构建期/链接期配置;运行时行为(如升级 Bank 切换)由 jlfs 与 Flash 控制器状态决定。

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

从源码可验证的失败模式与处理策略:

失败场景处理方式源码证据
Flash 擦除失败log_error 记录后 break 中止升级,不继续写入dual_bank_demo.c#L113-L116
升级期间看门狗超时每个扇区擦除后 wdt_clear() 喂狗;出错 break 后看门狗仍可复位系统dual_bank_demo.c#L118
新固件写入中断/损坏累计 CRC16(chip_crc16_with_init),通过 jlfs_check_dual_bank_info 校验dual_bank_demo.c#L133-L134、#L178
Bank 信息校验失败IOCTL_ERASE_SECTOR 擦除损坏 Bank 头部,回退旧 Bank 启动dual_bank_demo.c#L196
升级过程中断(断电)双 Bank 机制保证旧 Bank 完整,重启仍可引导整体设计(jlfs_get_idle_bank_info 只写空闲 Bank)

并发/时序注意:升级流程是单线程阻塞式长任务,Flash 擦写期间不响应其他业务;MCU 环境下由看门狗保证不会永久卡死。dual_bank_demo.c 中 my_free((void *)f_tmp_buf) 的调用表明临时缓冲区在流程结束或失败路径上需要释放——启动/升级代码中的内存泄漏会直接侵蚀有限的 RAM 堆。

性能与运维考量

  • 擦写粒度:以 flash_alignsize 对齐分块擦写,避免整片擦除,缩短单次升级的阻塞时间;块大小越小,喂狗频率越高,看门狗窗口越宽裕。
  • 流式 CRC:chip_crc16_with_init 逐块累计,无需为整个固件分配缓冲,内存占用恒定(tmp_buf/f_tmp_buf 级别),这是 MCU 场景的关键约束。
  • SFC 读取模式切换:IOCTL_SET_SFC_READ 在升级前后切换 Flash 控制器读取模式,避免高速缓存与直读模式不一致导致读到旧数据——该 IOCTL 的调用点(写入前/校验后)是排查"升级后启动异常"的首选检查位置。
  • 镜像生成可复现性:post_build/sh54/voice_toy/app_ld.c 在每次构建时执行,确保 Flash 布局与 BootROM 预期一致;修改平台宏后应重新构建而非复用旧镜像。

扩展点

  1. 新增芯片平台:仿照现有 Makefile.<platform>_<product> + <PLATFORM>_<product>.cbp 添加构建目标,并在链接脚本中导出正确的 _free_start/_free_end 符号。
  2. 新增产品形态:在 sdk/Makefile 总入口登记新的 Makefile.<platform>_<product> 组合,复用 sdk/app/bsp/common/ 公共代码。
  3. 差异化能力合入:参考 patch/AD14N_20250303_ANC_noise_reduction_patch/,以补丁形式向 sound_effect_list/ans_api.c 等公共模块合入芯片专属功能,保持平台基线稳定。
  4. 自定义升级策略:dual_bank_demo.c 是参考实现,可通过替换 jlfs 回调或实现自己的 Bank 切换逻辑来定制 OTA 流程(如多副本、签名校验)。
  5. 启动后置任务:在应用主循环之前插入初始化钩子(如外设自检、开机音效),需保证在 my_malloc 堆初始化与设备注册之后执行。

Related Links

  • fw-AD15N 仓库主页
  • README.md / README-en.md
  • 双 Bank 升级参考实现:dual_bank_demo.c
  • 堆初始化与链接符号:my_malloc.c
  • 构建后处理:app_ld.c
  • 平台 Makefile 示例:Makefile.ad15n_mcu、Makefile.ad18n_voice_toy
  • 音频解码子系统:decoder_api.c(详见"音频编解码框架"页)
  • 音效与 ANC 降噪:ans_api.c 及 AD14N ANC 补丁(详见"音效处理与 ANC 降噪"页)
Next
预编译库与头文件体系