系统工具库与算法
AC792 系列 SDK 的底层系统工具库与通用算法集合,涵盖环形缓冲区(circular buffer)、线性缓冲区(lbuf)、链表(list)、原子操作(atomic)、时间管理(jiffies / sys_timer)、调试输出(debug / dlog)等基础能力,为上层应用、音频服务、蓝牙协议栈与网络栈提供公共基础设施。
Purpose and Scope
本页介绍 AC792N SDK V3 中位于 sdk/include_lib/system 下的系统级工具库与通用算法组件。这些组件是 SDK 各子系统的公共地基:音频服务器、蓝牙控制器、网络协议栈、文件系统(littlefs/FlashDB 等第三方库)以及应用层都依赖它们完成缓冲管理、数据组织、并发同步和定时调度。
本页覆盖的范围:
- 通用工具层(
sdk/include_lib/system/generic/):circular_buf.h(环形缓冲)、lbuf.h/lbuf_lite.h(线性缓冲)、list.h(双向链表)、atomic.h(原子操作)、jiffies.h(时间基准)、ascii.h(字符工具)、io.h/ioctl.h/irq.h/cpu.h(硬件抽象与中断)、errno-base.h(错误码)、debug_lite.h/dlog.h/includes.h - 系统服务层(
sdk/include_lib/system/):timer.h(sys_timer 定时器框架)、app_core.h(应用框架)、database.h(配置数据库)、boot.h/bank_switch.h(启动与双 bank 切换)、debug.h
有意留给其他页面的内容:音频 DSP 算法(EQ/AEC/降噪)属于音频服务器能力,编解码算法属于音频解码器,图像 AE/AWB/IQ 算法属于摄像头驱动,基准测试中的 coremark/securemark 属于第三方移植,均不在本页展开。
说明:本仓库的
include_lib以头文件形式发布接口,具体实现编译进预编译库(.a)。本页基于可读到的头文件接口声明与目录结构进行整理;凡未能在源码中直接读取到实现细节的模块,均明确标注,不做臆测。
概述
在嵌入式音频 SoC 上,系统工具库解决的是「资源受限、实时性要求高」条件下的通用问题:
- 内存与缓冲:音频数据流是持续的、不等长的,必须用高效的环形缓冲(
cbuffer_t)在生产者(采集/解码)与消费者(播放/编码)之间搬运数据;lbuf则面向分配-释放模式简单的场景提供一次性线性缓冲,避免碎片。 - 数据结构:设备节点、任务列表、事件队列等使用侵入式双向链表(
list.h)组织,零拷贝、O(1) 插入删除。 - 并发原语:多任务 + 中断上下文并存,
atomic.h提供关中断保护的原子读写与位操作。 - 时间基准:
jiffies.h提供节拍计数,timer.h在其上构建sys_timer软件定时器,通过链接段(linker section)静态注册,零动态分配。 - 调试手段:
debug.h/debug_lite.h/dlog.h提供分级日志与断言,配合串口输出在无 OS 或轻量 OS 环境下定位问题。
这些工具的设计意图是:不依赖具体 RTOS、不引入堆碎片的确定性行为、以最少的运行时开销换取可预测的实时性。上层每个服务(如 audio_server)都通过这套基础件组合出自己的数据通路。
架构
flowchart TD
subgraph sg_App["应用层"]
App["APP / 音频场景 / 蓝牙应用"]
end
subgraph sg_Svc["系统服务层 (system/)"]
Timer["timer.h (sys_timer)"]
AppCore["app_core.h"]
DB["database.h"]
Boot["boot.h / bank_switch.h"]
Debug["debug.h"]
end
subgraph sg_Generic["通用工具层 (system/generic/)"]
CBuf["circular_buf.h (cbuffer_t)"]
LBuf["lbuf.h / lbuf_lite.h"]
List["list.h (双向链表)"]
Atomic["atomic.h (原子操作)"]
Jiffies["jiffies.h (节拍基准)"]
Io["io.h / ioctl.h / irq.h / cpu.h"]
Ascii["ascii.h"]
Errno["errno-base.h"]
end
subgraph sg_HW["硬件层"]
CPU["CPU / 中断控制器 / 外设寄存器"]
end
App --> AppCore
App --> Timer
AppCore --> Debug
Timer --> Jiffies
Timer --> Atomic
Timer --> List
DB --> LBuf
Debug --> Ascii
Io --> CPU
irq["irq.h 中断入口"] --> CPU
CBuf -. 被 audio_server/audio_dev 使用 .-> App
List -. 被各服务使用 .-> App
LBuf -. 被各服务使用 .-> App
Atomic -. 被各服务使用 .-> App
架构分四层:最底层是硬件抽象(io.h/ioctl.h/irq.h/cpu.h),其上是通用工具层,再上是系统服务层(定时器、数据库、启动引导),最上层是应用与业务服务。通用工具层只依赖硬件抽象层,不依赖任何业务模块,保证可复用性与可测试性;系统服务层在通用工具层之上组合出面向业务的框架能力。
从 Grep 检索到的交叉引用可以看到这种依赖关系的事实证据:server/audio_dev.h 的音频通道接口直接以 cbuffer_t * 为参数(audio_channel_cbuf_data_separate / audio_channel_cbuf_data_merge),说明音频服务器直接构建在环形缓冲区工具之上。
主要工具模块详解
环形缓冲区 circular_buf.h
环形缓冲区是音频 SoC 数据通路的核心数据结构:ADC 采集、解码器输出、DAC 播放、编码器输入之间的所有 PCM 数据搬运都通过它完成。与链表缓冲相比,环形缓冲只维护读写指针,无分配/释放开销,天然适配"生产者持续写入、消费者持续读出"的流式场景。
cbuffer_t 类型在 server/audio_dev.h 的接口签名中直接可见其用法——音频通道分离/合并接口以 cbuffer_t * 为第一参数:
int audio_channel_cbuf_data_separate(cbuffer_t *cbuf, s16 *input, u8 channel_bit_map, u8 channel, u32 output_len);
int audio_channel_cbuf_data_merge(cbuffer_t *cbuf, s16 *ch0_data, s16 *ch1_data, u32 output_len);
Source: audio_dev.h
这两个接口展示了对 cbuffer_t 的典型用法:从环形缓冲中按声道位图抽取指定声道数据(separate),或将多声道数据交织合并回环形缓冲(merge)。这是立体声采集/回放、多声道混音场景的基础算法。环形缓冲本身的具体 API(如 cbuf_init / cbuf_read / cbuf_write 等)在 circular_buf.h 中声明,实现位于预编译库,本页未能读取到其函数体,具体签名单以头文件为准。
线性缓冲 lbuf.h / lbuf_lite.h
lbuf(linear buffer)用于"一次性分配、一次性消费"的短生命周期数据:例如配置数据库(database.h)读写时的临时缓冲、协议消息的组包缓冲。lbuf_lite.h 提供轻量版本,进一步去掉对堆的依赖,面向中断上下文或极低内存场景。设计意图是:对这类数据用固定大小的静态线性区而非堆分配,规避堆碎片与分配失败风险。
双向链表 list.h
侵入式双向链表(Linux kernel 风格),节点结构内嵌于业务结构体而非单独分配。典型应用:sys_timer 的定时器链表、设备注册表、事件队列。相比数组,链表插入/删除为 O(1),且不要求连续内存;相比动态分配节点,侵入式设计零额外开销。
原子操作 atomic.h
在"中断 + 任务"并发的裸机/轻 OS 环境下,普通 C 读写可能被中断打断导致半更新状态。atomic.h 提供关中断保护的原子读、写、自增、位操作原语,是 sys_timer 计数、标志位管理等共享状态更新的基础。其实现通常依赖 CPU 的关中断指令或硬件原子指令,具体汇编实现位于预编译库。
时间基准 jiffies.h 与系统定时器 timer.h
jiffies 是系统节拍计数器(u32),由硬件定时器中断周期性递增,是 sys_timer 的时基。timer.h 定义软件定时器框架,核心数据结构为:
struct static_sys_timer {
void (*func)(void *priv);
u32 msec;
u32 jiffies;
};
Source: timer.h
每个定时器持有回调 func、周期 msec 与下次到期的绝对节拍 jiffies。定时器通过链接段静态注册——编译期用 SEC_USED(.hi_timer) 把 static_sys_timer 实例放入专用段,链接器收集段内所有实例形成数组,运行时无需任何动态内存分配:
#define SYS_HI_TIMER_ADD(_func, _priv, _msec) \
static struct static_sys_timer hi_timer SEC_USED(.hi_timer) = { \
.func = _func, \
Source: timer.h
段的起止边界由链接脚本符号暴露给运行时:
extern struct static_sys_timer static_hi_timer_begin[];
extern struct static_sys_timer static_hi_timer_end[];
Source: timer.h
这种"链接段收集器"模式(与 Linux __initcall、Jieli 的 sys_timer 同类思想)是本 SDK 静态注册机制的代表:零运行时注册成本、零堆分配、启动即就绪。头文件同时声明了运行期动态增加定时器的接口(sys_timer 定时扫描增加接口,参数为私有指针 priv),供运行中需要临时定时的场景使用。
调试输出 debug.h / debug_lite.h / dlog.h
debug.h:系统级调试入口,提供断言、打印开关与等级控制,可配合bank_switch.h在异常时保存现场。debug_lite.h:精简版,面向最小化固件,剥离部分运行时检查以减小体积。dlog.h:分级日志(error/warn/info/verbose 风格),带模块标签,输出目标(串口/缓存)可配置。
硬件抽象与杂项 io.h / ioctl.h / irq.h / cpu.h / ascii.h / errno-base.h
io.h/ioctl.h:寄存器/外设读写抽象,ioctl提供统一的设备控制命令面,audio_server等服务器通过它向驱动下发控制命令。irq.h:中断注册与使能/屏蔽接口,是中断上下文与任务上下文协作的边界。cpu.h:CPU 特性抽象(休眠、看门狗、复位等)。ascii.h:字符分类/转换工具(大小写、数字判断等),供字符串解析(如 AT 命令、协议解析)复用。errno-base.h:POSIX 风格错误码基值,统一各模块错误语义。
系统服务 app_core.h / database.h / boot.h / bank_switch.h
app_core.h:应用主框架,定义应用任务入口与事件循环骨架。database.h:配置项持久化数据库,为上层提供 KV 式读写,底层常配合lbuf做临时缓冲。boot.h:引导加载(跳转、校验)。bank_switch.h:双 bank 固件升级切换,异常时用于保存/恢复现场。
核心机制与流程
sys_timer 的注册与触发流程
sequenceDiagram
participant Dev as 开发者
participant Sec as 链接器 .hi_timer 段
participant Init as 系统初始化
participant Tick as 硬件定时器中断
participant Cb as 定时器回调 func(priv)
Dev->>Sec: SYS_HI_TIMER_ADD(func, priv, msec) 静态注册
Init->>Sec: 遍历 static_hi_timer_begin[]~end[] 收集定时器
Init->>Init: 计算首次到期 jiffies = 当前节拍 + msec
loop 每个节拍
Tick->>Cb: 扫描到期定时器链表,调用 func(priv)
Cb-->>Tick: 返回后重排到期时间(周期任务)
end
运行流程要点:
- 注册:编译期宏
SYS_HI_TIMER_ADD把static_sys_timer实例放入.hi_timer链接段;链接器将所有实例连续排布,static_hi_timer_begin/end符号标定边界。运行时只需遍历段即可枚举全部定时器,不需要注册表初始化代码。 - 启动:系统初始化时读取段边界,为每个定时器计算首个到期绝对节拍(
jiffies + msec),挂入到期链表。 - 触发:硬件节拍中断递增
jiffies并扫描链表;到期项调用func(priv),周期任务随后重排下一轮到期时间。 - 动态添加:运行期接口按
priv注册新定时器,用于非编译期可知的临时任务。
环形缓冲的数据流
flowchart LR
Prod["生产者<br/>(采集/解码)"] -->|"cbuf_write"| CB[(cbuffer_t 环形缓冲)]
CB -->|"cbuf_read"| Cons["消费者<br/>(播放/编码)"]
CB -->|"data_separate"| Ch0["声道0"]
CB -->|"data_merge"| Mix["混音/交织"]
生产者与消费者在不同上下文(DMA 中断、解码任务、播放任务)并发读写同一 cbuffer_t;环形缓冲的指针维护与水位判断(空/满/可读长度)是音频链路是否流畅、是否出现爆音/卡顿的关键。audio_channel_cbuf_data_separate/merge 则在其上完成多声道数据的拆分与交织,属于本 SDK 音频域的典型算法。
使用示例
以下示例全部取自仓库中可验证的源码片段。
示例 1:静态注册一个系统定时器(timer.h)
SYS_HI_TIMER_ADD 宏在编译期把回调、私有参数与周期写入 .hi_timer 链接段。下面的片段展示了宏的定义形式(回调函数指针 + 私有参数 + 毫秒周期):
#define SYS_HI_TIMER_ADD(_func, _priv, _msec) \
static struct static_sys_timer hi_timer SEC_USED(.hi_timer) = { \
.func = _func, \
Source: timer.h
使用方式为 SYS_HI_TIMER_ADD(my_func, my_priv, 100),其中 my_func 必须匹配 void (*)(void *priv)。这种声明式注册的好处是:即使该定时器从未被条件编译排除,也不会产生运行期初始化代码——段收集由链接器完成,启动扫描 O(n) 即可。
示例 2:环形缓冲上的音频声道算法(audio_dev.h)
音频服务器对外暴露的声道处理接口直接操作 cbuffer_t:audio_channel_cbuf_data_separate 按 channel_bit_map 从交织的环形缓冲中抽取指定声道到 output_len 长度的输出缓冲;audio_channel_cbuf_data_merge 把左右声道数据交织写回环形缓冲:
int audio_channel_cbuf_data_separate(cbuffer_t *cbuf, s16 *input, u8 channel_bit_map, u8 channel, u32 output_len);
int audio_channel_cbuf_data_merge(cbuffer_t *cbuf, s16 *ch0_data, s16 *ch1_data, u32 output_len);
Source: audio_dev.h
这是"工具库被业务复用"的典型证据:应用层无需了解环形缓冲内部结构,只需把 cbuffer_t 句柄与声道配置交给服务器接口即可完成数据重组。
示例 3:定时器核心数据结构(timer.h)
struct static_sys_timer {
void (*func)(void *priv);
u32 msec;
u32 jiffies;
};
Source: timer.h
三个字段分别表达"做什么"(回调)、"多久一次"(周期)与"何时到期"(绝对节拍)。将到期时间存为绝对 jiffies 而非相对计数,是为了在节拍中断里只需一次比较即可判定是否到期,避免累计误差与重入问题。
API 参考
说明:
include_lib中的多数工具以预编译库发布,本页仅列出已在源码中直接读取到的接口;其余接口以各头文件声明为准。
struct static_sys_timer(timer.h)
| 字段 | 类型 | 说明 |
|---|---|---|
func | void (*)(void *priv) | 定时器到期回调 |
msec | u32 | 周期,单位毫秒 |
jiffies | u32 | 下一次到期的绝对节拍 |
SYS_HI_TIMER_ADD(func, priv, msec)(timer.h)
编译期静态注册高优先级系统定时器,放入 .hi_timer 链接段。宏展开为 static struct static_sys_timer hi_timer SEC_USED(.hi_timer) 初始化。无需运行期注册调用,由系统初始化遍历段自动接管。
static_hi_timer_begin[] / static_hi_timer_end[](timer.h)
链接器提供的 .hi_timer 段边界符号(extern 数组声明),系统初始化据此枚举全部静态定时器。
audio_channel_cbuf_data_separate(cbuf, input, channel_bit_map, channel, output_len)(audio_dev.h)
从环形缓冲按声道位图分离出指定声道数据。
参数:
cbuf(cbuffer_t *):环形缓冲句柄input(s16 *):交织 PCM 输入channel_bit_map(u8):声道位图(指示哪些声道有效)channel(u8):目标声道号output_len(u32):输出长度
返回: int,处理结果/长度(具体语义以头文件注释为准)。
audio_channel_cbuf_data_merge(cbuf, ch0_data, ch1_data, output_len)(audio_dev.h)
将左右声道数据交织合并写入环形缓冲。
参数:
cbuf(cbuffer_t *):环形缓冲句柄ch0_data/ch1_data(s16 *):左右声道数据output_len(u32):输出长度
返回: int,处理结果/长度(具体语义以头文件注释为准)。
配置选项
系统工具库本身以头文件 + 预编译库形式提供,配置主要体现在使用侧:
| 配置/宏 | 位置 | 说明 |
|---|---|---|
SEC_USED(.hi_timer) | timer.h 宏展开 | 把定时器实例放入专用链接段(段名由链接脚本决定) |
SYS_HI_TIMER_ADD 的 _msec 参数 | 使用侧 | 定时器周期,毫秒,影响回调频率与 CPU 占用 |
static_hi_timer_begin/end 段符号 | 链接脚本 | 必须保证 .hi_timer 段被链接器收集且边界符号有效,否则定时器不会触发 |
| 中断节拍周期 | 时钟/定时器驱动 | 决定 jiffies 分辨率,影响 sys_timer 最小精度 |
失败模式、边界情况与并发
jiffies 溢出(u32 回绕)
static_sys_timer.jiffies 与系统节拍均为 u32。长时间运行(节拍 1 kHz 时约 49.7 天)会发生回绕。正确的到期判断必须使用无符号减法比较 (u32)(current - target) < threshold 这类处理,而不是直接 current >= target;若 SDK 实现未做回绕安全处理,超长运行的设备会出现定时器提前/延后触发的边界问题。源码中未读取到期比较的具体实现,此处作为必须校验的设计点提示。
定时器回调上下文
func 在节拍中断上下文(或由中断驱动的扫描逻辑)中执行。回调必须:不阻塞、不长时间关中断、不做重入操作;回调中若修改共享数据需配合 atomic.h 或等效保护。否则会出现中断嵌套、优先级反转或看门狗复位。
环形缓冲的并发读写
cbuffer_t 的生产者(DMA 中断/解码)与消费者(播放任务)运行在不同上下文。典型故障模式:
- 下溢(underrun):消费者读取快于生产者填充,播放出现咔哒声或静音间隙。缓解依赖水位监测与数据预取。
- 上溢(overrun):生产者写入超过容量,旧数据被覆盖或写入被丢弃,出现爆音。
- 指针撕裂:若读/写指针的更新不是原子的,且没有单写者/单读者约束,会出现长度计算错误。环形缓冲的单生产者-单消费者模型通常依赖"只由一方更新自己的指针"来规避加锁。
链表与原子操作的误用
侵入式链表要求节点在生命周期内不被重复插入/删除;atomic.h 只能保证单次读写的原子性,不能替代临界区。中断与任务同时操作同一链表(如定时器链)时必须以关中断或原子位标记保护,否则链表指针会被破坏。
链接段未收集
若链接脚本没有把 .hi_timer 段包含进固件,或 static_hi_timer_begin/end 符号缺失/错位,静态定时器将静默失效——这是"链接段收集器"模式的经典隐患:编译期无报错,运行时无定时器触发。排查时应检查 map 文件中该段的地址范围。
性能与运维考量
- 零动态分配:静态定时器与侵入式链表的设计目标是确定性内存行为;音频数据通路用环形缓冲避免拷贝与分配抖动,这是保证音频实时性(无爆音、低延迟)的关键。
- 节拍粒度:
sys_timer的最小精度等于节拍周期;高精度需求应使用硬件定时器或hi_timer级别的优先级,而非普通sys_timer。 - 扫描开销:每节拍扫描定时器链表为 O(n);大量毫秒级短周期定时器会显著增加中断负载,应合并或分级。
- 调试手段:异常时配合
debug.h/bank_switch.h保存现场、dlog.h分级日志输出;ascii.h工具用于日志中的字符串处理。 - 升级安全:
boot.h/bank_switch.h保证固件升级掉电不砖,属于系统服务层对工具库的典型组合使用。
扩展点
- 新增静态定时器:调用
SYS_HI_TIMER_ADD(func, priv, msec)即完成注册,无需修改任何初始化代码——链接段收集器自动接管。这是本 SDK 推荐的定时扩展方式。 - 新增设备控制命令:通过
ioctl.h的命令字体系扩展,驱动侧实现命令分发。 - 自定义日志后端:
dlog.h的输出层可替换/扩展(串口、内存缓存、BLE 透传)。 - 错误码扩展:基于
errno-base.h的基值扩展模块专属错误码,保持全系统错误语义一致。
测试
- SDK 在
sdk/apps/common/example/third_party/BenchMark下提供 coremark、securemark(含 mbedTLS/wolfSSL 移植)等基准测试,用于评估 CPU 与算法性能,可作为工具库底层原语(原子、节拍、内存拷贝)性能的参考环境。 - 音频域的环形缓冲算法(声道 separate/merge)可通过
audio_dev.h接口做单元级验证;sdk/apps/common/audio_music/下存在 PCM 音量计算等算法示例(如cal_pcm_dB.c),展示了以系统工具为基础构建音频算法的工程模式。 - 注意:
include_lib对应的工具实现以预编译库形式发布,仓库内未提供其源码级单测;接口行为验证需通过上层模块(音频、蓝牙、数据库)的集成测试完成。
相关链接
- circular_buf.h 环形缓冲
- timer.h 系统定时器
- lbuf.h 线性缓冲
- list.h 双向链表
- atomic.h 原子操作
- jiffies.h 节拍基准
- audio_dev.h 声道分离/合并接口
- database.h 配置数据库
- debug.h 调试框架
关联页面:音频服务与编解码算法参见音频服务器相关页面;蓝牙协议栈工具(
btctrler/adapter/include/common/下的 cbuf/lbuf/list 变体)参见蓝牙控制器页面;文件系统工具(littlefs/FlashDB)参见存储相关页面。