跳转至

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 协议栈分层

+---------------------------------------------+
|             应用层 (App Layer)                |
|  角色管理 | 状态同步 | 按键镜像 | 音频同步   |
+---------------------------------------------+
|            TWS 协议层 (Protocol Layer)       |
|  打包/解析 | 序列号管理 | 校验 | 重传       |
+---------------------------------------------+
|             L2CAP CoC 传输层                 |
|      BLE 连接 | 流量控制 | 分段重组          |
+---------------------------------------------+

2. TWS 链路建立流程

2.1 出盒连接序列

出盒事件
    ├── 检测盒内传感器 → 不在盒中
    ├── 开始扫描 TWS 对端广播
    │   ├── 扫描到对端 → 尝试 L2CAP CoC 连接
    │   │   ├── 连接成功 → 进入角色协商流程
    │   │   └── 连接失败 → 成为 Master,开始广播
    │   │
    │   └── 未扫描到对端 (超时 3 秒)
    │       └── 成为 Master,开始可被发现的广播
    └── 角色裁定完成
        ├── Master: 开始 BLE 对手机广播
        └── Slave: 通过 TWS 链路等待命令

2.2 链路建立时序

Master                               Slave
  |                                    |
  |   [出盒]                           |
  |--- 扫描对端 (3s超时)               |
  |   (未找到)                         |
  |--- 成为 Master                     |
  |--- 开始 ADV (TWS发现通道)          |
  |                                    |  [出盒]
  |                                    |--- 扫描到 Master 广播
  |<--- L2CAP CoC 连接请求 ------------|
  |--- CoC 接受 ---------------------->|
  |                                    |
  |--- [ROLE_NEGOTIATE] ------------->|
  |<-- [ROLE_ANNOUNCE: SLAVE] --------|
  |                                    |
  |--- [STATE_SYNC: 全状态] --------->|
  |--- [AUDIO_SYNC: 参数] ----------->|
  |                                    |
  |        == TWS 链路就绪 ==          |

3. 数据包格式

3.1 通用数据包结构

所有 TWS 协议数据包使用统一的帧格式:

1
2
3
4
5
6
7
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|      Magic     |     Magic     |    Length     |   Msg Type   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Reserved    |    Sequence   |   Checksum    |   Payload... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
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 (双向协商)

1
2
3
4
5
6
7
 0                   1                   2
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Claimed Role |     MAC Address (6 bytes)      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  (cont) |
+-+-+-+-+
偏移 长度 字段 说明
0 1 Claimed Role 0x01=Master, 0x02=Slave
1 6 MAC Address 发送方蓝牙 MAC 地址

4.2.2 ROLE_ANNOUNCE (0x02)

方向: Master → Slave (宣告裁决结果)

1
2
3
4
5
6
7
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Assigned Role |  Master MAC  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  (cont, 6 bytes)               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
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 即将入盒, 转让角色)

1
2
3
4
5
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|    Reason     |  BLE Conn Handle |  Phone Connected | Reserved|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
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)

1
2
3
4
5
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Touch Event   |   Reserved   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
0 1 Touch Event 0x01=单击, 0x02=双击, 0x03=三击, 0x04=长按保持, 0x05=长按抬起, 0x06=上滑, 0x07=下滑
1 1 Reserved 保留

Slave 触控事件处理流程:

  1. Slave 检测到触控事件
  2. Slave 通过 TOUCH_EVENT 消息发送到 Master
  3. Master 收到后执行对应动作 (如播放/暂停/音量)
  4. 若动作涉及音频状态变化, Master 通过 STATE_UPDATE 同步回 Slave

4.2.5 COMMAND (0x11)

方向: Master → Slave (Master 转发手机控制命令)

1
2
3
4
5
 0                   1                   2
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Command ID  |   Parameter   |  Reserved     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
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)

1
2
3
4
5
6
7
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Timestamp (4 bytes, master clock, us)                  |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Frame Seq   |  Src Buffer  |  Reserved     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
0 4 Timestamp Master 音频时钟时间戳 (微秒级)
4 1 Frame Seq 音频帧序列号 (0~255, 循环)
5 1 Src Buffer Master 当前音频缓冲区水位 (0~100%)
6 1 Reserved 保留

Slave 端处理:

  1. Slave 记录本地时钟 T_slave 和 Master 时间戳 T_master
  2. 计算时钟偏差: offset = T_master - T_slave
  3. 通过 PI 控制器调整 Slave 的音频采样率, 使左右声道同步
  4. 缓冲区水位低于 20% 或高于 80% 时触发速率调整

4.2.8 AUDIO_START (0x21) / AUDIO_STOP (0x22)

1
2
3
4
5
 0
 0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|   Reserved    |
+-+-+-+-+-+-+-+-+

负载 1 字节保留, 固定 0x00

4.2.9 STATE_SYNC (0x30)

