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

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

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

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

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

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

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

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

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

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

BSP 板级支持包

BSP(Board Support Package,板级支持包)是 SDK 中位于应用层与芯片底层驱动之间的基础支撑层,负责系统启动初始化、板级事件分发与 Flash 存储子系统初始化,为上层应用提供统一的运行环境。

Purpose and Scope

本页面向 sdk/apps/app/bsp/ 目录下的板级支持能力,重点讲解:

  • 启动初始化(start/init.c 的 system_init()):系统上电后的初始化顺序与各阶段职责;
  • 板级事件循环(start/bsp_loop.c/h):基于位掩码 + CLZ 指令的高效事件分发机制,包括 bsp_post_event、bsp_get_event、bsp_loop;
  • Flash 子系统初始化(start/flash_init.c/h):Flash 存储初始化及其在启动序列中的位置;
  • 上述模块涉及的配置宏(TFG_EXT_FLASH_EN、BLE_EN、UPDATE_V2_EN、SYS_TIMER_EN 等)与扩展方式。

以下内容属于兄弟页面,不在本页展开:bsp/common/ 下的音频编解码器(decoder)、音频通路(audio)、均衡器(audio_eq)、配置文件工具(config)等具体功能模块各有独立页面;应用层升级流程(app_update_init)属于升级(Update)主题;驱动层 tick_timer_driver、device、vfs 等属于芯片驱动(Driver)主题。本页只说明 BSP 如何在启动序列中调用它们。

Overview

在嵌入式 SDK 中,BSP 层承担"承上启下"的职责:对上,它向应用提供 bsp_loop() 事件循环和 bsp_post_event() 事件投递接口;对下,它封装芯片的定时器(tick_timer_driver)、消息系统(msg)、设备驱动框架(device/devices_init_api)与 Flash 存储(flash_system_init)。

BSP 的核心设计思路可以归纳为三点:

  1. 启动顺序即依赖顺序。system_init() 严格按照"基础时钟 → 消息系统 → 设备框架 → Flash → 配置/升级"的顺序推进,每一步都为下一步提供前提条件。init.c 通过 #pragma 段属性将初始化代码放入专门的 .init.text 段,便于链接器在启动早期阶段布局与回收。
  2. 事件用位掩码表达。BSP 事件是一个 32 位整型 bsp_event,每一位代表一类事件(如 B_EVENT_100MS),通过 OS_ENTER_CRITICAL 临界区保护,可在中断上下文与任务上下文之间安全投递。
  3. 事件获取用 CLZ 单指令。bsp_get_event() 借助 CPU 的 clz(Count Leading Zeros)指令在常数时间内取出当前优先级最高的事件,无需遍历 32 位,体现了对 MCU 资源受限环境的极致优化。

Architecture

flowchart TD
    subgraph sg_App["应用层 (app)"]
        AppModule["App Modules"]
        BLE["BLE / BT 配置"]
        Update["升级模块 (UPDATE_V2)"]
    end

    subgraph sg_BSP["BSP 板级支持包 (sdk/apps/app/bsp)"]
        subgraph sg_Start["start/ 启动与事件"]
            Init["init.c<br/>system_init()"]
            Loop["bsp_loop.c/h<br/>事件循环"]
            Flash["flash_init.c/h<br/>flash_system_init()"]
        end
        subgraph sg_Common["common/ 公共组件"]
            Decoder["decoder 解码器"]
            Audio["audio 音频通路"]
            CfgTools["config 配置工具"]
        end
    end

    subgraph sg_Driver["芯片驱动层"]
        TickTimer["tick_timer_driver"]
        Msg["msg 消息系统"]
        Device["device / devices_init_api"]
        Vfs["vfs"]
    end

    AppModule -->|"bsp_post_event / bsp_loop"| Loop
    BLE -->|"cfg_bin_init / cfg_file_parse"| Init
    Update -->|"app_update_init"| Init
    Init --> TickTimer
    Init --> Msg
    Init --> Device
    Init --> Flash
    Flash --> Vfs
    Loop -->|"B_EVENT_100MS"| Flash
    AppModule --> Decoder
    AppModule --> Audio
    AppModule --> CfgTools
    Decoder --> Device
    Audio --> Device

