ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

恒玄BES芯片嵌入式开发实战:从SDK架构到量产调试

恒玄BES芯片嵌入式开发实战:从SDK架构到量产调试 做过TWS耳机、智能音箱、降噪耳机这类产品的嵌入式工程师对“恒玄BES”这几个字应该不陌生。恒玄科技Bestechnic的BES系列蓝牙音频SoC过去几年在国内音频方案里出货量非常大从经典的BES2300系列到后面带主动降噪的BES2500、BES2700系列几乎在主流品牌和方案公司的产品里都有身影。这里要说明一下嵌入式圈子说“BES”通常就是指恒玄的这套蓝牙音频芯片方案和某些企业级软件中间件里的“BES”不是一回事别搞混。这篇笔记不是官方文档的搬运而是站在开发者视角把BES项目从接手到量产这一路的关键节点、容易踩的坑、以及很多SDK文档里不会写的细节梳理一遍。如果你正准备接手BES方案、或者第一次接触这套SDK这篇内容应该能帮你把起步阶段的时间压缩不少。我会尽量少讲虚的多讲实际能落地的操作和排查思路。1. 项目认知先别急着写代码把BES的开发框架弄清1.1 芯片架构决定开发思路BES芯片不管哪个系列整体思路都是一致的一颗SoC里同时集成音频处理、蓝牙通信、电源管理和各类外设控制。从开发角度看第一件事不是急着点开SDK而是先理解芯片内部的资源是怎么划分的。以我接触过的BES2300系列为例它内部有一颗主控核和一个低功耗的音频DSP核两者协作完成整个音频链路。主核跑系统调度和蓝牙协议栈DSP负责音频编解码、ANC降噪滤波这类实时性极强的任务。这个“双核分工”的理念贯穿了整个开发过程。你写的每一段代码都要清楚它跑在哪个核上跑在哪个RTOS任务里因为后续排查卡顿、电流、稳定性问题第一步就是要做任务级和核级的定位。BES的SDK默认跑的是RT-Thread之类的小型RTOS不同SDK版本也可能用FreeRTOS或自研调度器但思路差不多上层所有功能都被包装成一个个任务。比如音频播放是一个任务蓝牙协议栈事件处理是另一个任务按键检测又在别的任务里。这种设计的好处是模块独立某个模块崩溃不会立刻拖垮整个系统坏处是任务间通信、优先级设置一旦不对就会出现各种诡异的超时和抢占问题。所以前期花点时间看任务划分图比上来就改代码要高效得多。我见过不少新同事一头扎进代码里改了一个音量调节的接口结果音频任务被频繁阻塞声音卡成了拖拉机最后发现是另一个任务的优先级把音频任务压住了。1.2 SDK看起来很大但核心目录只有那几块第一次解压BES SDK的时候大多数人会被目录数量吓到src、platform、services、apps、utils、voice、tools一长串光看目录名没法下手。我自己的经验是先抓主线和次线。主线是apps和services这里放着产品级逻辑和业务服务次线是platform和driver这里是对芯片寄存器和外设的封装。不要一上来就去读driver很多驱动代码是芯片原厂写好的除非你确实要调一个没有现成驱动的新外设否则尽量别动。驱动层一旦改错往往不会马上暴露而是会埋下很深的隐患比如某个引脚的复用关系被破坏导致按键失灵。再往下是编译系统。BES的编译脚本基本是Python加Makefile的组合顶层会有一个build相关入口脚本里面维护了产品target列表。新增一个项目时通常不需要从零搭工程而是拿一个同平台、同Flash大小的现有target复制一份然后改产品名、改宏配置。这也是为什么老工程师拿到新项目会先问一句你们是基于哪个SDK、哪个target改的。不同基线差异很大有些看起来差不多的功能在不同SDK版本里的实现方式可能完全不同。版本管理也要特别注意SDK经常以压缩包形式发布原厂的patch可能是一堆diff文件。我的习惯是绝不直接改原厂文件而是把整个工程纳入git每个patch单独记录后续升级SDK时才能清楚地合并自己的改动。1.3 产品宏配置很多“新需求”只是配置组合在BES开发里有一个概念一定要理解透那就是产品宏配置。SDK为了适配不同型号的芯片、不同厂家、不同功能组合把一大半功能开关都做成了编译期宏。比如是否支持TWS、是否支持ANC、是否带充电盒通信、MIC通道走哪一路、代码c的主要看点是FLASH和RAM容量。通常同一代产品芯片型号越高Flash和RAM越大对应的功能也会更多比如带ANC的型号对音频处理能力要求更高。选型时不要只看最高规格还要看你的产品形态如果是入门级运动耳机可能根本不需要ANC选带ANC的型号反而是浪费成本。了解型号差异之后再看SDK里的顶层层目录思路就会清晰很多。你会发现SDK其实可以大致分成几个层次硬件抽象层屏蔽芯片寄存器、驱动层独立访问、协议栈层处理蓝牙HCI和AVDTP、中间层处理音频流和业务逻辑、应用层跑产品业务逻辑。开发的时候绝大多数工作还是在中间层和应用层。这有点像装修房子驱动相当于水电管线埋好了就别总去刨业务逻辑相当于软装这是产品差异化的重要阵地。2.2 编译产品target与配置宏进入SDK根目录后第一步是找到你想要编译的产品target。通常会在根目录执行一个类似于“python build.py”的编译指令脚本会列出所有可用的target列表此时选一个带“anc”或“tws”名称的target。第一次建议只编译原厂默认target不修改任何代码先确保整条编译链路是通的。这一步很关键可以在后续乱改代码时帮你确认问题到底出在环境还是出在代码。编译通过后你会看到生成目录里有一堆bin和patch文件这些就是要烧录的固件。BES固件通常分成主核固件和DSP固件分别以不同文件名出现量产时会把它们打包成烧录文件。这个过程看起来简单但有一个关键点bin版本必须和lib库版本匹配。SDK里一些模块是以.a库形式提供的二进制版本对不上编译能过但运行时就会出现各种奇怪问题。所以我每次拿到新SDK第一件事是核对lib库里记录的版本号和源码版本号是否一致。别小看这个动作我至少遇到过三次因为混用了不同版本的SDK补丁导致最终产品出现在某些手机上回连不上的问题。配置宏方面我建议阅读时先关注几个大类音频通路、蓝牙连接参数、系统任务配置、功能开关。音频通路的宏决定了音频数据从哪个MIC进入、从哪个DAC输出蓝牙连接参数比如广播间隔、连接间隔、超时时间会直接影响功耗和连接稳定性系统任务配置决定每个任务的栈空间和优先级这往往是稳定性的关键功能开关则决定这个固件是否包含ANC、是否支持TWS等。改宏的时候要特别小心很多宏之间存在依赖关系比如开启某个功能后会自动关闭另一个功能。我的做法是每改一个宏就全量编译一次同时用git记录改动避免“改了很多最后不知道是哪个引起的问题”。2.3 烧录、串口日志与第一眼确认固件编出来后烧录原厂一般提供J-Link方式或者量产烧录器方式。开发阶段用J-Link最方便直接连SWD接口。烧录时要注意芯片是否处于加密状态如果上一位同事开了加密你用J-Link读不到Flash所以开发板通常要接出复位和boot引脚必要时先擦除加密区域。有些厂家的加密策略还会导致烧录后无法用调试器回读这时候只能通过日志判断固件是否真正跑起来。烧录后最重要的就是日志。BES的日志输出默认走串口波特率通常比较高比如1.5M甚至更高你手里的USB转串口模块如果质量太差高速日志会乱码或者丢包。建议用带硬件缓存的USB转串口芯片线尽量短避免跨接太长。日志级别可以在配置宏里打开TRACE_LEVEL相关开关刚接手项目建议把日志调到最详细能帮助理解系统启动流程。除了串口还可以用厂商提供的调试工具配合分析工具可以实时查看蓝牙事件、定时器触发、任务调度情况定位问题更直接。我第一次用这个工具时惊讶地发现系统的很多问题通过日志曲线一眼就能看出来比如任务堆栈水位告警。2.4 工具链版本与硬件环境核对编译环境除了操作系统还有一个容易忽略的点工具链版本。我在实际项目中遇到过多次因为gcc版本过新导致编译告警被当成错误、或者链接结果异常的情况。我的建议是严格按照README里锁定的工具链版本来不要图省事直接apt安装最新版。用一个脚本统一管理工具链路径会让全组开发环境保持一致性。硬件环境也要核对。接J-Link时注意给开发板供电是否独立。BES芯片在烧录和调试时如果供电不稳定很容易出现擦除失败、调试器识别不到目标板的情况。手边备一个质量好一点的稳压电源对排查这类诡异问题很有帮助。我遇到过的情况是开发板通过USB供电插上调试器后板子居然不断复位最后查出来是电脑USB口供电能力太弱。换独立电源后一切正常。这类环境问题看似低级却很耽误时间提前排除能让后面的开发顺畅很多。3. 音频与蓝牙两条主线的实战拆解3.1 音频通路从ADC到扬声器的每一步都要有数音频是BES的核心。用户听到的声音从麦克风采集、模数转换、DSP处理降噪、EQ、增益、再到数模转换、功放输出中间任何一环配错都会表现为“声音不对”。做音频调试第一件事是拉出音频框图确定当前配置走的是哪条通路模拟输入还是数字MICI2S还是模拟输出采样率是16kHz还是48kHz。因为这些参数在SDK里通常由一组配置宏管理改错一个声音可能直接变成噪音。实际遇到最多的问题是DSP参数和硬件不匹配。ANC降噪效果不好很多时候不是算法的问题而是前馈麦克风的位置、密封性、喇叭频响与DSP参数不匹配造成的。这属于软硬联调范畴我的建议是先把硬件通路用扫频确认一遍再动软件参数不然会陷入“调一天参数问题依旧”的死循环。BES也提供调音工具可以实时调整EQ、压缩器参数这个工具在项目调试中几乎每天都用。调音工具连上设备后可以实时看到音频频谱还能在线修改滤波器参数调完直接写入设备非常高效。音频问题定位的速度往往取决于对“数据流方向”的敏感度。拿到一个声音异常的问题先判断是模拟端问题还是数字端问题。你可以播放一段扫频信号从DAC输出后面挂示波器看波形也可以从数字接口处抓取I2S数据看数据和期望是否一致。这样逐级定位比盲目调EQ科学得多。我自己的习惯是建一个音频测试脚本列表每次改完硬件或DSP参数后依次跑一遍覆盖频响、失真、左右声道隔离、信噪比等项目确保没有引入新的问题。3.2 蓝牙协议栈与TWS组队机制蓝牙这块BES用的是自研协议栈对外接口和标准BLE/GATT比较接近但内部实现封装得比较深。开发中最常碰到的三个任务是配对流程、回连策略和TWS主从切换。先说配对BES的配对流程一般由App通过蓝牙指令触发SDK里会有一个“配对模式”的状态机负责广播、可发现、可连接这些状态的切换。调试时最好能抓HCI日志看清底层在哪个环节失败不要只看App界面上报的“失败”二字。HCI日志会告诉你到底是连接超时、认证失败还是服务发现没走完定位问题的速度会有天壤之别。TWS组队是BES方案的重头戏。左右耳各有一颗芯片通过私有协议协商主从关系主耳负责和手机通信从耳通过TWS链路和主耳同步音频和状态。这个机制调试起来比较难很多问题发生在“主从切换”的瞬间比如连接不稳定、回连后左右耳角色乱掉。我的经验是先把TWS的组网时序图吃透明确每个状态由谁发起、超时多久、失败后如何回退再结合日志一步步排查。不要在没搞懂时序的情况下盲目加超时时间那样往往只会把问题往后拖延。TWS常见的问题还包括左右耳音量不平衡、某一侧断连、充电盒开关导致角色切换异常等。这类问题大多和状态机有关不是单点逻辑错误。我通常先用日志记录完整的角色切换过程再把事件时间戳对齐到毫秒级看哪一步耗时异常。比如从耳回连主耳时如果信道质量差重传次数多耗时会明显拉长导致用户体验上感觉“连不上”。这里需要调TWS链路的phy速率、重传策略和功率等级而不是简单改超时时间。3.3 功耗优化低功耗不是开个宏就行BES方案的卖点是低功耗但低功耗不是默认就有的。睡眠策略、外设电源门控、蓝牙广播间隔、音频播放时的DSP任务负载全都影响续航。开发阶段看功耗需要一个能测微安级电流的设备普通万用表很难看动态电流变化我常用的是低功耗分析仪或者可记录电流波形的设备。拿到电流波形后看系统在“待机-连接-播放-通话”各状态下是否按预期工作如果本应睡着的时刻突然出现电流尖峰基本就是有任务在周期性唤醒系统。排查这种问题的思路是“从外往里”先断开所有外设最小系统测底电流然后逐个打开外设看电流增量。如果打开某个外设后出现不规则电流多半是这个外设的中断或者供电没有正确配置。BES的GPIO配置和电源域管理在SDK里都有专门的模块调试时把每个外设对应的电源域开关代码看一遍会省很多时间。另外要注意某些调试工具打开时本身就会阻止系统进入睡眠测功耗前要把调试口相关配置关掉。不关调试功能测出来的功耗高了十倍都有可能。低功耗这块还有一个容易被忽略的参数是蓝牙广播间隔和连接间隔。待机时广播间隔设长一点功耗就能明显下降但连接建立速度会变慢需要根据产品需求平衡。播放音乐时音频蓝牙包的连接间隔和重传次数直接决定功耗如果蓝牙链路质量好可以适当调低重传次数来省电。我一直强调低功耗优化不能只看平均值还要看电流毛刺的次数和大小这类毛刺往往会影响整机续航和发热是产品体验的隐形杀手。3.4 连接稳定性射频、晶振和天线一个都不能少蓝牙音频产品的连接稳定性除了协议栈逻辑还和硬件设计强相关。BES这类SoC对外部晶振的精度和匹配电容有要求如果晶振频偏过大蓝牙信号会出现大量丢包表现为声音卡顿、连接距离短、回连失败率高。拿到一个新的硬件版本我会先用仪器或芯片自带的频率校准功能确认晶振频率是否在误差范围内。如果条件不具备也可以通过长时间连接测试来观察丢包率虽然不够精确但能发现明显问题。天线匹配是另一个大头。耳机体积小天线净空区有限如果天线匹配不好蓝牙灵敏度会差很多。我遇到过一款样品连接距离只有正常的一半后来硬件在π型匹配网络上调了一颗电容后距离恢复正常。软件侧能做的有限但可以在日志里观察RSSI和丢包率给硬件同事提供数据支撑。蓝牙射频问题的排查思路是判断问题是否集中在特定距离、特定方向、特定环境。如果在实验室近场测试很好但用户实际使用场景中卡顿还要考虑人体遮挡和设备摆放方向的影响。我自己的经验是连接稳定性和功耗往往需要一起调。连接间隔越小延迟越低但功耗越高广播间隔越大待机功耗越低但回连变慢。这些参数在BES的配置宏或config文件中都可以调整建议在开发早期就把目标值定下来不要到了量产前才开始调否则会非常被动。4. 从样机到量产我踩过的坑和排查速查手册4.1 编译、链接和启动崩溃的处理方法跑BES开发最常遇到的启动崩溃是hardfault也就是程序跑飞了。原因无非是空指针、数组越界、任务栈溢出、中断处理函数没正确注册这几类。遇到hardfault先别慌我的流程是第一步看PC指针掉在哪个地址第二步从这个地址对应的代码反汇编第三步看LR寄存器回溯调用栈。如果基础调试工具不好用就在怀疑点附近加打印日志通过二分法缩小范围。很多初学者一看到hardfault就麻了实际上只要定位到是哪个函数里触发异常大部分问题都能很快解决。还有一个经典问题是RAM占用超了。BES的SRAM资源有限代码里开一个过大的全局数组或者某个任务的栈设得太大链接阶段就会直接报错。这时候需要调整内存布局必要时把不用的功能宏关掉释放对应的RAM。这种问题在后期增加新功能时非常常见属于“加功能容易腾空间难”。所以开发前期给关键模块预估栈大小时一定别偷懒。你可以通过shell命令或者在调试器里查看每个任务的栈水位提前发现哪些任务栈分配过大哪些接近溢出从而在早期就把内存拉到合理水位。链接阶段还有一个容易被忽略的问题是符号冲突。SDK里不少模块以lib形式提供如果同时引入了两个不同版本或不同模块的lib符号重复定义非常难查。遇到这类报错我会用工具查看lib里的导出符号再用文本搜索定位源码里的定义确认是否真的有必要同时引用。总之编译链接报错大多不可怕最怕的是不加分析地盲目改内存配置那样只会把问题越盖越深。4.2 音频和蓝牙联调中的典型问题记录音频和蓝牙联调的问题很多出在数据流堵塞。耳机使用时出现断断续续的卡顿可能的点非常多蓝牙射频干扰、音频缓冲不足、调度优先级设置、DSP任务超时、甚至手机端的编码格式切换。我的排查习惯是先区分是射频问题还是数据流问题。如果卡顿只在特定场景出现比如手机放口袋且日志里能看到蓝牙层丢包优先怀疑射频链路如果卡顿是周期性的且伴随DSP任务超时日志优先怀疑音频数据流。这两条路径的排查思路完全不同选错方向会白费很多时间。下面这张速查表是我实际工作中沉淀下来的覆盖了大部分日常碰到的典型问题希望对你排查有帮助问题类型可能原因第一步排查建议卡顿/断连蓝牙射频干扰更换信道、检查天线匹配卡顿且日志显示buffer overrun音频缓冲配置过小加大环形缓冲区、检查中断优先级左右耳不同步TWS主从切换异常检查从耳回连日志确认状态机声音断续且有DSP警告DSP任务被更高优先级抢占调高DSP任务优先级或降低负载休眠时电流偏高外设未完全下电逐一开启外设测电流增量回连不上手机蓝牙缓存中保存的设备信息异常清除配对信息重新配对测试这个表不算全但覆盖了大部分日常问题。另外改任何配置后记得备份原始配置。有几次我改完一个宏发现系统不启动了最后靠git diff才发现被改的参数不止一个浪费了不少时间。这里也想提醒一句当问题现象非常诡异、完全不符合常理时先怀疑自己的改动引入了隐藏问题再怀疑芯片本身的bug。原厂芯片在量产阶段通常已经相当稳定真正不稳定的往往是工程里的某些误配置。4.3 调试工具推荐与分段排查法BES相关的调试工具官方有音频调音工具、HCI日志抓取工具、量产烧录工具等。第三方工具里J-Link配合任意支持ARM Cortex-M的IDE都能做在线调试因为BES主核是ARM核。不过BES还有DSP核DSP核的调试只能在编译时打开对应宏通过专门的接口输出调试信息这点和纯ARM项目不太一样。我第一次调音频算法时试图在DSP核的代码里断点单步结果发现调试器根本挂不上DSP核耽误了半天时间。后来才知道DSP核有自己的调试方式不能直接用ARM那套。我自己很依赖的一个方法是“分段排查法”遇到复杂问题先确定是在哪一段出的问题。比如“连接后听不到声音”可以拆成四个段链路连接、音频通路建立、从耳同步、DSP播放。每段都有对应的日志标志看日志里走到哪个段就断了问题就锁定在哪一段。比拿着代码从头翻到尾高效太多。这也是我想强调的BES这种量级的开发真正决定效率的不是代码写得有多快而是定位问题有多快。除了官方工具我也推荐建立自己的日志解析脚本。比如把串口日志的特定关键字段提取出来按时间排序再格式化到Excel或数据库里排查长时间运行类问题会非常方便。我记得排查过一个偶发掉线的bug人工翻日志翻了三天没有头绪后来写了脚本把所有掉线时间点前后几秒的日志自动汇总才发现掉线前都有一个共同特征某个外设中断频繁触发。顺藤摸瓜最终定位到中断配置漏了一个标志位。这类问题如果没有日志脚本辅助几乎不可能在可接受的时间内找到原因。4.4 量产前的最后检查清单项目量产前有些事项必须逐一确认否则等产品铺到渠道后再发现问题代价会非常大。第一项是固件版本和硬件版本的对应关系。很多产品在生产过程中会有硬件小改版固件如果不跟着更新轻则功能异常重则无法开机。建议在量产版本上明确记录SDK版本、lib版本、编译日期、功能宏列表这些信息建议固化到固件里可以通过特定指令读取。这个习惯能在售后和客诉中节省大量沟通成本。第二项是烧录流程的验证。量产烧录和开发烧录不一样量产时大多用脱机烧录器或自动化测试设备要求固件能稳定、快速地批量烧录。要提前验证加密方案、烧录成功率、烧录后首次开机时间。如果烧录良率达不到要求问题大概率出在烧录器固件版本、电平匹配或者目标板电源设计上需要尽快反馈给工具或硬件团队。第三项是射频和音频的产测覆盖。量产产品通常会经过RF校准和音频测试BES芯片一般支持产测模式可以在产线通过命令进入测试状态测试蓝牙发射功率、接收灵敏度、喇叭和麦克风是否正常。不要以为开发板功能正常量产就必然正常。同一套固件放到不同批次硬件上可能因为贴片差异导致音频灵敏度不同产测是最后一道关卡务必设计完整。我在实际项目中还有一个小习惯量产前至少做一轮100台以上的长时间压力测试覆盖播放音乐、通话、开关机、充电、TWS主从切换等场景。很多偶发问题在单台样机上根本看不出规律只有数量上来、时间拉长才能暴露出来。别嫌麻烦这一轮测试花的时间往往能避免你之后花十倍时间处理客诉。结尾最后再分享一个我自己一直在用的习惯每次拿到一个新项目我会先花半天时间把SDK里和target相关的配置文件从头读一遍把每个宏的含义记到笔记里而不是急着编译改代码。这样做看起来慢但后续排查问题时会快很多。很多新同事一上来就是“先跑起来”跑起来之后发现哪儿都不对又回头一点点啃配置反而更慢。这套“先读配置、再改代码、最后看日志”的顺序帮我避开过大量低级错误。BES开发说难也难因为涉及音频、蓝牙、功耗、RTOS多个知识面说容易也容易因为原厂SDK已经把大部分底层细节封装好了关键是你得知道每个旋钮在哪、每个参数改了会有什么影响。希望这篇笔记能让你在BES开发路上少走几步弯路。
返回列表