3、电话音频通路设计:SCO/eSCO链路建立、音频编解码器配置与PCM接口调试

电话音频通路,说白了就是蓝牙耳机里那个「喂喂喂」的声音怎么传过去的。很多刚入行的朋友觉得这步简单,不就是配个PCM时钟嘛。但我在项目里吃过不少亏,有一次样机通话时声音断断续续,查了三天才发现是SCO链路的发包间隔没对齐。

嗯,今天咱们就把这块彻底讲透。

3.1 SCO与eSCO链路:选哪个?

SCO(Synchronous Connection-Oriented)是蓝牙里最早的电话音频传输方式。它固定每两个蓝牙时隙发一个包,说白了就是「定时定点」地传数据。好处是延迟低,坏处是抗干扰差。

eSCO(Enhanced SCO)是后来改进的版本。它允许重传,还能调整发包间隔。我个人习惯,只要芯片支持,优先用eSCO。为什么?

  • 重传机制:丢包了可以补发,通话不容易断
  • 灵活间隔:可以选6、8、10个时隙发一次,省电
  • 兼容性好:大部分现代蓝牙芯片都支持

关键参数对比

参数SCOeSCO
发包间隔2个时隙(1.25ms)6/8/10个时隙
重传不支持支持(最多4次)
典型延迟~3ms~5-10ms
抗干扰

你想想看,在车里打电话,周围Wi-Fi、其他蓝牙设备都在抢2.4G频段。用SCO的话,一个干扰包过来,声音就「咔」一下断了。eSCO能重传,体验好很多。

3.2 链路建立流程:从连接到通话

建立一条电话音频链路,其实就三步。我当年刚做车载项目时,以为配好PCM就完事了,结果发现链路根本没连上——嗯,顺序搞反了。

  1. ACL连接建立:先让手机和车机连上基础链路
  2. SCO/eSCO连接建立:在ACL基础上,发起音频链路
  3. 音频流启动:开始传PCM数据

代码里怎么配?以CSR8670为例,关键就这几行:

/* 配置eSCO参数 */
#define ESCO_PACKET_TYPE   EDR_ESCO_EV3
#define ESCO_TX_INTERVAL   6   /* 6个时隙发一次 */
#define ESCO_RETRANSMIT    2   /* 最多重传2次 */

/* 发起eSCO连接 */
sink_esco_connect(acl_handle, esco_params);

我的经验:EV3包类型在大多数场景下够用。如果信号差,可以换成2-EV3,数据量翻倍但抗干扰更好。

3.3 音频编解码器配置:CVSD还是mSBC?

蓝牙电话音频支持两种编解码器:

  • CVSD(Continuous Variable Slope Delta Modulation):老牌编码,64kbps,音质一般
  • mSBC(modified Sub-Band Coding):宽带语音,16kHz采样,音质好很多

我曾经在一个项目里,客户投诉通话声音「闷」。查了半天,发现默认用的是CVSD。换成mSBC后,客户说「哎,清楚多了」。

配置mSBC时要注意:

/* mSBC编解码器初始化 */
codec_config_t cfg;
cfg.sample_rate = 16000;    /* 16kHz */
cfg.bit_depth   = 16;       /* 16位 */
cfg.channels    = 1;        /* 单声道 */
cfg.codec_type  = CODEC_MSBC;

codec_init(&cfg);

注意:mSBC需要蓝牙双方都支持。如果手机不支持,会自动降级到CVSD。所以代码里要做好回退处理。

3.4 PCM接口调试:时钟、帧同步和数据

PCM接口是音频编解码器和蓝牙芯片之间的「高速公路」。三条线:

  • PCM_CLK:位时钟,决定每个bit的传输速度
  • PCM_SYNC:帧同步,标记一帧数据的开始
  • PCM_DIN/PCM_DOUT:数据线

调试时最容易出问题的是时钟极性。我遇到过好几次,示波器一看,CLK和SYNC的相位反了,声音全是噪声。

典型配置参数:

参数典型值说明
采样率8kHz / 16kHzCVSD用8k,mSBC用16k
位宽16bit标准配置
帧长16位 × 2通道 = 32位左声道右声道
时钟极性上升沿采样看芯片手册

调试步骤我一般这么走:

  1. 先看时钟:示波器量PCM_CLK,频率对不对
  2. 再看同步:PCM_SYNC的周期是不是1/采样率
  3. 最后看数据:发个正弦波,看DIN/DOUT波形对不对

避坑指南:我曾经在调试时发现PCM_CLK频率是对的,但SYNC信号一直没拉高。查了半天,原来是蓝牙芯片的PCM主从模式没配对。主设备输出时钟,从设备接收时钟,搞反了就没同步信号。

3.5 实战:一个完整的音频通路调试案例

去年做的一个车载项目,问题现象是:打电话时,对方能听到我说话,但我听不到对方。

排查过程:

  1. 先看链路:eSCO连接建立成功,没问题
  2. 再看编解码:mSBC配置正确
  3. 最后看PCM:示波器一量,PCM_DOUT有数据,但PCM_DIN一直是低电平

原因找到了——蓝牙芯片的PCM_DIN引脚虚焊。补焊后,声音正常了。

嗯,有时候问题就这么简单。但你不去量波形,光看代码是看不出来的。

我的习惯:每次新板子回来,先写个PCM回环测试程序。把DIN和DOUT短接,发一段数据看能不能收回来。能回环通过,再跑上层应用。

3.6 小结

电话音频通路设计,说白了就是三件事:

  • 选对链路:eSCO优先,抗干扰好
  • 配对编解码:mSCD音质好,但要考虑兼容性
  • 调对PCM:时钟、同步、极性,一个都不能错

你想想看,车里打电话,信号本来就复杂。要是音频通路再出问题,那体验就太差了。希望今天的内容能帮你少走些弯路。

下一章咱们聊聊蓝牙电话的HFP协议栈实现,那个更绕,但更有意思。