从零实现BLE协议栈(7-1)Slave Latency:低功耗的权衡艺术

Slave Latency:低功耗的权衡艺术

  • 理解连接事件的周期性调度(第 4-2 篇有详细讲解):Slave 默认每隔 connInterval 唤醒一次射频,监听 Master 的包,回复 ACK 或数据,然后关闭射频等待下一次唤醒。本文的全部讨论都建立在这个周期性循环之上。
  • 理解 Window Widening 的含义(第 5-2 篇有详细讲解):Slave 必须比预计的锚点更早打开 RX 窗口,以应对时钟漂移。WW 的大小直接影响 Slave Latency 跳过事件后重新监听时的功耗和成功率。
  • 理解 L2CAP 发送队列(第 6-2 篇有详细讲解):Slave Latency 的一个核心约束是“有数据要发就不能跳过”,这里的“有数据”指的就是 L2CAP TX 队列非空。

不需要了解 LL Control Procedure 或 Instant 机制。本文只讨论纯粹的 Slave Latency 行为。


一、为什么要有 Slave Latency

一个典型的 BLE 连接场景是智能手表和手机配对。连接建立后,手表每隔 30 ms(connInterval = 24)就要唤醒一次射频.整个过程中,射频从唤醒到关闭大约 300~500 µs,但唤醒本身——从深度睡眠到 PLL 锁相——消耗的电流远超空闲时的待机功耗。对一块 CR2032 纽扣电池而言,每 30 ms 唤醒一次射频将在几周内耗尽电量。

问题在于:绝大多数连接事件里,Master 发来的是空包(没有新数据),Slave 回的也是空包。两端只是在互相确认"对方还活着"——纯粹的心跳维持,没有有效载荷

如果 Slave 能够在没有数据要发的时候跳过一些连接事件不唤醒,功耗问题就迎刃而解了。这就是 Slave Latency(connSlaveLatency) 的设计初衷。

BLE Core Spec Vol 6, Part B, 4.5.2 定义了 Slave Latency 的行为:

The slave may transmit during any connection event that it listens or sends in. A slave does not need to listen during every connection event. The slave shall listen to at least every connSlaveLatency connection events even if it has no data to send.

翻译成操作规则:

  1. Slave 最多可以连续跳过 connSlaveLatency 个连接事件
  2. 跳过 connSlaveLatency 个之后,必须在下一个事件唤醒监听
  3. 如果 Slave 有数据要发(TX 队列非空),不得跳过,必须监听
  4. 跳过事件期间,信道选择算法仍需正常推进——下次唤醒时使用的信道编号必须和 Master 一致

connSlaveLatency 在连接建立时由 CONNECT_IND 的 Latency 字段给出,单位是"连接事件个数"。例如 Latency = 4 表示 Slave 最多跳过 4 个事件,即每 5 个事件至少醒一次。

这意味着 Slave 的有效唤醒周期从 connInterval 变成了 connInterval × (connSlaveLatency + 1)。例如:

connInterval connSlaveLatency 有效唤醒周期 射频唤醒频率
30 ms 0 30 ms 每秒 33 次
30 ms 4 150 ms 每秒 6.7 次
30 ms 9 300 ms 每秒 3.3 次

从每秒 33 次降到每秒 3.3 次,射频功耗降低约 90%

二、代码实现

实现 Slave Latency 最直观的误区是“直接让 CPU 睡到下次唤醒”。这会导致信道丢失和时钟脱节。跳过事件 \(\neq\) 什么都不做。

3.1 跳过判断函数

判断是否可以跳过当前连接事件的函数非常短,但每一个条件都至关重要:

static bool can_skip_event(void)
{
    /* 已经用完 latency 配额 — 必须唤醒 */
    if (lat.latency_used >= lat.latency_max) {
        return false;
    }

    /* 有数据要发 — 必须唤醒 */
    if (data_tx_q.count > 0 || tx_pdu_pending) {
        return false;
    }

    return true;
}

3.2 主循环中的跳过逻辑

在连接态主循环中,跳过和监听的分支:

while (!conn_terminated) {
    bool skip_this_event = false;

    if (lat.latency_max > 0 && can_skip_event()) {
        skip_this_event = true;
        lat.latency_used++;
        lat.events_skipped++;

        /* ★ 跳过时: 仍需推进信道选择! */
        (void)chan_sel_1();
        conn_event_counter++;
    } else {
        lat.latency_used = 0;
        lat.events_listened++;
    }

    if (!skip_this_event) {
        /* 正常监听: 打开射频、收包、发包 */
        ww_event_us += ww_periodic_us;
        /* ... conn_event() ... */
    } else {
        /* ★ 跳过时: WW 仍需累积 */
        ww_event_us += ww_periodic_us;
    }

    /* 锚点递推 (无论是否跳过) */
    next_event_rtc = (next_event_rtc + interval_ticks) &
                     HAL_TICKER_CNTR_MASK;
    next_event_remainder_ps += interval_remainder_ps;
    /* ... */
}

有三个关键操作在跳过时必须执行:

  1. chan_sel_1():推进信道选择算法,确保下次唤醒时使用正确的信道
  2. ww_event_us += ww_periodic_us:WW 按事件数线性累积
  3. 锚点递推next_event_rtc += interval_ticks,确保时间线正确

跳过事件时不执行的是 conn_event() 函数——即不打开射频、不进行 TX/RX 交换。CPU 在推进完状态后,直接进入下一个循环迭代。

3.3 Window Widening 的累积效应

这里需要特别强调 WW 的累积问题。假设 ww_periodic_us = 5 µs(每个连接事件的漂移上限),latency_max = 4

  • 跳过事件 #1:ww = 5 µs(不监听)
  • 跳过事件 #2:ww = 10 µs(不监听)
  • 跳过事件 #3:ww = 15 µs(不监听)
  • 跳过事件 #4:ww = 20 µs(不监听)
  • 监听事件 #5:ww = 25 µs → 打开射频,RX 窗口需要 HCTO = ... + 2 × 25 µs + ...

如果不累积 WW,第 5 个事件使用的 WW 只有 5 µs,远不够覆盖 5 个 interval 的总漂移。结果就是 RX 窗口太窄,Master 的包到达时 RX 已经关闭——连接失败。

WW 的累积直到成功收到 Master 的包后才重置为 0。这正是 conn_event()rx_ok 分支执行 ww_event_us = 0 的含义:成功同步一次,漂移归零,重新开始累积

三、Supervision Timeout 的安全约束

Slave 不能无限跳过事件。BLE 规范要求:

\[connSlaveLatency \times connInterval \times 2 < supervisionTimeout \]

这个约束保证即使 Slave 用满了 latency 配额,它在 Supervision Timeout 到期之前仍有足够多的连接事件来重新同步。如果不满足这个约束,可能出现以下场景:

  1. Slave 跳过了 connSlaveLatency 个事件
  2. connSlaveLatency + 1 事件唤醒后,RX 超时(干扰或时钟漂移过大)
  3. Slave 尝试在后续事件重试,但 Supervision Timeout 已经到期
  4. 连接被断开

在我们的实现中,即使 Slave 跳过了事件,supervision_timeout_us 的检查仍在每次非跳过事件的 conn_event() 失败分支中执行——一旦从上次成功 RX 算起超过了 supervisionTimeout,立即终止连接。


本系列教程同款硬件:👇
芯片: nRF 52832 开发板
工具: nRF 52840 BLE Dongle 蓝牙嗅探器
工具: 逻辑分析仪
工具: BPA low energy 蓝牙分析仪

posted @ 2026-04-02 10:17  ixbwer  阅读(103)  评论(0)    收藏  举报