TWS 耳机对等通信协议 (Master/Slave)¶
芯片: 杰理 AC7002 | 蓝牙版本: BT 5.3 | 传输层: BLE L2CAP CoC
文档版本: v1.0 | 最后更新: 2026-05-17
1. 概述¶
本文档定义 TWS 左右耳机之间的私有对等通信协议。左右耳机通过 BLE L2CAP Connection-oriented Channel (CoC) 建立私有数据通道,实现角色协商、状态同步、按键镜像、音频同步等功能。
1.1 角色定义¶
| 角色 | 简称 | 说明 |
|---|---|---|
| Master | M | 主耳机,负责与手机建立 BLE 连接,转发音频流,管理 TWS 链路 |
| Slave | S | 从耳机,通过 TWS 私有通道与 Master 通信,接收音频和命令 |
| Mono | — | 单耳模式(另一只未取出或不在范围内) |
1.2 角色协商原则¶
- 谁先出盒谁为 Master:从充电仓取出的第一只耳机自动成为 Master
- 后出盒为 Slave:后取出的耳机自动检测到 Master 广播并连接为 Slave
- 同时出盒:比较蓝牙 MAC 地址,较低者优先成为 Master
- Master 入盒:Master 检测到入盒后,将角色转让给 Slave,Slave 升为 Master
- 链路断开:Slave 若丢失 Master 连接且检测到自身不在盒中,自动升为 Master
1.3 传输层¶
TWS 私有通道建立在 BLE L2CAP Connection-oriented Channel (CoC) 之上:
| 参数 | 值 |
|---|---|
| 传输层 | BLE L2CAP CoC (CID: 0x0040 ~ 0x007F, 动态分配) |
| PSM | 0x00FF (自定义, 需在芯片协议栈注册) |
| MTU | 最小 64 字节, 建议 128 字节 |
| MPS | 最小 64 字节 |
| 优先级 | 高优先级 (音频同步帧 > 状态帧 > 心跳) |
1.4 协议栈分层¶
2. TWS 链路建立流程¶
2.1 出盒连接序列¶
2.2 链路建立时序¶
3. 数据包格式¶
3.1 通用数据包结构¶
所有 TWS 协议数据包使用统一的帧格式:
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Magic High | 魔数高字节: 0x54 ('T') |
| 1 | 1 | Magic Low | 魔数低字节: 0x57 ('W') |
| 2 | 1 | Length | 负载长度 (不包含头部), 0~64 |
| 3 | 1 | Msg Type | 消息类型 (见 §4) |
| 4 | 1 | Reserved | 保留字节, 固定 0x00 |
| 5 | 1 | Sequence | 序列号 (发送端自增, 用于去重/乱序检测) |
| 6 | 1 | Checksum | XOR 校验: 所有之前字节 (0~5) + 全部 Payload 的 XOR |
| 7 | N | Payload | 负载数据 (N = Length) |
包头固定长度: 7 字节 最大包总长: 7 + 64 = 71 字节
3.2 序列号管理¶
- 每个消息类型维护独立的序列号计数器
- 发送方每发一帧自增 1, 循环 0~255
- 接收方检测序列号跳跃超过阈值 (≥64) 时触发重传请求
- Slave 侧处理乱序: 若 seq 与预期相差 ±2 以内, 按当前值处理; 否则丢弃
3.3 校验与可靠性¶
| 机制 | 说明 |
|---|---|
| XOR Checksum | 每个包尾部 1 字节 XOR 校验, 丢弃无效包 |
| ACK/NACK | 关键消息 (角色协商/切换) 需要 ACK 确认 |
| 超时重传 | 无 ACK 的消息最多重试 3 次, 间隔 100ms |
| 心跳保活 | 空闲时每 5 秒发送心跳帧 |
4. 消息类型定义¶
4.1 消息总表¶
| Type | 消息名称 | 方向 | 需 ACK | 说明 |
|---|---|---|---|---|
0x01 | ROLE_NEGOTIATE | M→S, S→M | 是 | 角色协商请求 |
0x02 | ROLE_ANNOUNCE | M→S, S→M | 是 | 角色宣告确认 |
0x03 | ROLE_SWITCH | M→S | 是 | 角色切换通知 (Master 入盒) |
0x10 | TOUCH_EVENT | S→M | 否 | 触控按键事件同步 |
0x11 | COMMAND | M→S | 否 | 控制命令转发 |
0x12 | COMMAND_ACK | S→M | 否 | 命令执行结果回复 |
0x20 | AUDIO_SYNC | M→S | 否 | 音频同步帧 |
0x21 | AUDIO_START | M→S | 否 | 音频流启动通知 |
0x22 | AUDIO_STOP | M→S | 否 | 音频流停止通知 |
0x30 | STATE_SYNC | 双向 | 否 | 全状态同步 |
0x31 | STATE_UPDATE | 双向 | 否 | 增量状态更新 |
0x40 | HEARTBEAT | 双向 | 否 | 心跳保活 |
0x41 | HEARTBEAT_ACK | 双向 | 否 | 心跳回复 |
0x50 | PING | M→S | 否 | 延迟测量 |
0x51 | PONG | S→M | 否 | 延迟回复 |
0xFF | ERROR | 双向 | 否 | 错误报告 |
4.2 消息负载格式¶
4.2.1 ROLE_NEGOTIATE (0x01)¶
方向: Master → Slave 或 Slave → Master (双向协商)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Claimed Role | 0x01=Master, 0x02=Slave |
| 1 | 6 | MAC Address | 发送方蓝牙 MAC 地址 |
4.2.2 ROLE_ANNOUNCE (0x02)¶
方向: Master → Slave (宣告裁决结果)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Assigned Role | 0x01=Master 保留, 0x02=Slave, 0x03=Mono |
| 1 | 6 | Master MAC | 最终 Master 的 MAC 地址 |
4.2.3 ROLE_SWITCH (0x03)¶
方向: Master → Slave (Master 即将入盒, 转让角色)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Reason | 0x01=入盒, 0x02=低电, 0x03=用户主动 |
| 1 | 2 | BLE Conn Handle | 当前手机 BLE 连接句柄 (0xFFFF=无) |
| 3 | 1 | Phone Connected | 0x00=无手机连接, 0x01=已连接手机 |
| 4 | 1 | Reserved | 保留 |
4.2.4 TOUCH_EVENT (0x10)¶
方向: Slave → Master (Slave 触控事件同步给 Master)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Touch Event | 0x01=单击, 0x02=双击, 0x03=三击, 0x04=长按保持, 0x05=长按抬起, 0x06=上滑, 0x07=下滑 |
| 1 | 1 | Reserved | 保留 |
Slave 触控事件处理流程:
- Slave 检测到触控事件
- Slave 通过 TOUCH_EVENT 消息发送到 Master
- Master 收到后执行对应动作 (如播放/暂停/音量)
- 若动作涉及音频状态变化, Master 通过 STATE_UPDATE 同步回 Slave
4.2.5 COMMAND (0x11)¶
方向: Master → Slave (Master 转发手机控制命令)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Command ID | 与 BLE 协议 Command ID 一致 (见 ble-protocol.md §3.2.2) |
| 1 | 1 | Parameter | 命令参数 |
| 2 | 1 | Reserved | 保留 |
4.2.6 COMMAND_ACK (0x12)¶
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Command ID | 对应命令 ID |
| 1 | 1 | Result | 0x00=成功, 0x01=不支持, 0x02=参数无效, 0x03=忙 |
4.2.7 AUDIO_SYNC (0x20)¶
方向: Master → Slave (音频时间同步, 周期性发送 ~100ms)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 4 | Timestamp | Master 音频时钟时间戳 (微秒级) |
| 4 | 1 | Frame Seq | 音频帧序列号 (0~255, 循环) |
| 5 | 1 | Src Buffer | Master 当前音频缓冲区水位 (0~100%) |
| 6 | 1 | Reserved | 保留 |
Slave 端处理:
- Slave 记录本地时钟 T_slave 和 Master 时间戳 T_master
- 计算时钟偏差:
offset = T_master - T_slave - 通过 PI 控制器调整 Slave 的音频采样率, 使左右声道同步
- 缓冲区水位低于 20% 或高于 80% 时触发速率调整
4.2.8 AUDIO_START (0x21) / AUDIO_STOP (0x22)¶
负载 1 字节保留, 固定 0x00。
4.2.9 STATE_SYNC (0x30)¶
方向: 双向 (首次连接时全同步, 或主动请求)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Playback | 0x00=停止, 0x01=播放中, 0x02=暂停 |
| 1 | 1 | Volume | 音量 0~100 |
| 2 | 1 | ANC Mode | 0x00=关闭, 0x01=降噪, 0x02=通透, 0x03=抗风噪 |
| 3 | 1 | EQ Mode | 0x00=默认, 0x01=流行, 0x02=古典, 等 |
| 4 | 1 | Battery L | 左耳电量百分比 0~100 |
| 5 | 1 | Battery R | 右耳电量百分比 0~100 |
| 6 | 1 | Flags | Bit0: 盒内(L), Bit1: 盒内(R), Bit2: 充电中, Bit3: 佩戴(L), Bit4: 佩戴(R), Bit5~7: 保留 |
| 7 | 1 | Reserved | 保留 |
4.2.10 STATE_UPDATE (0x31)¶
方向: 双向 (增量更新, 仅发送变化字段)
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Change Mask | 变更掩码, 见下表 |
| 1 | N | New Values | 按 mask 位顺序排列的字段新值 |
Change Mask 定义:
| Bit | 字段 | 值长度 |
|---|---|---|
| 0 | Playback | 1 byte |
| 1 | Volume | 1 byte |
| 2 | ANC Mode | 1 byte |
| 3 | EQ Mode | 1 byte |
| 4 | Battery L | 1 byte |
| 5 | Battery R | 1 byte |
| 6 | Flags | 1 byte |
| 7 | (保留) | — |
示例: Change Mask = 0x05 (bit0 + bit2) → Payload = [0x01, 0x02] (Playback=播放中, ANC=通透)
4.2.11 HEARTBEAT (0x40)¶
负载 1 字节保留。HEARTBEAT_ACK (0x41) 格式相同。
心跳策略:
| 参数 | 值 |
|---|---|
| 空闲发送间隔 | 5 秒 |
| 超时判定 | 连续 3 次无回复 (15 秒) |
| 超时后行为 | 尝试重建 L2CAP 通道, 3 次失败后触发角色重协商 |
4.2.12 PING/PONG (0x50/0x51)¶
方向: Master → Slave (Ping), Slave → Master (Pong)
Slave 收到 PING 后立即回复 PONG, 携带收到的 Timestamp。Master 计算 RTT = (T_recv - T_send) / 2。
4.2.13 ERROR (0xFF)¶
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 1 | Error Code | 0x01=协议版本不匹配, 0x02=校验失败, 0x03=超时, 0x04=不支持的消息类型 |
| 1 | 1 | Ref Type | 引起错误的原始消息类型 |
5. 角色管理¶
5.1 状态机¶
5.2 角色切换 (Role Switch) 时序¶
当 Master 检测到入盒 (充电触点接通) 时:
5.3 角色冲突处理¶
| 场景 | 处理方式 |
|---|---|
| 两个 Master 同时存在 | 比较 MAC 地址, 较低者保留 Master, 较高者降为 Slave |
| Slave 丢失 Master 连接 | Slave 等待 3s + 3 次心跳超时后自动升为 Master |
| Master 恢复并重新入网 | 检测到已有 Master, 自动降为 Slave (如需) |
| 链路 CRC 校验连续失败 | 触发 ERROR 消息, 然后重建 L2CAP 通道 |
6. 音频同步机制¶
6.1 同步原理¶
6.2 同步算法¶
- Master 每 100ms 发送 AUDIO_SYNC 帧, 携带当前音频时钟
- Slave 记录接收时间, 计算单向延迟 (假设 RTT/2)
- 使用 PI (比例-积分) 控制器调整 Slave 音频 DAC 采样率:
- 比例项:
P = Kp * (T_master - T_slave) - 积分项:
I += Ki * (T_master - T_slave) * dt - 调整量:
adjust = P + I, 限制在 ±500ppm 以内 - 缓冲区水位监控: 水位 < 20% 时加速采样, > 80% 时减速采样
6.3 初始同步¶
| 步骤 | 说明 |
|---|---|
| 1 | Master 发送 AUDIO_START 通知 Slave 准备 |
| 2 | Slave 清空音频缓冲区, 准备接收 |
| 3 | Master 发送连续 5 个 AUDIO_SYNC 帧 (10ms 间隔) |
| 4 | Slave 计算初始时钟偏移 |
| 5 | Slave 开始播放, 应用时钟调整 |
| 6 | 进入稳态同步 (100ms 间隔) |
7. 低功耗策略¶
| 场景 | TWS 链路行为 | 说明 |
|---|---|---|
| 播放中 | 活跃, 音频同步帧 ~100ms | 保持精确同步 |
| 空闲连接 | 仅心跳 ~5s | 降低功耗 |
| Slave 入盒 | 发送入盒通知, 断开 TWS | Slave 进入休眠 |
| Master 入盒 | 执行角色切换, 断开 TWS | 新 Master 保持心跳 |
| 双耳入盒 | 无 TWS 活动 | 深度休眠, 等待霍尔唤醒 |
8. 安全考虑¶
| 项 | 说明 |
|---|---|
| L2CAP CoC 加密 | 由 BLE 链路层加密 (AES-CCM) 保障 |
| 角色劫持防护 | ROLE_NEGOTIATE/ROLE_SWITCH 需 MAC 地址匹配 |
| 重放防护 | 递增序列号 + 接收端丢弃重复序列号 |
| 欺骗防护 | 校验失败的包直接丢弃, 连续失败触发 ERROR |
9. 版本历史¶
| 版本 | 日期 | 变更说明 |
|---|---|---|
| v1.0 | 2026-05-17 | 初稿, 定义 TWS 对等通信协议 |
文档结束