架构说明:

  • **init.c(system_init)**是 BSP 的启动入口,按依赖顺序初始化 tick_timer、消息系统、设备框架和 Flash,并在条件编译宏开启时继续完成 BLE 配置解析与升级初始化。
  • bsp_loop.c/h 提供板级事件机制:bsp_event_init() 清零事件位,bsp_post_event() 在临界区中置位,bsp_loop() 取出事件并分发给处理逻辑(如 B_EVENT_100MS 触发 norflash 缓存同步)。
  • flash_init.c/h 提供 flash_system_init(),在配置解析(cfg_file_parse)之前完成 Flash 子系统就绪,保证上层可安全读写存储。
  • common/ 目录收纳音频解码、音频通路、配置工具等可复用的板级公共组件,属于本包但功能独立,各自有专题页面。

启动初始化:system_init()

system_init() 定义在 sdk/apps/app/bsp/start/init.c,是整个 SDK 在 BSP 层面的初始化入口。函数体很短,但每一行的调用顺序都体现了硬性的依赖关系:

void system_init(void)
{
    /* my_malloc_init(); */
#if SYS_TIMER_EN
    /* sys_timer_init(); */
#endif
    tick_timer_init();
    message_init();
    /* app_system_init(); */
    devices_init_api();
    flash_system_init();

#if defined(BLE_EN) && (1 == BLE_EN)
    cfg_bin_init();
    cfg_file_parse(0);
#endif

#if defined(UPDATE_V2_EN) && (1 == UPDATE_V2_EN)
    //升级初始化
    app_update_init();
#endif
}

来源:init.c

初始化阶段解读

阶段调用作用设计意图
1. 基础时钟tick_timer_init()初始化系统 tick 定时器驱动一切延时、超时、周期性事件的基础,必须先于任何依赖时间的模块就绪
2. 消息系统message_init()初始化内核消息队列任务间/中断间通信的前提,事件循环与各模块消息传递都依赖它
3. 设备框架devices_init_api()注册并初始化芯片设备驱动 API为上层(解码器、音频通路等)提供统一设备访问接口
4. Flash 存储flash_system_init()初始化 Flash 子系统(见 flash_init.h)必须在配置解析之前完成,保证配置读写安全
5. BLE 配置(可选)cfg_bin_init()、cfg_file_parse(0)解析二进制配置文件仅在 BLE_EN == 1 时编译;从 Flash 读取用户配置,供蓝牙协议栈使用
6. 升级初始化(可选)app_update_init()初始化固件升级框架仅在 UPDATE_V2_EN == 1 时编译;升级需要早期介入,因此放在初始化尾段

值得注意的两点设计:

  • 被注释的备选路径:my_malloc_init()、sys_timer_init()、app_system_init() 均被注释保留,说明这些模块在不同产品配置下可能由其它位置初始化或不再需要,代码保留了切换余地。
  • 条件编译隔离:BLE 与升级逻辑分别用 BLE_EN、UPDATE_V2_EN 宏包裹,保证未启用这些功能的产品不会引入额外代码与 RAM 占用。

