最近圈子里聊得比较多的 Auracast 广播音频,泰凌这次给了一套能直接落地的方案,亮点是把 Auracast 广播接收/发射和高音质低延时的全双工对讲打包在一起。我拿到样品之后做了好几轮测试,今天把这套方案背后的技术思路、SDK 里的处理细节,以及实际调试中踩过的坑整理出来,供做 LE Audio 产品、无线对讲或者场馆导览设备的朋友参考。
这个方案适合谁?如果你是做无线对讲、助听辅听、会议系统、场馆导览或者公共广播接收设备的,这篇应该能帮你少走不少弯路。哪怕你只是刚接触蓝牙音频开发,也可以从第1章开始看,把 Auracast 和全双工的概念先捋清楚。
1. Auracast广播音频、全双工对讲,这是一套什么样的方案
1.1 先理解Auracast:从私享到广播的蓝牙音频范式变化
传统蓝牙音频是典型的“一对一”连接,手机连耳机、手机连音箱,配好对才能听。Auracast 是蓝牙技术联盟在 LE Audio 规范里定义的广播音频能力,简单说就是让发射端像电台一样持续播送音频,周围任意支持 Auracast 的接收设备都能直接收听,不需要配对、不需要连接,广播源可以服务无限多个接收端。
第一次听到这个逻辑的时候,我脑子里冒出来的场景是机场航班信息播报和博物馆导览。以前做助听器或者场馆广播接收设备,要么用调频广播,要么用私有 2.4G 协议,各有各的麻烦。调频广播音质一般、抗干扰差,私有协议又不兼容主流手机和耳机。Auracast 的好处是把这套能力收到标准蓝牙规范里,手机、耳机、助听器、专用接收器都能互通,产品做出来不用自己闭门造车。
泰凌这套方案在这条链路里的位置比较有意思,它把广播音频的发射和接收都支持了,不是说只能做一个Auracast只能听,而是发射、接收、转发,甚至跟设备上的其他功能组合起来都可以做。再加上全双工对讲功能,实际上是在同一个SoC平台上同时覆盖了“一对多播放”和“点对点实时通话”两条音频产品线。
1.2 全双工对讲跟传统半双工对讲的本质差异
对讲机大家都不陌生,传统对讲是半双工,按下PTT才能说话,松开就听对方。这种模式的好处是简单,坏处是交流不自然,两边同时开口就互相打断。全双工对讲则是双方可以同时说、同时听,体验上更像打电话,而不是拿对讲机喊话。
说到全双工和半双工的区别,做过有线通信的朋友应该很熟悉RS485。经典RS485就是一对差分线,同一时刻只能一个方向发送数据,本质是半双工;要真正做全双工,一般得上 RS422 或者四线制的 RS485 扩展,靠独立收发通道来同时双向传输。无线通信里的全双工要更麻烦,尤其是同频段同时收发,天线端收自己发的信号会造成严重的自干扰,蓝牙这类系统通常不会在物理层做同频同时全双工,而是通过不同链路、不同时隙、不同通道的配合,在用户使用体验层面实现“同时说话同时听到”。
泰凌这套方案里涉及的全双工对讲,我理解也是逻辑链路层面做双工协同,而不是射频物理层做单天线同时收发。这一点对产品定义很重要,因为实际测试时你验证的是“双方能不能顺畅地同时通话”,而不是去测射频自干扰指标。做产品的人要把这个概念跟客户讲清楚,客户经常把无线全双工等同于有线电话的全双工,实际上无线方案的频谱资源、天线隔离、回音消除都有额外约束。
1.3 为什么泰凌把广播音频和全双工对讲放在一起
这两个功能乍一看不是一个赛道,一个面向公共广播、一个面向双向通信,但仔细看设备形态就会发现,做导览机的厂商通常也要做团队对讲,做助听器的厂商往往也在考虑好友之间互相通话。同一个硬件平台如果既能收广播、又能做全双工对讲,产品覆盖的场景会宽很多。
另外从芯片资源角度看,TLSR9系列本身定位就是低功耗蓝牙SoC,CPU、射频和协议栈资源足够同时跑音频编解码、广播同步和双向链路调度。对做模组的厂商来说,一个方案覆盖两种产品形态,备货和BOM成本都能省不少。我看到这套方案的时候第一反应是,这正好解决了很多中小厂商“想做多种音频产品但没有精力维护多套方案”的痛点。
2. 高音质和低延时是怎么做到的
2.1 LC3编码是音质的底座
LE Audio 相比经典蓝牙音频,最核心的变化就是用 LC3 编码替代了 SBC。SBC 是早期蓝牙音频为了统一格式定的编码,兼容性没问题,但码率利用率一般,音质上限不高。LC3 在同样码率下主观听感和语音可懂度明显更好,特别是语音场景,齿音和底噪处理都比 SBC 干净很多。
LC3 支持多种采样率,常见的有 16kHz、24kHz、32kHz、44.1kHz、48kHz,帧长可以在 7.5ms 和 10ms 之间选择。采样率和帧长的组合直接决定编码延迟和码率。做广播音乐类产品,可以用 48kHz 采样、高码率档位,让听感尽量饱满;做对讲类产品,语音带宽不需要太高,用 16kHz 或 32kHz 采样、低码率档位就能保证清晰度,同时把空口占用压下来,为双工通信腾出射频资源。
泰凌方案里,LC3 编解码是直接集成在软件链路里的,开发者需要关心的不是算法本身,而是采样率、码率、帧长这几个参数怎么跟产品场景匹配。我习惯先把这几个参数在工程里调成自己目标场景的组合,再去测延迟和音质,而不是拿着默认参数一直往下做。
2.2 低延时的关键在传输调度与缓冲策略
蓝牙音频的端到端延迟可以从几个环节拆:麦克风采集、编码、射频发送、接收端解包缓冲、解码、DAC 播放。早期 SBC 方案里系统延迟普遍在 100ms 以上,用户看视频唇形对不上就是这个原因。LC3 的设计目标就是降低帧长和调度周期,理论上可以把编码和传输环节的延迟压到几十毫秒级别,但实际能做到多少,很大程度上取决于接收端的缓冲策略。
缓冲是把双刃剑。缓冲调大,抗干扰能力更强,声音不容易断,但延迟会变高;缓冲调小,延迟好看,但射频抖动或者时钟漂移一上来,声音就开始卡顿。我在实际测试里发现,低延时方案不能只盯着平均延迟,还要看最坏情况下的延迟波动,否则产品交付到客户手里,稍差一点的射频环境下体验就很拧巴。
广播音频场景还有一个额外要求叫同步一致性。同一个广播源,周围 10 台接收设备如果延迟不一致,声音会有先后,这在多语言同传或者多人同时听一个导览的场景里非常明显。泰凌这套方案要做低延迟广播,必然在同步基准和时钟漂移补偿上有专门处理,我们做应用层时也要配合,不要在音频链路里随意增加额外缓冲。
2.3 全双工对讲的高音质难点:回声与底噪
全双工对讲和广播播放的高音质难点不完全一样。广播场景只要保证播放端音质好就行,对讲场景最麻烦的是自己扬声器里正在播放对方的声音,这个声音会被本地麦克风重新拾进去,再传回对方那里,形成回声。回声不消掉,全双工就没法用,哪怕编码再好也白搭。
所以方案里必须要有回声消除 AEC 模块,而且这个模块接到的参考信号必须来源于真实的播放链路,否则参考对不上,回声消除算法分不清是远端语音、近端语音还是扬声器回声。此外还有背景噪声抑制,比如户外对讲场景风声很大,NS 降噪必须跟上,但要避免降噪算法把语音尾部切掉,不然听感就是一顿一顿的。
另外一个容易被忽略的点是,全双工模式下收发同时进行,音频处理算法的计算量会叠加,低功耗芯片上如果算法开得太重,CPU 负载和内存占用会明显上涨。泰凌这套方案既然敢把全双工对讲和 LC3 编解码同时跑在单芯片上,说明资源调配上是有余量的,但开发者还是要根据实际产品形态取舍,比如语音对讲不需要音乐级频响,可以把带宽限制在 3.5kHz 左右,这样降噪和回声消除的压力都会小很多。
3. 实际工程落地:SDK开发、Flash操作与音频链路
3.1 开发环境与工程结构
泰凌的开发工具链主要有 Telink IoT Studio 和 Zephyr 两条路线。我自己的习惯是用 Telink IoT Studio 做快速验证,它基于 Eclipse 封装,集成了编译、下载和调试功能,对刚接触泰凌芯片的朋友更友好。Zephyr 适合那些对抽象层和可移植性有更高要求的团队,但初次上手的学习曲线会更陡。
拿到开发板之后,先烧一个官方 demo 工程确认工具链没问题,再开始改代码。泰凌 SDK 的典型工程结构大致分为几个区域:驱动层提供外设驱动,比如 GPIO、UART、SPI、Flash、音频接口;协议栈层封装蓝牙 controller 和 host;应用层放你自己的业务逻辑。还有一个公共库区域,存放 log、timer、功耗管理等通用模块。
我需要提醒一下,不同版本的 SDK 在文件组织和 API 命名上会有差异,网上搜到的老代码不一定能直接编译过。最稳妥的做法是拿你当前这份 SDK 里的 demo 工程作为起点,需要什么功能就到对应模块里去改,而不是网上找一个工程硬套。
3.2 泰凌SDK里的Flash操作与分区规划
泰凌微蓝牙 SDK 操作 Flash 是很多刚上手的人容易踩坑的地方。Flash 在蓝牙产品里承担两个核心任务,一个是存固件,一个是存用户配置和 MAC 地址之类的重要数据。Flash 本身有擦写寿命和按扇区擦除的特性,不能像内存一样随便改写,所以规划分区比写读写代码更重要。
我一般会把 Flash 划分成几个区域:Bootloader 区、App 固件区、OTA 临时区、参数配置区、MAC/校准信息区。参数配置区和 MAC 区要避开固件区,不然每次 OTA 升级都可能把关键配置覆盖掉。用户自有数据尽量放在 Flash 末尾的高地址扇区,这类区域被固件升级影响的风险最小。
当你想存一组配置参数时,典型的操作流程是定义一个结构体,初始化时从指定 Flash 地址读出来,修改后再擦除对应扇区、写入新数据、回读校验。这里要注意 Flash 擦除粒度一般是一个扇区,常见的是 4KB,你哪怕只改一个字节,也要先把整个扇区擦掉再完整写回去。给一段比较通用的示例流程:
// 伪代码,具体API以你拿到的SDK版本为准 typedef struct { uint8_t version; uint8_t channel; uint16_t mic_gain; uint32_t flags; } user_cfg_t; user_cfg_t cfg; void user_cfg_save(void) { // 暂停可能和Flash操作冲突的任务 // 擦除用户配置扇区 flash_sector_erase(USER_CFG_ADDR); // 写入新配置 flash_program(USER_CFG_ADDR, (uint8_t *)&cfg, sizeof(cfg)); // 回读校验,防止写入异常 }这套流程看起来简单,实际有几个坑。第一个坑是在蓝牙回调函数或者音频中断里直接做 Flash 擦写。Flash 擦写时间可以达到毫秒甚至更高的级别,期间会把 CPU 或者总线占住,影响蓝牙协议栈调度,严重的直接导致断连或者音频卡顿。正确做法是先在内存里准备好数据,等系统闲下来再写入。
第二个坑是掉电导致写入半截。产品突然断电时,Flash 写入可能没完成,再次上电读到的是损坏数据。比较好的做法是保留双份配置区,写入时先写副本,校验成功再切标志位,启动时优先读有效标志位的配置。对追求低成本的产品来说,至少要在读配置时做 CRC 或和校验,读到异常就回退默认参数。
3.3 音频接口、参数与双工链路配置要点
音频链路是这套方案落地时的重中之重。泰凌芯片上通常要接麦克风输入和喇叭输出,输入侧可以用模拟麦克风也可以接 PDM 数字麦克风,输出侧比较常见的是 I2S 到外置 Codec,或者接 D 类功放直接推喇叭。工程里需要把对应的引脚和音频设备类型配置正确,尤其是麦克风偏置电压和增益,调不好底噪会很大。
在配置双工对讲链路时,采样率、位深、帧长要保持收发一致,否则两头的声音会出现音调异常。音频参数一旦确定,后续改起来影响面很大,建议在项目早期就把产品需要的音频参数定下来,比如语音对讲用 16kHz 采样、LC3 10ms 帧长,音乐广播用 48kHz 采样、LC3 高码率,然后再去做对应的射频参数和缓冲参数调整。
全双工对讲和 Auracast 广播如果要同时跑,音频链路之间还要做策略切换。比如设备处于广播接收状态时,可以不启用麦克风;一旦进入通话模式,再切到双工链路。切换过程要平滑,不能有爆音或卡顿,这对软件状态机设计是有要求的。我这里是按常见实践补充一下,具体切换接口以泰凌方案提供的 API 为准。好的方案是它把链路管理器已经封装了一部分,应用层做场景决策就行。
4. 典型应用场景实战参考
4.1 场馆广播、助听与导览场景
Auracast 广播音频最典型的落地场景就是公共场馆。机场、博物馆、教堂、大礼堂,用户可以拿着自己的 TWS 耳机直接收听指定广播源的内容。对于场馆方来说,不需要发接收设备,只需要部署 Auracast 发射器,成本非常低;对于用户来说,不用安装 App,不用配对,靠近就能听,这种体验是传统蓝牙连接给不了的。
助听器场景对 Auracast 的依赖度更高。听力障碍人士在嘈杂场所很难听清广播信息,如果助听器支持 Auracast,就可以直接接收场馆里的清晰语音流,不需要额外适配。泰凌这套方案低功耗的特点在这个场景特别重要,助听器电池容量小、续航要求高,如果接收 Auracast 音频的功耗压不下来,整个产品就没法用。
导览场景里我特别想提醒延迟一致性的问题。同一个展厅多个接收设备,如果 A 设备听到导游声音比 B 设备晚了半拍,游客集中起来讲解时就会感觉很怪。做这种产品时,建议把接收端缓冲调到一个合理的值,既保证抗干扰,又不至于引入过大差异。同时要做频道切换功能,游客走到不同展厅时能快速切到对应频道,切换过程要短要顺,不能让用户等太久。
4.2 无线对讲、会议与团队通信场景
全双工对讲最直接的产品就是无线对讲机和团队通信设备。传统对讲机使用半双工 PTT 模式,在多人协作场景里效率不高。煤矿、工地、酒店、安保、户外领队,这类用户需要的是随时能说话、随时能听到,不用按键的那种体验。泰凌这套方案把全双工对讲做成标准参考设计,对做对讲机的厂商来说是个不错的切入点。
会议室场景也比较适合全双工。现在的无线会议麦克风系统经常面临多设备互联的问题,如果用传统蓝牙,一台主机只能连一个麦克风。换成 LE Audio 和私有双工链路配合,可以做小组讨论、多方拾音、个人音箱同时聆听。不过会议场景对回声消除的要求比普通对讲更高,因为会议室里发言人离麦克风距离远,声音反射复杂,AEC 参数需要专门调。
做这类产品时,不可忽略的一个指标是距离和穿墙能力。全双工模式下的 RF 调度比单向接收更密集,如果天线设计不好或者发射功率被压低,通话距离会比预期差不少。我建议在硬件设计阶段就留好天线匹配的调试时间,软件上也要根据距离变化适当调整重传策略,否则单纯靠协议栈默认参数,很难保证复杂的现场环境。
5. 常见问题与排查技巧实录
5.1 收不到广播音频,或声音断断续续
Auracast 广播音频收不到,先不要怀疑芯片是不是有问题。我遇到的第一类原因是发射端根本没有启动周期广播,或者广播参数和接收端期望的不一致,需要在 log 里确认广播事件有没有正常发出。如果发射端本身就有问题,接收端做再多都是白搭。
第二类原因是接收端的同步逻辑没有跟到广播包。Auracast 工作依赖周期广播同步,接收端需要在信道、跳频序列、广播间隔上和发射端保持一致。可以用支持 LE Audio 抓包分析的工具看射频空口有没有周期广播同步过程,如果有同步但声音还是断,大概率是时钟漂移或者缓冲设置不合适。
第三类原因是两只设备距离拉远或者中间有遮挡,导致广播信号信噪比下降。接收端在信号弱的时候会靠重传或者纠错来恢复,代价就是延迟波动。遇到这种情况,优先检查天线匹配和射频灵敏度,而不是盲目加大接收缓冲。我以前犯过这个错误,把缓冲调大后声音确实稳定了一些,但视频画面和声音对不上,产品评审直接被否。后来还是回头优化天线,问题才真正解决。
5.2 对讲有回声、断句和延迟超标
回声是全双工对讲里排第一的问题。如果通话时能听到自己的回声,先确认 AEC 有没有真正启动,再确认参考信号是不是从正确的播放节点取的。有些时候麦克风串进来的不只是喇叭播放的声音,还有外壳振动传导的固体声,单靠 AEC 很难处理干净,需要在结构设计上做减振处理。
断句比回声更隐蔽。断句表现为对方说话时中间缺字,像被切掉了一样。这种情况很可能是 VAD 语音活动检测阈值设得太高,把正常语音前半段当成静音丢掉了。排查时先把 VAD 关掉或者阈值调到最低,如果断句消失,问题就确认了,再回头微调阈值。
延迟超标主要原因还是缓冲和帧长。把 LC3 帧长从 10ms 改成 7.5ms,把接收端缓冲往下压,一般会有明显改善。但要注意,帧长改短意味着单位时间内要处理更多帧,CPU 占用和功耗会上升,低功耗产品要算好这笔账。
在方案调试过程中,我把一些常见现象、可能原因和处理手段整理成了速查表,方便开发阶段对照排查:
| 现象 | 可能原因 | 排查与解决建议 |
|---|---|---|
| 广播音频完全收不到 | 发射端未启动周期广播/接收端同步参数不匹配 | 检查发射端日志,用抓包工具确认广播事件 |
| 广播声音断断续续 | 时钟漂移、接收缓冲过小、信号弱 | 调整缓冲,检查天线匹配,必要时加时钟补偿 |
| 通话有明显回声 | AEC 未启用或参考信号接错 | 确认 AEC 开关,核对参考信号来源 |
| 说话断字、缺字 | VAD 阈值过高、射频丢包 | 先关闭 VAD 验证,再调整阈值 |
| 全双工延迟超标 | 缓冲过大、LC3 帧长偏大 | 缩短缓冲,改用 7.5ms 帧长 |
| Flash 写入后数据丢失 | 掉电写入中断、地址冲突 | 做双区备份,加 CRC 校验,避开固件区 |
| OTA 后启动失败 | App 与 Bootloader 版本不匹配 | 检查版本匹配,保留恢复出厂入口 |
5.3 Flash读写、OTA与启动异常问题
我做产品时遇到过一次很隐蔽的 Flash 问题:配置参数写入时正常,但设备重启后读出来偶尔是错的。排查下来发现是自动生成的工程把某个 RAM 地址映射到了 Flash 寄存器区域,代码里访问这个地址时没走预期流程,等于写了个寂寞。后来对照数据手册和 SDK 驱动重新映射了地址,问题彻底消失。这里建议大家别自己直接操作底层 Flash 寄存器,老老实实走 SDK 的 flash 驱动接口。
OTA 后启动失败也遇到过。原因是 App 固件区和 Bootloader 的版本匹配逻辑没处理好,新固件升级完成后校验失败,设备停留在启动失败状态。后来我在工程里加了一个启动标志检测机制:如果连续多次启动失败,就强制回退到上一个可用固件版本,同时在产品上留一个长按按键进入恢复模式,这样设备在客户手里不至于变成砖。
Flash 擦写寿命问题上,我做了一个很简单的优化:配置参数不频繁擦写。比如增益调节、音量设置这类参数,先把最新值缓存在 RAM 里,延迟几秒再写入 Flash,避免用户连续调音量时反复擦写扇区。别小看这个改动,对延长 Flash 寿命和减少掉电风险都有好处。
最后说点个人体会。Auracast 和全双工对讲放在同一颗芯片上,方向是对的,但不是所有产品都适合同时启用两个功能。我做的项目里,真正能跑顺的关键是先定清楚产品形态,再取舍协议栈参数,千万不要一上来就追求“所有功能全开”。后续如果时间允许,我会再单独把 AEC 参数和 LC3 码率选择这块展开讲讲。