方向: 双向 (首次连接时全同步, 或主动请求)

1
2
3
4
5
6
7
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Playback     |  Volume       |  ANC Mode     |  EQ Mode      |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Battery L    |  Battery R    |  Flags        |  Reserved     |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
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)

方向: 双向 (增量更新, 仅发送变化字段)

1
2
3
4
5
6
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|  Change Mask  |  New Values   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   (可变长度, 按 mask 位决定)
偏移 长度 字段 说明
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
2
3
4
5
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|       Reserved                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

负载 1 字节保留。HEARTBEAT_ACK (0x41) 格式相同。

心跳策略:

参数
空闲发送间隔 5 秒
超时判定 连续 3 次无回复 (15 秒)
超时后行为 尝试重建 L2CAP 通道, 3 次失败后触发角色重协商

4.2.12 PING/PONG (0x50/0x51)

方向: Master → Slave (Ping), Slave → Master (Pong)

1
2
3
4
5
 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|        Timestamp (4 bytes, 发送方本地时钟, us)                |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Slave 收到 PING 后立即回复 PONG, 携带收到的 Timestamp。Master 计算 RTT = (T_recv - T_send) / 2。

4.2.13 ERROR (0xFF)

1
2
3
4
5
 0                   1
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|   Error Code   |   Ref Type   |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
偏移 长度 字段 说明
0 1 Error Code 0x01=协议版本不匹配, 0x02=校验失败, 0x03=超时, 0x04=不支持的消息类型
1 1 Ref Type 引起错误的原始消息类型

5. 角色管理

5.1 状态机

                     +-----------+
           +-------->|  INIT     |<---------+
           |         +-----------+           |
           |             |                   |
           |         [出盒检测]              |
           |             |                   |
           |    +--------v--------+          |
           |    |  SCANNING       |          |
           |    |  (扫描对端 3s)  |          |
           |    +--------+--------+          |
           |             |                   |
           |     +-------+-------+           |
           |     |               |           |
           | [找到对端]   [未找到]           |
           |     |               |           |
           |     v               v           |
           | +----------+  +-----------+     |
           | | NEGOTIATE|  | MASTER    |     |
           | +----+-----+  +-----+-----+     |
           |      |              |           |
           |      v              |           |
           | +----------+        |           |
           | | SLAVE    |        |           |
           | +----+-----+        |           |
           |      |              |           |
           | [Master入盒]  [Slave断开]      |
           |      |              |           |
           +------+----+----+----+           |
                      |    |                 |
                      v    v                 |
                +-----------+    [入盒关盖]  |
                | MONO      +----------------+
                +-----------+

5.2 角色切换 (Role Switch) 时序

当 Master 检测到入盒 (充电触点接通) 时:

Master (旧)                         Slave (旧) -> Master (新)
  |                                       |
  |--- [ROLE_SWITCH: Reason=IN_CASE] --->|
  |                                       |
  |   等待 ROLE_SWITCH_ACK               |
  |<-- [ROLE_ANNOUNCE: Assigned=MASTER] -|
  |                                       |
  |   Slave 升为 Master                  |
  |   (开始对手机广播, 接管音频路径)     |
  |                                       |
  |   保存最后状态到 NVS                 |
  |   进入低功耗/关机                     |
  |                                       |

5.3 角色冲突处理

场景 处理方式
两个 Master 同时存在 比较 MAC 地址, 较低者保留 Master, 较高者降为 Slave
Slave 丢失 Master 连接 Slave 等待 3s + 3 次心跳超时后自动升为 Master
Master 恢复并重新入网 检测到已有 Master, 自动降为 Slave (如需)
链路 CRC 校验连续失败 触发 ERROR 消息, 然后重建 L2CAP 通道

6. 音频同步机制

6.1 同步原理

Master 时钟域                    Slave 时钟域
+-------------+                 +-------------+
| Audio Clock |---- AUDIO_SYNC ->| Audio Clock |
| (48kHz)     |   (含时间戳)    | (48kHz)     |
+------+------+                 +------+------+
       |                               |
+------v------+                 +------v------+
| Buffer      |                 | Buffer      |
| (FIFO)      |                 | (FIFO)      |
+------+------+                 +------+------+
       |                               |
       v                               v
     Speaker                         Speaker

6.2 同步算法

  1. Master 每 100ms 发送 AUDIO_SYNC 帧, 携带当前音频时钟
  2. Slave 记录接收时间, 计算单向延迟 (假设 RTT/2)
  3. 使用 PI (比例-积分) 控制器调整 Slave 音频 DAC 采样率:
  4. 比例项: P = Kp * (T_master - T_slave)
  5. 积分项: I += Ki * (T_master - T_slave) * dt
  6. 调整量: adjust = P + I, 限制在 ±500ppm 以内
  7. 缓冲区水位监控: 水位 < 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 对等通信协议

文档结束