段属性(#pragma)的意义

init.c 顶部通过一组 #pragma 将整个文件放入专用段:

#pragma bss_seg(".init.data.bss")
#pragma data_seg(".init.data")
#pragma const_seg(".init.text.const")
#pragma code_seg(".init.text")
#pragma str_literal_override(".init.text.const")

来源:init.c

这段代码把初始化数据(BSS/数据段)、只读常量与代码分别放到 .init.* 段中。这样链接脚本可以将启动期代码/数据与运行期代码/数据分离:BSP 初始化代码在完成使命后,这些段可以被复用或释放,节省宝贵的 RAM/Flash 资源——这是小型 MCU SDK 常见的"一次性初始化段"优化手法。

板级事件循环:bsp_loop

事件机制的核心数据结构是全局位掩码 bsp_event,配合 BSP_EVENT 枚举使用:

typedef enum {
    B_EVENT_100MS = 0,

    B_NO_EVENT = 32,
} BSP_EVENT;

void bsp_event_init(void);
int bsp_post_event(BSP_EVENT be);
int bsp_loop(void);

来源:bsp_loop.h

B_EVENT_100MS = 0 表示事件位 bit0;B_NO_EVENT = 32 并非一个真实事件位,而是"无事件"的哨兵值(32 位掩码中第 32 位不存在),同时它也是合法事件号的上界——bsp_post_event 正是用 be / 32 来校验事件号是否越界。

事件投递:bsp_post_event

int bsp_post_event(BSP_EVENT be)
{
    int err = 0;
    CPU_SR_ALLOC();
    OS_ENTER_CRITICAL();
    if (0  != (be / 32)) {
        err = E_BSP_EVENT;
    } else {
        bsp_event |= BIT(be);
    }
    OS_EXIT_CRITICAL();
    return err;
}

来源:bsp_loop.c

要点:

  • 临界区保护:OS_ENTER_CRITICAL/OS_EXIT_CRITICAL 关中断后对 bsp_event 做读改写,保证该接口可以从中断服务函数(ISR)中安全调用——投递方不限于任务上下文。
  • 越界校验:be / 32 != 0 即事件号 ≥ 32(或为负),返回错误码 E_BSP_EVENT,防止 BIT(be) 位移溢出导致未定义行为。
  • 幂等投递:同一事件多次投递只保留一个位,事件是"电平触发"而非"计数触发",语义是"某类事件至少发生过一次"。

事件获取:bsp_get_event

static BSP_EVENT bsp_get_event(void)
{
    u32 i;
    CPU_SR_ALLOC();
    OS_ENTER_CRITICAL();
    u32 event_cls;
    BSP_EVENT event = B_NO_EVENT;
    __asm__ volatile("%0 = clz(%1)":"=r"(event_cls):"r"(bsp_event));
    if (event_cls != 32) {
        event = 31 - event_cls;
        bsp_event &= ~BIT(event);
    }
    OS_EXIT_CRITICAL();
    return event;
}

来源:bsp_loop.c

要点:

  • CLZ 单指令取最高优先级事件:clz(Count Leading Zeros)统计 bsp_event 二进制表示中前导 0 的个数。若 event_cls = k,则最高置位位是第 31 - k 位,即当前数值最大(优先级最高)的事件。整个查找过程只需一条内联汇编指令,无循环、无分支预测开销。
  • 取走即清:取到事件后立即 bsp_event &= ~BIT(event) 清除该位,保证事件不会被重复处理;清除与读取在同一临界区内完成,避免与 bsp_post_event 竞争。
  • 无事件哨兵:clz(0) = 32,此时返回 B_NO_EVENT,调用方据此跳过处理。

事件处理:bsp_loop

int bsp_loop(void)
{
    u32 event = bsp_get_event();

    switch (event) {
    case B_EVENT_100MS:
#if TFG_EXT_FLASH_EN
        _norflash_cache_sync_timer(5);
#endif
        break;
    default:
        break;
    }
    return 0;
}

来源:bsp_loop.c

bsp_loop() 由上层应用周期性调用(通常放在主循环/空闲任务中):每次调用取一个事件并分发。当前仅处理 B_EVENT_100MS——在使能外部 Flash(TFG_EXT_FLASH_EN)时调用 _norflash_cache_sync_timer(5) 执行 norflash 缓存同步的定时步进,这是对 Flash 缓存一致性的周期维护。函数恒返回 0,default 分支吸收所有未知事件,保证循环不会被意外事件卡死。

Flash 子系统初始化

flash_system_init() 声明于 start/flash_init.h,在 system_init() 中被调用,位于 devices_init_api() 之后、cfg_bin_init()/cfg_file_parse(0) 之前。这一位置说明了它的依赖契约:

  • 上游依赖:依赖设备框架已就绪(Flash 是设备的一种,需要设备抽象层支持);
  • 下游提供:为配置解析(cfg_file_parse)与升级(app_update_init)提供可用的 Flash 读写能力。

换言之,Flash 是"配置系统与升级系统的地基",因此 BSP 必须在任何持久化访问发生前完成其初始化。bsp_loop 中的 _norflash_cache_sync_timer 则从运行期侧面维护 Flash 缓存一致性,与初始化阶段形成闭环。

核心流程

下面用时序图展示从系统上电到事件被处理的完整链路:

sequenceDiagram
    participant RST as 上电复位
    participant Init as init.c system_init()
    participant Tick as tick_timer_driver
    participant Msg as msg 消息系统
    participant Dev as devices_init_api
    participant Flash as flash_system_init
    participant ISR as 中断/定时器 ISR
    participant Loop as bsp_loop()
    participant Nor as _norflash_cache_sync_timer

    RST->>Init: 跳转至系统初始化
    Init->>Tick: tick_timer_init()
    Init->>Msg: message_init()
    Init->>Dev: devices_init_api()
    Init->>Flash: flash_system_init()
    Note over Flash: 配置/升级的前置条件
    Init-->>ISR: 初始化完成,系统进入主循环

    loop 主循环每次迭代
        ISR->>Loop: 周期触发 B_EVENT_100MS 投递
        ISR->>Loop: bsp_post_event(B_EVENT_100MS)
        Loop->>Loop: bsp_get_event()(临界区 + CLZ 取位)
        alt 取到 B_EVENT_100MS
            Loop->>Nor: TFG_EXT_FLASH_EN 开启时调用 sync_timer(5)
            Nor-->>Loop: 同步完成
        else 无事件
            Loop->>Loop: 返回 B_NO_EVENT,跳过处理
        end
        Loop-->>ISR: 返回 0,继续下一轮
    end

流程要点:

  1. 启动阶段:system_init() 按"时钟 → 消息 → 设备 → Flash"的依赖链完成 BSP 初始化;BLE 与升级逻辑由编译宏决定是否追加。
  2. 运行阶段:周期性事件源(如 100ms 定时器)在中断上下文中调用 bsp_post_event() 置位 bsp_event 位掩码。
  3. 分发阶段:主循环每次调用 bsp_loop(),经 bsp_get_event() 在临界区中执行 CLZ 取最高位事件并清除;switch 分发到具体处理分支。
  4. 无事件快速路径:掩码为 0 时直接返回 B_NO_EVENT,主循环开销极小,适合轮询式 MCU 架构。

使用示例

事件投递与处理(BSP 对外接口)

以下代码展示了 BSP 事件机制的完整使用形态——ISR/任务投递事件,主循环消费事件:

void bsp_event_init(void)
{
    CPU_SR_ALLOC();
    OS_ENTER_CRITICAL();
    bsp_event = 0;
    OS_EXIT_CRITICAL();
}

int bsp_post_event(BSP_EVENT be)
{
    int err = 0;
    CPU_SR_ALLOC();
    OS_ENTER_CRITICAL();
    if (0  != (be / 32)) {
        err = E_BSP_EVENT;
    } else {
        bsp_event |= BIT(be);
    }
    OS_EXIT_CRITICAL();
    return err;
}

来源:bsp_loop.c

bsp_event_init() 在临界区内清零掩码,用于系统启动早期复位事件状态;bsp_post_event() 是事件投递入口,返回 0 表示成功,返回 E_BSP_EVENT 表示事件号越界。

事件消费(主循环侧)

int bsp_loop(void)
{
    u32 event = bsp_get_event();

    switch (event) {
    case B_EVENT_100MS:
#if TFG_EXT_FLASH_EN
        _norflash_cache_sync_timer(5);
#endif
        break;
    default:
        break;
    }
    return 0;
}

来源:bsp_loop.c

上层应用在每次主循环迭代中调用 bsp_loop()。扩展新事件时,只需在 BSP_EVENT 枚举中新增成员(保持 < 32),并在 switch 中增加对应 case。

系统启动序列(BSP 初始化入口)

void system_init(void)
{
    tick_timer_init();
    message_init();
    devices_init_api();
    flash_system_init();

#if defined(BLE_EN) && (1 == BLE_EN)
    cfg_bin_init();
    cfg_file_parse(0);
#endif

#if defined(UPDATE_V2_EN) && (1 == UPDATE_V2_EN)
    //升级初始化
    app_update_init();
#endif
}

来源:init.c

这是板级初始化的标准调用序列,任何需要接入 BSP 的启动代码都应遵循"先基础、后外围"的顺序;条件编译块体现了按产品特性裁剪的能力。

配置选项

BSP 相关行为由编译期宏控制,宏的开关直接影响初始化路径与事件处理逻辑:

宏类型默认作用
SYS_TIMER_EN整型宏视工程配置控制系统定时器 sys_timer_init() 是否启用(当前 init.c 中该调用被注释保留)
BLE_EN整型宏视工程配置置 1 时编译 BLE 相关初始化:cfg_bin_init() 与 cfg_file_parse(0)
UPDATE_V2_EN整型宏视工程配置置 1 时编译升级框架初始化:app_update_init()(含 code_v2/update.h 路径)
TFG_EXT_FLASH_EN整型宏视工程配置置 1 时 B_EVENT_100MS 事件触发 _norflash_cache_sync_timer(5) 外置 Flash 缓存同步
B_EVENT_100MS枚举值0事件位索引(bit0),代表 100ms 周期性事件
B_NO_EVENT枚举值32"无事件"哨兵,同时作为合法事件号上界(事件号必须 < 32)

宏开关通过 #if defined(...) && (1 == ...) 方式使用,配置集中在工程级配置头文件中(app_config.h 体系),BSP 代码本身不定义默认值。

API 参考

void bsp_event_init(void)

复位板级事件状态。在临界区(OS_ENTER_CRITICAL)内将全局位掩码 bsp_event 清零。

参数: 无 返回: 无

int bsp_post_event(BSP_EVENT be)

向 BSP 事件位掩码投递一个事件,可在中断上下文调用。

参数:

  • be(BSP_EVENT):要投递的事件号,必须满足 0 <= be < 32。

返回:

  • 0:投递成功;
  • E_BSP_EVENT:事件号越界(be / 32 != 0)。

说明: 同一事件重复投递只保留一个位(幂等,电平语义)。

int bsp_loop(void)

取一个待处理事件并分发。每次调用至多处理一个事件;无事件时返回 B_NO_EVENT 对应的空转路径。当前实现处理 B_EVENT_100MS(外置 Flash 缓存同步),其余事件落入 default 分支忽略。

参数: 无 返回: 恒为 0(当前实现)。

void system_init(void)

BSP 层系统初始化入口。按依赖顺序调用 tick_timer_init → message_init → devices_init_api → flash_system_init,并按编译宏追加 BLE 配置解析与升级初始化。

参数: 无 返回: 无

void flash_system_init(void)

Flash 子系统初始化(声明于 flash_init.h)。由 system_init() 在设备框架初始化之后、配置解析之前调用。具体实现细节见 flash_init.c,本页不展开。

参数: 无 返回: 无

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

  • 事件号越界:bsp_post_event 对 be >= 32(含负数)返回 E_BSP_EVENT,调用方应检查返回值;若忽略错误,事件将不会被投递,可能表现为功能静默缺失。
  • 事件丢失语义(非计数):位掩码是"电平"语义,同一事件在两次 bsp_loop() 之间多次触发只保留一位。若应用需要统计触发次数,必须在 BSP 事件之上自行计数。
  • 并发安全:bsp_event 的读写全部在 OS_ENTER_CRITICAL/OS_EXIT_CRITICAL 临界区内完成。临界区会短暂关中断,投递频率过高可能影响中断实时性;bsp_post_event 本身开销极小,通常不是瓶颈。
  • 取事件与清位原子性:bsp_get_event 在同一临界区内完成"CLZ 取位 + 清位",避免事件被取出后、清除前被重复消费。
  • 未知事件:bsp_loop 的 default 分支吞掉未定义事件,保证主循环不因枚举扩展而崩溃;但新增事件若忘记在 switch 中添加 case,将表现为"事件被静默忽略"。
  • CLZ 对 0 的处理:clz(0) = 32 被显式判断为无事件(B_NO_EVENT),该分支是主循环热路径,开销仅一条指令加一次比较。

性能与运维

  • O(1) 事件获取:CLZ 单指令替代逐位遍历,32 位掩码下取事件恒定一条指令,适合高频轮询的主循环。
  • 极简空转路径:无事件时 bsp_loop() 仅执行"取事件 + 比较 + 返回",对 CPU 占用几乎为零,允许上层以高频率轮询。
  • 缓存一致性维护:B_EVENT_100MS 驱动的 _norflash_cache_sync_timer(5) 周期性同步外置 Flash 缓存,属运行期必需维护;若关闭 TFG_EXT_FLASH_EN,则此维护被完全编译掉,不产生运行时开销。
  • 一次性初始化段:.init.* 段将启动期代码/数据与运行期分离,链接器可复用或回收,降低 RAM/Flash 占用——在资源受限 MCU 上是显著的收益。

扩展点

  1. 新增板级事件:在 BSP_EVENT 枚举中追加成员(编号 < 32),在 bsp_loop() 的 switch 中增加处理分支。例如新增 B_EVENT_1S,在 1 秒定时器 ISR 中 bsp_post_event(B_EVENT_1S)。
  2. 调整初始化序列:system_init() 是唯一初始化编排点;新增模块可仿照 BLE/升级的写法,用条件编译宏包裹自己的初始化调用,置于正确的依赖位置。
  3. 启用/禁用外设:通过 TFG_EXT_FLASH_EN、BLE_EN、UPDATE_V2_EN、SYS_TIMER_EN 等宏裁剪功能,代码中对应的初始化调用与处理分支会被编译器整体移除。

测试

仓库中未发现针对 BSP 启动与事件循环的独立单元测试文件(sdk/apps/app/bsp/ 下无 test 目录)。该层代码依赖芯片硬件环境(中断、定时器、Flash),验证方式以板级联调为主:bsp_loop.c 中被注释的 log_info("post_event :0x%x\n", ...) 日志即为调试手段的痕迹,可临时开启日志观察事件投递与消费时序。

Related Links

  • init.c 系统初始化
  • bsp_loop.c 事件循环实现
  • bsp_loop.h 事件定义与接口
  • flash_init.h Flash 初始化声明
  • 相关主题:驱动层(tick_timer_driver、device、vfs)、升级流程(Update)、BLE 配置(bt_config_tool/user_cfg)——这些能力由各自专题页面覆盖
Prev
构建系统与批处理工具