🎧 蓝牙耳机

基于经典蓝牙 A2DP + HFP Profile 的音频设备,支持立体声音乐和通话功能。

📐 系统架构

flowchart TB subgraph Headset["蓝牙耳机"] MIC[麦克风] --> CODEC[音频 Codec] CODEC --> BT[BLE+ 经典双模芯片] BT --> ANT[天线] BT --> AMP[功放] AMP --> SPK[扬声器] end subgraph Phone["手机"] APP[音乐 App] --> AUDIO[音频系统] AUDIO --> BT2[BLE+ 经典双模芯片] BT2 --> ANT2[天线] end ANT <-->|A2DP 音频流 | ANT2 ANT <-->|HFP 通话 | ANT2 ANT -.->|BLE 控制 | ANT2 style Headset fill:#e3f2fd,stroke:#007bff style Phone fill:#d4edda,stroke:#28a745

📋 蓝牙音频 Profile

flowchart TB subgraph Classic["经典蓝牙 Profile"] A2DP["A2DP
音频传输"] AVRCP["AVRCP
远程控制"] HFP["HFP/HSP
免提通话"] end subgraph BLE["低功耗蓝牙"] BATTERY["电池服务"] FINDME["查找耳机"] FWUPD["固件升级"] end A2DP --> CODEC1{音频编码} CODEC1 --> SBC[SBC] CODEC1 --> AAC[AAC] CODEC1 --> APTX[aptX] CODEC1 --> LDAC[LDAC] style Classic fill:#fff3cd,stroke:#ffc107 style BLE fill:#e3f2fd,stroke:#007bff

📊 A2DP 音频传输流程

sequenceDiagram participant Phone as 手机 participant Headset as 蓝牙耳机 Note over Phone,Headset: 1. 经典蓝牙连接 Phone->>Headset: Inquiry/Page Headset-->>Phone: 连接完成 Note over Phone,Headset: 2. 服务发现 (SDP) Phone->>Headset: SDP 查询 A2DP Headset-->>Phone: 返回 A2DP/AVRCP UUID Note over Phone,Headset: 3. A2DP 信令 Phone->>Headset: L2CAP 连接 (信令通道) Phone->>Headset: 配置 Codec (SBC) Headset-->>Phone: 配置确认 Note over Phone,Headset: 4. 音频流传输 Phone->>Headset: L2CAP 连接 (媒体通道) loop 持续传输 Phone->>Headset: RTP 音频包 (10ms) end Note over Phone,Headset: 5. 播放控制 Phone->>Headset: AVRCP Play/Pause Headset-->>Phone: 执行确认

📦 A2DP 数据包结构

// A2DP 音频数据包结构
// 基于 L2CAP ACL 数据

typedef struct {
    // L2CAP Header (4 bytes)
    uint16_t l2cap_len;      // L2CAP 数据长度
    uint16_t l2cap_cid;      // Channel ID (媒体通道)
    
    // RTP Header (12 bytes)
    uint8_t  rtp_v_p_x_cc;   // Version, Padding, Extension, CC
    uint8_t  rtp_m_pt;       // Marker, Payload Type
    uint16_t rtp_seq;        // Sequence Number
    uint32_t rtp_timestamp;  // Timestamp
    uint32_t rtp_ssrc;       // SSRC
    
    // AVDTP Media Payload
    uint8_t  frame_count;    // 帧数量
    uint8_t  frame_len[4];   // 每帧长度
    uint8_t  audio_data[];   // SBC/AAC 音频数据
} a2dp_packet_t;

// SBC 音频帧头
typedef struct {
    uint8_t  sync_word;      // 同步字 0x9C
    uint8_t  freq_samp;      // 采样率 + 块数 + 声道 + 分配方法
    uint8_t  subbands;       // 子带数 + 联合声道
    uint8_t  snr_alloc;      // 信噪比 + 比特池
    uint8_t  scale_factor[]; // 缩放因子
    uint8_t  audio_data[];   // 音频数据
} sbc_frame_t;

// 典型配置:44.1kHz, 立体声,SBC
// 每包约 120-150 bytes,每 10ms 发送一次

📈 完整连接流程

flowchart TD A[开机] --> B[BLE 广播] B --> C{手机连接?} C -->|是 | D[BLE 配对] C -->|否 | B D --> E[经典蓝牙可发现] E --> F{经典蓝牙连接?} F -->|是 | G[建立 A2DP+HFP] F -->|否 | E G --> H{音频播放?} H -->|是 | I[A2DP 流传输] H -->|否 | J{来电?} I --> K[播放音乐] J -->|是 | L[HFP 通话] J -->|否 | M[待机] K --> N{暂停/结束?} L --> N N -->|是 | M N -->|否 | I M --> O{断开?} O -->|是 | B O -->|否 | M style A fill:#28a745,color:#fff style I fill:#007bff,color:#fff style L fill:#ffc107,color:#000 style B fill:#dc3545,color:#fff

💻 HFP 通话控制

// HFP AT 命令交互
// 通过 RFCOMM 通道传输

// 耳机发送 AT 命令
const char *hfp_at_commands[] = {
    "AT+BRSF=1023\r\n",      // 支持的功能
    "AT+CIND=?",             // 查询指示器
    "AT+CLCC=?",             // 查询当前通话
    "ATA\r\n",               // 接听电话
    "AT+CHUP\r\n",           // 挂断电话
    "AT+CKPD=200\r\n",       // 按键按下 (多功能键)
};

// 手机响应
const char *hfp_responses[] = {
    "+BRSF: 1023",           // 功能确认
    "+CIND: (\"batt\",(0-5))", // 电池指示器
    "OK",                    // 命令成功
   "RING",                   // 来电提醒
    "+CLCC: 1,1,4,0,\"123456789\"", // 通话列表
};

// HFP 事件处理
void hfp_event_handler(hfp_event_t event) {
    switch (event) {
        case HFP_EVENT_CALL_INCOMING:
            // 来电:播放铃声
            play_ringtone();
            break;
            
        case HFP_EVENT_CALL_CONNECTED:
            // 通话建立:切换音频路由
            audio_route_to_speaker();
            break;
            
        case HFP_EVENT_CALL_ENDED:
            // 通话结束:恢复待机
            audio_route_to_idle();
            break;
            
        case HFP_EVENT_AUDIO_CONNECTED:
            // A2DP 音频连接
            a2dp_streaming_start();
            break;
    }
}

🎵 音频编码对比

编码 码率 延迟 音质 兼容性
SBC 328 kbps ~150ms ★★★☆☆ 100% (强制支持)
AAC 256 kbps ~100ms ★★★★☆ iOS/Android
aptX 352 kbps ~70ms ★★★★☆ Qualcomm 芯片
aptX HD 576 kbps ~150ms ★★★★★ 高端设备
LDAC 990 kbps ~150ms ★★★★★ Sony/Android
LC3 160-345 kbps ~40ms ★★★★☆ LE Audio (蓝牙 5.2+)

🔋 低功耗设计

功耗模式

  • 播放中: 30-50mA
  • 通话中: 40-60mA
  • 待机: 3-5mA
  • 休眠: <50μA

省电策略

  • 无音频时进入 Sniff 模式
  • BLE 广播替代经典蓝牙
  • 自动关机 (30 分钟无操作)
  • 入耳检测暂停播放

🔍 调试要点

常见问题

  • 连接失败:清除配对重试
  • 音频卡顿:干扰/距离过远
  • 不同步:重新配对
  • 通话无声:检查 HFP 路由

推荐工具

  • Frontline: 协议分析仪
  • Ellisys: 蓝牙分析仪
  • Wireshark: HCI 抓包
  • Audio Tester: 音质测试

🔧 A2DP 数据包构建详解

从音频采样到蓝牙空中传输,每一层如何添加头部,组合成完整的音频数据包。

数据包组合流程 (A2DP 音频流)

flowchart TB subgraph Step1["步骤 1: 音频采样"] A1["PCM 音频数据
44.1kHz 16-bit 立体声"] end subgraph Step2["步骤 2: 编码"] A2["SBC/AAC 编码
压缩音频帧"] end subgraph Step3["步骤 3: RTP 封装"] A3["RTP Header
12 bytes"] end subgraph Step4["步骤 4: AVDTP"] A4["AVDTP Media Header
1-4 bytes"] end subgraph Step5["步骤 5: L2CAP"] A5["L2CAP Header
4 bytes"] end subgraph Step6["步骤 6: HCI ACL"] A6["HCI ACL Header
4 bytes"] end subgraph Step7["步骤 7: LL+PHY"] A7["LL Header + MIC + CRC
前导码 + 访问地址"] end A1 --> A2 A2 --> A3 A3 --> A4 A4 --> A5 A5 --> A6 A6 --> A7 style Step1 fill:#d4edda,stroke:#28a745 style Step2 fill:#e3f2fd,stroke:#007bff style Step3 fill:#e3f2fd,stroke:#007bff style Step4 fill:#e3f2fd,stroke:#007bff style Step5 fill:#fff3cd,stroke:#ffc107 style Step6 fill:#d4edda,stroke:#28a745 style Step7 fill:#d4edda,stroke:#28a745

完整数据包结构图 (A2DP)

flowchart LR subgraph RF["RF 空中传输"] R1["RF Packet
约 340 bytes"] end subgraph LL["Link Layer"] L1["DH5 Packet
339 bytes 数据"] end subgraph HCI["HCI ACL"] H1["HCI ACL Header
4B"] H2["ACL Data
308B"] end subgraph L2CAP["L2CAP"] C1["L2CAP Header
4B"] C2["AVDTP Data
304B"] end subgraph AVDTP["AVDTP"] A1["AVDTP Header
2B"] A2["RTP Packet
302B"] end subgraph RTP["RTP"] R2["RTP Header
12B"] R3["SBC Frame
290B"] end subgraph SBC["SBC 编码"] S1["SBC Header
4B"] S2["Audio Data
286B"] end R1 --> LL L1 --> HCI H2 --> L2CAP C2 --> AVDTP A2 --> RTP R3 --> SBC style RF fill:#d4edda,stroke:#28a745 style LL fill:#e3f2fd,stroke:#007bff style HCI fill:#fff3cd,stroke:#ffc107 style L2CAP fill:#e3f2fd,stroke:#007bff style AVDTP fill:#e3f2fd,stroke:#007bff style RTP fill:#e3f2fd,stroke:#007bff style SBC fill:#d4edda,stroke:#28a745

数据包大小计算 (A2DP 音频流)

头部大小 数据大小 本层总大小 说明
PCM 音频 0 B 1764 B (10ms) 1764 B 44.1kHz 16-bit 立体声
SBC 编码 4 B 286 B 290 B 压缩比约 1:6
RTP 12 B 290 B (SBC) 302 B 时间戳 + 序列号
AVDTP 2 B 302 B (RTP) 304 B 媒体包头
L2CAP 4 B 304 B (AVDTP) 308 B CID=0x0040
HCI ACL 4 B 308 B (L2CAP) 312 B 连接句柄
经典蓝牙 LL 可变 312 B (HCI) 约 340 B DH5 包类型

HFP 通话数据包 (SCO 链路)

// HFP 通话使用 SCO (Synchronous Connection-Oriented) 链路
// 固定间隔发送,保证实时性

// CVSD 编码 (传统)
// 每包 10 bytes, 每 3.75ms 发送一次
uint8_t sco_cvsd[] = {
    // SCO Header
    0x00, 0x01, 0x02,  // 3 bytes 同步信息
    // CVSD 音频数据
    0x10, 0x20, 0x30, 0x40, 0x50,  // 5 bytes 音频
    0x60, 0x70, 0x80, 0x90         // 4 bytes 音频
};
// 总长度:12 bytes, 每包 3.75ms

// mSBC 编码 (宽带语音,HFP 1.6+)
// 每包 60 bytes, 每 7.5ms 发送一次
uint8_t sco_msbc[] = {
    // mSBC 帧头
    0x00, 0x01, 0x02, 0x03,  // 4 bytes 同步
    // mSBC 音频数据
    // ... 56 bytes 音频数据
};
// 总长度:60 bytes, 每包 7.5ms, 音质更好

音频编码参数对比

编码 每包大小 发送间隔 码率 数据包/秒
SBC 约 290 B 10 ms 328 kbps 100 包/秒
AAC 约 250 B 10 ms 256 kbps 100 包/秒
aptX 约 350 B 10 ms 352 kbps 100 包/秒
LDAC 约 990 B 10 ms 990 kbps 100 包/秒
LC3 (LE Audio) 约 160 B 10 ms 160 kbps 100 包/秒
CVSD (HFP) 10 B 3.75 ms 64 kbps 267 包/秒
mSBC (HFP) 60 B 7.5 ms 128 kbps 133 包/秒

🆕 LE Audio (蓝牙 5.2+)

flowchart TB subgraph LEA["LE Audio 特性"] LC3[LC3 编码
低功耗高音质] MULTI[多设备连接
同时播放] BROADCAST[广播音频
公共场所] HEARING[助听器支持
低功耗] end subgraph Classic["经典音频"] A2DP2[A2DP] HFP2[HFP] end LEA --> BENEFIT[优势
低功耗 + 多设备 + 广播] Classic --> LIMIT[限制
功耗高 + 单连接] style LEA fill:#d4edda,stroke:#28a745 style Classic fill:#fff3cd,stroke:#ffc107