BLE 连接建立之后,主从双方并不是时刻开着射频,而是按连接间隔(connInterval)周期性地"醒来"通信。两次连接事件之间,设备会进入低功耗睡眠,只靠一个廉价的睡眠时钟 定时唤醒。这个时钟精度有限,于是规范引入 SCA(Sleep Clock Accuracy,睡眠时钟精度)来描述它,并靠"窗口扩展(Window Widening)"来容错。本文把这套机制讲透。
BLE 设备为了省电,在两次连接事件之间深度睡眠,只依赖一个低精度的睡眠时钟 来定时下一次醒来。常见的睡眠时钟是:
注意,睡眠时钟 ≠ 射频高速时钟。射频用的 16/24/32 MHz 晶振精度极高,但太费电,不可能一直开着。真正用来"排程下一次连接事件"的,是那个低精度的睡眠时钟。
ppm(百万分之一)的含义:
由于睡眠时钟会漂移,从机从睡眠中醒来时,并不知道主机精确的锚点(anchor point)时刻——它只能"大致"知道。因此必须提前打开接收窗口,并撑得足够宽,才能确保不错过主机的包。这就是 SCA 存在的根本意义。
主机在建立连接时,通过 CONNECT_IND 告诉从机自己的睡眠时钟精度(字段名为 Master SCA)。规范(Bluetooth Core Specification,Vol 6 Part B §4.5.7 及相关)用 3 个 bit 表示,共 7 个等级(取值 0~7):
| Master_SCA | 睡眠时钟精度 |
|---|---|
| 0x00 | 500 ppm |
| 0x01 | 250 ppm |
| 0x02 | 150 ppm |
| 0x03 | 100 ppm |
| 0x04 | 75 ppm |
| 0x05 | 50 ppm |
| 0x06 | 30 ppm |
| 0x07 | 20 ppm |
500 ppm 是最坏情况("我不保证比这更好"),20 ppm 是最佳。大多数用 32.768 kHz 晶振的设备落在 20~100 ppm 之间,按实际精度向上取最接近的等级 来声明。
CONNECT_IND 的 LLData 末尾有一个字节,同时装了跳频增量和 Master SCA:
主机必须把"自己能达到的最差 精度"填进这个字段——宁大勿小。一旦虚报精度(填了过小的等级),从机据此开的接收窗口就会不够宽,结果在时钟漂移下偶发漏包,严重时触发监督超时(supervision timeout)断连。
从机在每个连接事件,需要在预期锚点两侧各保留一段"宽容窗口"才能可靠收到主机的包:
接收窗口 = [ anchorPoint − Window_Widening , anchorPoint + Window_Widening ]
窗口宽度按 Core Spec Vol 6 Part B §4.5.7 计算(原理性表达):
Window_Widening ≈ ( connInterval × (masterSCA_ppm + slaveSCA_ppm) / 1,000,000 ) + 2 µs
其中:
关键点:窗口是相对"上一次成功同步的锚点"累积的。连接间隔越大,两次事件之间的漂移累积越多,窗口越宽;而从机一旦在某个连接事件成功收到包,就以该事件为新的参考锚点,窗口随之"重新归零"。
举例:connInterval = 1 s(1,000,000 µs),主机 500 ppm,从机 500 ppm:
Window_Widening = 1,000,000 × (500 + 500) / 1,000,000 + 2 µs
= 1000 µs + 2 µs
≈ 1.002 ms
即从机每次都要提前约 1 ms 打开接收、并持续约 1 ms 多。看似很小,但在长连接、大间隔、双差精度下,这部分"空开窗"会显著推高从机功耗。
SCA 是 BLE 连接层"用低精度睡眠时钟定时、靠窗口扩展容错"的核心机制。它用 3 bit 声明精度等级(20~500 ppm),直接决定从机的 RX 窗口宽度与功耗。理解它,才能在晶振选型、连接参数调优、以及"明明连上了却偶发掉包"的排查中做出正确判断。