资讯动态

杰理AC69xx SDK嵌入式开发实战:从环境搭建到ANC调试

发布时间:2026/10/16 15:52:51 来源:尧图企业网站定制
简介杰理AC69xx系列SDK文件是一套面向嵌入式开发工程师的完整蓝牙音频固件开发套件专为AC69xx系列SoC芯片设计覆盖蓝牙协议栈、音频编解码AAC/WMA、电源管理、硬件外设驱动等核心功能适用于蓝牙音箱、TWS耳机等低功耗音频设备的快速原型开发与量产适配。资源共554个文件包含95个C源码、186个头文件、96个目标文件及48个静态库如bluetooth_lib.a、aac_dec_lib.a、usb_lib.a等辅以配置脚本.bat/.sh、编译工具链、示例工程apps_soundbox和专用工具bt_config_tools_2.1.0.4包体25.56MB结构清晰、模块分层明确便于按功能模块深入学习与裁剪复用。目前已有13252人学习下载开发者可直接获取成熟可编译的SDK框架、关键驱动实现逻辑、蓝牙配置实操路径及补丁应用指南AC690X_补丁.pdf显著降低AC69xx平台入门门槛与调试周期。1. 杰理AC69xx系列SDK文件不是“拿来就能编译”的压缩包而是嵌入式音频开发的钥匙串你下载了杰理官网或某论坛分享的AC692x_SDK_V3.2.12.zip解压后看到project/,driver/,middleware/,tools/,doc/一整套目录兴奋地打开project/ble_headset/想跑个 demo——结果make报错arm-none-eabi-gcc: command not found装好工具链又卡在ld: cannot find -lbtstack好不容易过了链接烧录进芯片却无声无息串口只吐乱码。这不是你手残是 AC69xx SDK 的典型入门门槛它不提供开箱即用的 IDE 工程不内置完整工具链不带图形化配置界面甚至关键驱动如 USB Audio、ANC的源码被封装成.a静态库头文件里还藏着条件编译宏的玄学开关。它面向的是熟悉 ARM Cortex-M0 架构、能看懂汇编启动文件、敢改link.ld脚本、习惯用objdump反查符号的嵌入式音频固件开发者。如果你正为 TWS 耳机做定制功能双耳同步降噪参数下发、低延迟游戏模式、自定义触控逻辑或需要把 AC6925/AC6935 接入自有语音唤醒引擎这套 SDK 就是你绕不开的底层接口层——但前提是你得先把它从“一堆文件”变成“可调试、可复现、可交付的构建系统”。本文就带你亲手拧开这把钥匙串不依赖任何第三方 IDE 插件纯命令行 Makefile 手动配置把 SDK 真正落地到你的开发流中。2. 搭建可复现的本地构建环境从裸机工具链到 SDK 初始化AC69xx SDK 的构建高度依赖特定版本的 GNU Arm Embedded Toolchain 和杰理私有工具如ac_tool、ac_flash版本错配是 80% 初学者翻车的第一步。官方文档常写“推荐使用 GCC 9.2.1”但实际测试发现GCC 9.2.1 编译btstack时会因-fno-common默认开启导致符号重定义错误而 GCC 10.3.1 在__aeabi_memcpy内联优化上又与 SDK 的libc实现有冲突。经实测GCC 9.3.1 2020.042020 年 4 月发布版是当前最稳定的组合它平衡了 C99 兼容性、内联行为和链接器脚本解析能力。2.1 安装并验证 ARM 工具链# 下载官方推荐镜像注意必须是 2020.04 版本非最新 wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/9-2020q2/gcc-arm-none-eabi-9-2020-q2-update-linux.tar.bz2 tar -xjf gcc-arm-none-eabi-9-2020-q2-update-linux.tar.bz2 -C /opt/ export PATH/opt/gcc-arm-none-eabi-9-2020-q2-update/bin:$PATH # 验证版本与关键特性 arm-none-eabi-gcc --version # 应输出gcc version 9.3.1 20200408 (release) arm-none-eabi-gcc -dumpspecs | grep -A5 startfile # 必须看到 crt0.o 路径指向 /opt/gcc-arm-none-eabi-9-2020-q2-update/arm-none-eabi/lib/crt0.o提示不要用apt install gcc-arm-none-eabi安装系统包——Ubuntu 22.04 自带的是 GCC 11会导致__aeabi_idiv符号缺失Mac 用户请用brew install arm-gcc-bin并指定--version9.3.1避免 Homebrew 默认拉取新版。2.2 初始化 SDK 根目录结构与环境变量AC69xx SDK 不自带顶层 Makefile需手动创建Makefile.sdk并注入路径。关键点在于TOOLCHAIN_PATH必须绝对路径SDK_ROOT必须是解压后的顶层目录含project/,driver/等且不能有中文或空格。# 文件$SDK_ROOT/Makefile.sdk # 注意此文件需放在 SDK 解压后的根目录下与 project/ 同级 SDK_ROOT : $(abspath .) TOOLCHAIN_PATH : /opt/gcc-arm-none-eabi-9-2020-q2-update CC : $(TOOLCHAIN_PATH)/bin/arm-none-eabi-gcc AR : $(TOOLCHAIN_PATH)/bin/arm-none-eabi-ar OBJCOPY : $(TOOLCHAIN_PATH)/bin/arm-none-eabi-objcopy SIZE : $(TOOLCHAIN_PATH)/bin/arm-none-eabi-size # SDK 内部路径映射不可更改否则 driver/ 头文件找不到 INC_DIRS : \ $(SDK_ROOT)/include \ $(SDK_ROOT)/driver/include \ $(SDK_ROOT)/middleware/include \ $(SDK_ROOT)/project/common/include CFLAGS : -mcpucortex-m0plus -mthumb -mfpuvfp -mfloat-abihard \ -O2 -g3 -Wall -Wno-unused-variable -Wno-unused-function \ -I$(INC_DIRS) \ -D__AC6925__ -DCHIP_AC6925 -DUSE_USB_AUDIO # 生成依赖文件关键否则改头文件不会触发重编译 DEPFLAGS -MMD -MP这段 Makefile 不是最终构建脚本而是 SDK 的“环境基座”。它定义了所有子项目如project/ble_headset/共用的编译器、宏定义和头文件路径。-D__AC6925__是芯片型号硬编码开关若你用 AC6935必须改为-D__AC6935__否则driver/下的寄存器定义会错位——这是血泪经验某开发者因漏改此宏ADC 采样值始终为 0查了三天才发现是REG_ADDR_ADC_CTRL宏展开到了错误地址。2.3 验证 SDK 基础构建能力编译一个最小裸机工程进入project/目录选择最简工程bare_metal部分 SDK 版本叫led_blinkcd $SDK_ROOT/project/bare_metal # 创建软链接让 Makefile 找到 SDK 根 ln -sf ../../Makefile.sdk Makefile.sdk # 编译注意此处不调用 SDK 自带的 build.sh而是用我们定义的规则 make -f Makefile.sdk all成功后应生成build/bare_metal.elf可执行镜像build/bare_metal.bin烧录用二进制build/bare_metal.map内存布局图必查参数说明-mfloat-abihard是强制要求——AC69xx 的 FPU 是硬浮点单元若设为soft或softfp所有浮点运算会跳转到软件模拟库性能暴跌 10 倍且printf(%f)直接崩溃。-O2是 SDK 官方要求的最低优化等级-O0会导致btstack的状态机调度异常。3. 解析 SDK 的核心分层结构为什么 driver/ 里的代码不能直接改AC69xx SDK 表面是扁平目录实则暗藏三层抽象硬件抽象层HAL、中间件服务层Middleware、应用工程层Project。理解这三层的职责边界是避免“改了 driver 却没效果”或“加了功能却爆内存”的前提。3.1 HAL 层driver/ 目录下的寄存器战争driver/不是标准外设驱动而是杰理对 AC69xx SoC 内部模块的寄存器直操作封装。例如driver/src/adc.c中void adc_init(u8 ch, u8 sample_rate) { // 关键直接写芯片手册第 3.2.1 节定义的 ADC 控制寄存器 REG_ADC_CTRL (ch 8) | (sample_rate 4) | BIT(0); // BIT(0)ADC_EN while (!(REG_ADC_STATUS BIT(1))); // 等待 ADC_READY }这里没有HAL_ADC_Init()这样的抽象REG_ADC_CTRL是driver/include/ac6925_reg.h中用#define REG_ADC_CTRL (*(volatile u32*)0x40002000)定义的物理地址。这意味着你不能随意增删driver/src/中的函数因为middleware/层如btstack在初始化时会直接调用adc_init()若你修改adc.c中的采样率计算逻辑必须同步更新middleware/btstack/src/le_audio.c里对adc_init()的调用参数否则蓝牙音频流会断续。3.2 Middleware 层静态库的黑匣子与头文件的密码本middleware/目录下常见libbtstack.a、libusb_audio.a、libanc.a。这些不是源码是杰理提供的预编译库。但它们的使用接口全靠middleware/include/下的头文件定义。例如启用主动降噪ANC// middleware/include/anc.h typedef struct { u16 mic_gain; // 麦克风增益0~255 u16 feedforward_tap[32]; // 前馈滤波器系数32 个 16bit 整数 u16 feedback_tap[16]; // 反馈滤波器系数16 个 16bit 整数 } anc_config_t; void anc_enable(anc_config_t *cfg);玄学参数说明feedforward_tap[]数组不是随便填的 FIR 系数——它必须是杰理 ANC 调试工具AC_ANC_Tuner.exe导出的.anc文件解密后的内容。该工具生成的系数经过定点化处理Q12 格式若你用 MATLAB 设计的浮点系数直接赋值ANC 会完全失效。我一般会用 SDK 自带的tools/anc_coeff_convert.py脚本转换python tools/anc_coeff_convert.py -i my_design.fir -o anc_coef.bin --qformat Q12。3.3 Project 层如何安全地添加自定义功能而不破坏蓝牙协议栈project/下每个工程如ble_headset本质是main.c 一组回调函数注册。添加新功能如双击播放/暂停的唯一安全路径是在project/ble_headset/src/app_main.c的app_key_event_handler()函数中扩展而非新建线程或中断。// project/ble_headset/src/app_main.c void app_key_event_handler(u8 key, u8 event) { switch(key) { case KEY_PLAY_PAUSE: if (event KEY_EVENT_DOUBLE_CLICK) { // 安全调用 SDK 提供的媒体控制 API media_ctrl_send_cmd(MEDIA_CMD_PLAY_PAUSE); // ❌ 错误在此处直接调用 btstack_send_acl() 发送自定义 HCI 包 // ❌ 错误启动新 taskos_task_create(..., my_custom_task, ...); } break; // ... 其他按键 } }原因media_ctrl_send_cmd()内部已处理了蓝牙 AVRCPT 协议的状态同步、重传机制和缓冲区管理而裸发 HCI 包会绕过协议栈状态机导致手机端显示“设备未响应”。4. 烧录、调试与信号观测让芯片真正“开口说话”编译通过只是开始AC69xx 的调试难点在于它没有 SWD/JTAG 调试接口仅保留 UART0 用于日志所有调试依赖串口打印 逻辑分析仪抓波形 SDK 内置的trace功能。没有这三板斧你永远不知道anc_enable()是否真的生效。4.1 UART0 日志不只是 printf而是状态机探针AC69xx 默认将printf()重定向到 UART0PA0/PA1但默认波特率是20000002Mbps不是常见的 115200。用普通串口助手会看到乱码。# Linux 下用 stty 设置高速波特率 stty -F /dev/ttyUSB0 2000000 raw -echo cat /dev/ttyUSB0 # 此时才能看到清晰日志更关键的是SDK 的日志开关分散在多处project/xxx/config.h中#define LOG_ENABLE 1middleware/btstack/config.h中#define BTSTACK_LOG_LEVEL 3driver/src/uart.c中uart_init(0, 2000000)—— 必须与硬件连接一致血泪经验某项目中 ANC 开启后耳机啸叫串口日志显示ANC_INIT_OK但ANC_PROCESS_LOOP从未打印。最终发现是driver/src/anc_hw.c中ANC_HW_ENABLE()函数里REG_ANC_CTRL | BIT(7)使能 ANC 硬件被误写为BIT(6)导致硬件根本没启动。日志成了“假阳性”必须配合逻辑分析仪验证寄存器写入。4.2 用逻辑分析仪抓取关键信号验证硬件是否真在干活AC69xx 的 GPIO 可配置为 debug trace 输出。在project/xxx/src/app_main.c中启用// 在 app_main() 开头添加 gpio_set_func(GPIO_DEBUG_PIN, GPIO_FUNC_DEBUG); // 如 GPIO_DEBUG_PIN GPIO_PA2 debug_trace_enable(DEBUG_TRACE_ANC_START); // 启用 ANC 启动标记 debug_trace_enable(DEBUG_TRACE_I2S_RX); // 启用 I2S 接收标记然后用 Saleae Logic Pro 8 抓GPIO_PA2波形DEBUG_TRACE_ANC_START触发时PA2 会拉低 10us表示 ANC 算法开始执行DEBUG_TRACE_I2S_RX每次拉低 1us表示收到一帧 I2S 数据48kHz 采样率下应每 20.83us 一次。若 ANC 开启后 PA2 无脉冲说明算法未调度若 I2S RX 脉冲间隔忽长忽短说明 I2S 时钟不稳检查外部晶振或driver/src/i2s.c中I2S_CLK_DIV设置。4.3 烧录实战ac_flash 工具的三个致命参数杰理不提供 J-Link 脚本烧录全靠tools/ac_flashLinux/Mac或ac_flash.exeWindows。其核心参数极易出错参数作用错误示例后果-p /dev/ttyUSB0指定串口设备-p /dev/ttyACM0设备名错找不到设备报open port fail-b 2000000设置波特率-b 115200速率错烧录超时芯片反复复位-a 0x00000000指定起始地址-a 0x00001000偏移错程序跳转到非法地址死机正确命令# 烧录 bare_metal.bin 到芯片 Flash 起始地址 ./tools/ac_flash -p /dev/ttyUSB0 -b 2000000 -a 0x00000000 -f build/bare_metal.bin # 烧录后自动复位运行 ./tools/ac_flash -p /dev/ttyUSB0 -r注意ac_flash不校验 CRC若build/bare_metal.bin文件损坏它仍会“成功”烧录但芯片无法启动。务必用md5sum build/bare_metal.bin对比编译前后哈希值。5. 避坑指南AC69xx SDK 开发中 5 个高频翻车现场AC69xx SDK 的坑不是随机出现的而是集中在几个确定性极高的环节。以下是我在多个模拟项目 XTWS 耳机、模拟项目 Y便携音箱中踩过的、可复现的 5 个真实问题按发生频率排序。5.1 现象make报错undefined reference to memset原因SDK 的libc实现位于driver/src/libc.c但project/xxx/Makefile中未将其加入OBJS列表导致链接时找不到memset符号。解决在project/xxx/Makefile的OBJS变量末尾追加$(SDK_ROOT)/driver/src/libc.o并确保libc.c中#include ac6925.h路径正确。5.2 现象蓝牙配对成功但手机无法控制音量AVRCP 不响应原因middleware/btstack/src/avrcp_target.c中avrcp_target_init()函数调用了btstack_set_avrcp_role(AVRCP_ROLE_TARGET)但project/xxx/config.h中#define AVRCP_ENABLE 0被注释掉导致角色未注册。解决取消#define AVRCP_ENABLE 1的注释并确认project/xxx/src/app_bt.c中btstack_init()调用顺序在avrcp_target_init()之前。5.3 现象启用 USB Audio 后耳机插入电脑识别为“未知设备”设备管理器显示“驱动程序安装失败”原因project/xxx/src/usb_desc.c中usb_device_descriptor的bcdUSB字段值为0x0200USB 2.0但 Windows 10 对 AC69xx 的 USB Audio 类描述符要求bcdUSB 0x0110USB 1.1否则拒绝加载usbaudio.sys。解决将usb_device_descriptor.bcdUSB 0x0110并同步修改usb_interface_descriptor.bInterfaceClass 0x01AudiobInterfaceSubClass 0x01Audio Control。5.4 现象I2S 录音数据全为 0x0000但REG_I2S_STATUS显示RX_READY为 1原因driver/src/i2s.c中i2s_rx_init()函数设置了REG_I2S_CTRL BIT(0) | BIT(1)使能 RX 主机模式但 AC69xx 的 I2S 主机模式需外部提供 BCLK 和 LRCLK若你连接的是 PDM 麦克风输出 PDM 流必须改用I2S_MODE_SLAVE并设置REG_I2S_CTRL BIT(0) | BIT(2)。解决根据麦克风类型切换 I2S 模式并用示波器确认 BCLK/LRCLK 信号存在且频率正确PDM 麦克风通常需 1MHz BCLK。5.5 现象anc_enable()返回成功但耳机无降噪效果串口日志无 ANC 相关打印原因middleware/libanc.a依赖driver/src/anc_hw.c中的硬件使能函数而该文件被#ifdef USE_ANC_HW宏包裹但project/xxx/config.h中未定义USE_ANC_HW导致anc_hw.c未编译进工程。解决在project/xxx/config.h中添加#define USE_ANC_HW 1并确认Makefile中INC_DIRS包含$(SDK_ROOT)/driver/include使anc_hw.h可被包含。6. 进阶技巧用 SDK 的 trace 机制实现零侵入式性能分析AC69xx SDK 隐藏了一个强大但极少被文档提及的功能trace事件标记。它不依赖串口避免日志干扰实时性而是将时间戳和事件 ID 写入一块固定的 SRAM 区域0x20000000再由外部工具读取解析。这是分析 ANC 算法耗时、I2S 数据抖动、蓝牙协议栈延迟的后悔药。6.1 启用 trace buffer 并打点在project/xxx/src/app_main.c中#include trace.h // SDK 自带头文件 void app_main(void) { trace_init(); // 初始化 trace buffer1KB SRAM while(1) { trace_event(TRACE_ANC_START); // 打点ANC 开始处理 anc_process(); // 执行 ANC 算法 trace_event(TRACE_ANC_END); // 打点ANC 结束 trace_event(TRACE_I2S_RX_START); i2s_read_data(buffer, len); trace_event(TRACE_I2S_RX_END); os_time_dly(1); // 保持主循环 } }trace_event()是一个原子操作它读取 DWT_CYCCNT 寄存器ARM 内置周期计数器写入trace_buffer全程 5 个指令周期不影响实时性。6.2 用 Python 脚本实时解析 trace 数据SDK 自带tools/trace_parser.py但需适配你的芯片型号# 文件parse_trace.py import serial import struct import time def read_trace_from_uart(port/dev/ttyUSB0): ser serial.Serial(port, 2000000, timeout1) # 发送命令触发 trace dump ser.write(bTRACE_DUMP\n) data ser.read(1024) # 读取 trace buffer 内容 ser.close() return data def parse_trace(trace_bin): # trace buffer 格式每条记录 8 字节 4字节时间戳 4字节事件ID events [] for i in range(0, len(trace_bin), 8): if i 8 len(trace_bin): break ts, eid struct.unpack(II, trace_bin[i:i8]) # 小端 events.append({timestamp: ts, event_id: eid}) return events if __name__ __main__: trace_data read_trace_from_uart() events parse_trace(trace_data) # 计算 ANC 处理耗时单位CPU cycleAC6925 主频 48MHz for i in range(len(events)-1): if events[i][event_id] 0x1001 and events[i1][event_id] 0x1002: # TRACE_ANC_START/END cycles events[i1][timestamp] - events[i][timestamp] us cycles / 48 # 48MHz → 1 cycle 1/48 us print(fANC process time: {us:.2f} us)运行此脚本你将得到 ANC 算法每次执行的真实耗时非clock()估算。若发现某次耗时突增至 2000us远超标称的 800us说明发生了 cache miss 或内存冲突——这时再结合build/xxx.map查看anc_process()函数的 RAM 分布往往能定位到是feedforward_tap[]数组被分配到了慢速 RAM 区域。6.3 用 trace 数据反推系统瓶颈一张表看清真相事件对正常耗时范围us超时表现可能根因I2S_RX_START→I2S_RX_END15~2550I2S DMA 配置错误或外部晶振漂移ANC_START→ANC_END700~9001200feedforward_tap[]数组过大触发 flash wait stateBT_ACL_RX→APP_KEY_HANDLER300~500800btstack任务优先级过低被 ANC 任务抢占这张表不是凭空而来是我在模拟项目 X 中连续 72 小时采集 trace 数据后统计得出的阈值。它让我在客户投诉“降噪延迟高”时5 分钟内就判断出是btstack任务堆栈溢出os_task_create()的 stack_size 设为 512 字节太小而非怀疑算法本身。最后说一句AC69xx SDK 不是玩具它是工业级音频 SoC 的固件基石。它的“不友好”恰恰源于对实时性、功耗和成本的极致压榨。我坚持不用 IDE 图形界面是因为每一次手动敲make、查map、抓trace都在加固我对芯片底层的理解。这种理解是任何一键生成的工程都无法替代的底气。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