3. TWS连接流程:经典TWS连接建立、LE Audio下的TWS连接、Snoop与Copy模式详解

好,咱们今天聊点硬核的。TWS耳机的连接流程,说白了就是左右耳怎么“勾搭”上,再一起跟手机“握手”。我做了这么多年,发现很多新手工程师在这块栽跟头。今天我把经典蓝牙和LE Audio下的连接流程掰开揉碎了讲,顺便聊聊Snoop和Copy这两种模式——嗯,这可是区分菜鸟和老鸟的分水岭。

3.1 经典TWS连接建立

先说说最传统的方案。经典TWS,左右耳之间用的是蓝牙经典链路(BR/EDR)。流程大概是这样的:

  1. 配对阶段:耳机从充电仓取出,左右耳自动进入配对模式。左耳(通常作为主耳)会先广播,右耳扫描并连接。
  2. 角色协商:左右耳通过HCI命令协商谁是主耳(Primary),谁是副耳(Secondary)。主耳负责跟手机通信,副耳听主耳的。
  3. 链路建立:主耳跟手机建立ACL链路,同时跟副耳也建立一条ACL链路。这时候,音频数据从手机到主耳,主耳再转发给副耳。

我个人习惯把主耳比作“翻译官”——手机说啥,它先听,再转述给副耳。但这里有个坑:转发延迟。我在项目中遇到过,如果主耳转发不及时,左右耳声音就会“一前一后”,用户听着像在听山谷回音。

避坑指南:我曾经调试一个项目,左右耳延迟差了40ms。查了半天,发现是主耳转发时用了高优先级队列,但副耳处理中断时被其他任务抢占了。解决办法是把转发线程的优先级提到最高,并且关掉不必要的蓝牙扫描。

3.2 LE Audio下的TWS连接

到了LE Audio时代,事情变得有意思了。LE Audio用的是LC3编解码,而且引入了新的连接模型。你想想看,经典蓝牙那种“主耳转发”的方式,功耗高、延迟大,LE Audio直接把它颠覆了。

LE Audio下,TWS连接有两种主流方式:

  • 同步组(Broadcast Isochronous Group, BIG):手机直接广播音频流,左右耳各自接收。左右耳之间不需要建立链路,各听各的。
  • 连接同步(Connected Isochronous Group, CIG):手机跟左右耳分别建立同步连接(CIS),左右耳之间也可以有辅助链路。

说白了,LE Audio让左右耳“独立”了。我记得第一次调LE Audio的TWS时,发现左右耳同步精度比经典蓝牙高了一个数量级——经典蓝牙做到±20μs就算不错,LE Audio轻松做到±5μs。为什么会这样?因为LE Audio的同步机制是基于时间戳的,而不是基于数据包转发的。

关键点:LE Audio的TWS连接,左右耳之间不再需要实时转发音频数据。这大大降低了功耗和延迟。但代价是,左右耳需要各自维护一个精确的时钟同步。

3.3 Snoop与Copy模式详解

好,接下来是重头戏。Snoop和Copy,这两个词在TWS开发里经常被提起。它们解决的是同一个问题:副耳怎么拿到音频数据?

3.3.1 Snoop模式

Snoop模式,直译就是“偷听模式”。副耳不直接跟手机连接,而是“偷听”主耳和手机之间的通信。具体来说:

  • 主耳跟手机建立ACL链路,副耳也加入这个微微网(Piconet)。
  • 副耳监听主耳和手机之间的数据包,但只收不发。
  • 副耳根据主耳的时钟和跳频序列,同步接收数据。

嗯,这里要注意:Snoop模式要求副耳能解析主耳和手机之间的链路层数据。我刚开始做的时候,以为副耳只要“听”就行了,结果发现副耳必须知道主耳的蓝牙地址和时钟偏移,否则根本解不了包。

个人经验:Snoop模式的好处是延迟低,因为副耳跟主耳几乎同时收到数据。但坏处是,如果主耳和手机之间的链路质量差,副耳也会跟着遭殃。我曾经在一个项目中,主耳离手机3米,副耳在口袋里,结果副耳丢包率高达30%。后来我加了重传机制,才把丢包率压到5%以下。

3.3.2 Copy模式

Copy模式,就是“复制转发模式”。主耳收到手机的数据后,再复制一份发给副耳。流程如下:

  1. 手机发数据给主耳。
  2. 主耳解码后,把音频数据复制一份。
  3. 主耳通过另一条ACL链路,把复制数据发给副耳。

Copy模式的好处是,副耳不需要“偷听”,它只需要跟主耳保持一条稳定的链路就行。但坏处是,主耳的负担重了——既要跟手机通信,又要转发给副耳,功耗和延迟都会增加。

我建议,如果产品追求低功耗,优先考虑Snoop模式;如果追求稳定性,Copy模式更靠谱。当然,现在很多芯片方案支持动态切换——信号好时用Snoop,信号差时切到Copy。嗯,这个思路挺聪明的。

避坑指南:我曾经在Copy模式下踩过一个坑。主耳转发数据时,如果副耳没及时应答,主耳会一直重传,导致主耳功耗飙升。后来我加了超时机制:如果副耳连续3次没应答,主耳就暂停转发,等副耳重新连上再继续。

3.4 两种模式的对比

为了让你看得更清楚,我整理了一个表格:

特性 Snoop模式 Copy模式
主耳功耗
副耳功耗
延迟
稳定性 依赖主-手机链路 依赖主-副链路
实现复杂度

你想想看,如果产品定位是运动耳机,用户经常把手机放口袋里,主耳和手机之间可能有遮挡,这时候Copy模式更稳。如果是办公耳机,手机就在桌上,Snoop模式更省电。

3.5 代码示例:Snoop模式初始化

最后,给一段伪代码,展示Snoop模式下副耳如何初始化监听:

// 副耳初始化Snoop模式
void snoop_init(uint8_t *master_bd_addr, uint32_t clock_offset) {
    // 1. 设置跳频同步
    hci_set_afh_channel_map(master_bd_addr, channel_map);
    
    // 2. 同步主耳时钟
    hci_set_clock_offset(clock_offset);
    
    // 3. 进入监听状态
    hci_set_sniff_mode(0x0012); // 监听间隔:18.75ms
    
    // 4. 注册数据回调
    register_snoop_callback(snoop_data_handler);
}

void snoop_data_handler(uint8_t *data, uint16_t len) {
    // 解析音频数据
    if (is_audio_packet(data)) {
        decode_lc3(data, len);
        play_audio();
    }
}

这段代码看着简单,但实际调起来坑不少。我记得第一次跑Snoop模式时,副耳死活收不到数据。查了半天,发现是时钟偏移没算对——主耳和副耳的晶振有±20ppm的误差,时间一长就漂了。后来我加了周期性时钟同步,每10秒校准一次,问题才解决。

好了,这一章就到这儿。连接流程是TWS的基础,Snoop和Copy是核心设计决策。下一章咱们聊聊音频同步——嗯,那才是真正考验功力的地方。