蓝牙协议栈与功耗管理:BLE与Classic蓝牙的选择

做TWS耳机这么多年,我经常被问到的一个问题是:为什么不用Classic蓝牙?毕竟它音质更好,延迟也更低。嗯,这个问题其实挺有意思的。咱们今天就从功耗角度,好好聊聊这个选择。

BLE vs Classic蓝牙:功耗差异的本质

说白了,Classic蓝牙(BR/EDR)是为持续连接设计的。它建立连接后,射频会一直保持活跃状态。你想想看,耳机戴在耳朵上,可能大部分时间都在听歌,但也有很多时间是待机状态。Classic蓝牙在这种场景下,功耗表现就不太理想了。

BLE(低功耗蓝牙)则完全不同。它的设计哲学是:能睡就睡,醒了赶紧干活。我在项目中遇到过,同样的电池容量,用BLE方案的耳机待机时间能比Classic方案长3-5倍。这不是夸张,是实测数据。

特性 Classic蓝牙 BLE
连接建立时间 ~3秒 ~6ms
峰值电流 ~30mA ~15mA
待机电流 ~1mA ~1μA
典型应用 音频流 数据交互

核心结论:TWS耳机的主连接建议使用BLE。Classic蓝牙只适合做音频传输的辅助通道。

蓝牙连接状态机

状态机这东西,我刚开始做蓝牙开发时觉得很简单。直到有一次,耳机在口袋里莫名其妙地断连了...嗯,从那以后我重新审视了状态机的设计。

BLE的五个核心状态

  • Standby(待机):啥也不干,射频关闭。这是功耗最低的状态。
  • Advertising(广播):向外喊话,告诉别人我在这儿。广播间隔决定了功耗。
  • Scanning(扫描):听别人在喊什么。扫描窗口和间隔是关键参数。
  • Initiating(发起连接):找到目标后,建立连接的过程。
  • Connected(已连接):稳定连接状态,按连接间隔周期性唤醒。

我的经验:状态切换的时机比状态本身更重要。比如从Connected切回Standby,如果处理不当,可能会多消耗几十毫安的电流。

广播与扫描功耗优化

广播和扫描,这两个操作看似简单,但坑特别多。我曾经在一个项目中,就因为广播参数没调好,导致耳机待机时间少了整整一天。

广播参数调优

广播间隔是个双刃剑。间隔短,被发现得快,但功耗高。间隔长,省电,但配对体验差。我个人的习惯是:

  • 快速广播模式:20-50ms间隔,用于配对阶段。持续3-5秒后切换。
  • 慢速广播模式:500-1000ms间隔,用于待机状态下的广播。
  • 定向广播:只对特定设备广播,减少无效广播次数。
// 广播参数配置示例
typedef struct {
    uint16_t adv_interval;    // 广播间隔,单位0.625ms
    uint8_t  adv_type;        // 广播类型
    uint8_t  adv_channel_map; // 广播信道
} adv_params_t;

// 快速广播:30ms间隔
adv_params_t fast_adv = {
    .adv_interval = 48,  // 30ms / 0.625ms = 48
    .adv_type = ADV_IND,
    .adv_channel_map = 0x07  // 三个信道全开
};

// 慢速广播:1000ms间隔
adv_params_t slow_adv = {
    .adv_interval = 1600, // 1000ms / 0.625ms = 1600
    .adv_type = ADV_NONCONN_IND,
    .adv_channel_map = 0x04  // 只用一个信道
};

注意:广播信道不要全开!三个信道全开虽然能提高被发现概率,但功耗会增加30%以上。我建议在信号好的环境下只开一个信道。

扫描参数优化

扫描的功耗主要取决于扫描窗口和扫描间隔的比例。这个比例我一般控制在10%-20%之间。

扫描窗口 扫描间隔 占空比 典型功耗
30ms 300ms 10% ~0.5mA
60ms 300ms 20% ~1.0mA
100ms 500ms 20% ~1.2mA

连接间隔对功耗的影响

连接间隔,这是BLE功耗管理中最核心的参数。说白了,就是两个设备之间多久同步一次数据。

连接间隔与功耗的关系

连接间隔越小,数据延迟越低,但功耗越高。反之亦然。我在项目中做过一个测试:

  • 7.5ms连接间隔:延迟低,但电流高达8mA。适合游戏模式。
  • 30ms连接间隔:平衡点,电流约3mA。日常使用推荐。
  • 100ms连接间隔:省电模式,电流约1mA。适合待机状态。

关键技巧:动态调整连接间隔!耳机在播放音乐时用30ms,暂停时切换到100ms,待机时切换到500ms。这样能省下30%以上的电量。

连接间隔的协商机制

BLE协议允许主从设备协商连接间隔。我建议在连接建立后,根据实际场景动态调整:

// 连接参数更新请求
typedef struct {
    uint16_t interval_min;   // 最小连接间隔
    uint16_t interval_max;   // 最大连接间隔
    uint16_t latency;        // 从设备延迟
    uint16_t timeout;        // 超时时间
} conn_params_t;

// 音乐播放模式
conn_params_t music_mode = {
    .interval_min = 48,   // 30ms
    .interval_max = 48,   // 30ms
    .latency = 0,
    .timeout = 400        // 2秒超时
};

// 待机模式
conn_params_t standby_mode = {
    .interval_min = 160,  // 100ms
    .interval_max = 800,  // 500ms
    .latency = 4,         // 允许4个间隔不响应
    .timeout = 800        // 4秒超时
};

避坑指南:我曾经把连接间隔设得太大,结果耳机在口袋里经常断连。后来发现是超时时间没同步调整。记住:连接间隔和超时时间要成比例调整,一般超时时间至少是连接间隔的10倍。

嗯,蓝牙协议栈的功耗管理,说白了就是一场平衡游戏。你要在功耗、延迟、稳定性之间找到最佳点。没有银弹,只有根据实际场景不断调优。我建议你在开发板上先跑一遍默认参数,然后用电流表实测,再根据数据做调整。这样出来的方案,才经得起市场的考验。