从零实现BLE协议栈(4-1)第一次约会:首个连接事件与锚点
第一次约会:首个连接事件与锚点诞生
前提知识
阅读本文需要具备以下基础:
- 理解
CONNECT_IND的 22 字节字段(第 3-1 篇有详细讲解):本文只聚焦其中与第一次连接事件直接相关的WinOffset、WinSize和Interval。如果还不知道这些字段出现在什么位置,先回去看第 3-1 篇。 - 理解 BLE 中的 T_IFS 和硬件计时(第 2-2、2-3 篇有详细讲解):第一次连接事件虽然不再是 150 µs 级别的短延迟,但它同样属于"定时必须正确,否则立即失联"的问题。
- 理解硬件定时器/捕获寄存器的基本用途:本文会用
T_anchor表示CONNECT_IND接收完成那一刻的精确时间戳,这个时间戳必须靠硬件捕获得到,而不是靠软件事后估算。
不需要提前了解 Anchor 调度器和 Window Widening 的完整算法。本文只讨论第一次连接事件怎么算、为什么不能算错。
一、接收窗口(transmit window)

根据3-1篇的分析,Master 发完 CONNECT_IND之后只是约定了一套基于私有规则的通信协议。真正的首次通信发生在稍后的 接收窗口 (Transmit Window) 中。这个窗口的作用有:
- 确定首次连接事件的时刻:Master 在这个窗口内的任意时刻发出第一个包,主机发出第一个包的那一刻,被定义为第一个锚点。只有这个锚点正确捕获了,后续的连接事件才有正确的时间基准,主从双方才算真正进入连接状态。
- 时间缓冲:主从双方都需要时间把自己从广播态切换到连接态。(更换 Access Address、CRCInit、Hop 算法、数据信道配置等)。如果没有这个窗口,可能造成时间上的冲突和资源上的浪费。
二、接收窗口时刻的计算
接收窗口时刻由三个重要时刻定义:

2.1 T_anchor
T_anchor 为 CONNECT_IND 最后一个 bit 在空口传输完毕的时刻。
这不是一个大概时刻,而是必须由硬件计时器在收发完成瞬间精确捕获的时间戳。从“包收发完毕”到“软件读到寄存器”之间,经过了中断响应、Header 检查、CRC 校验等一系列耗时指令。这段“软件延迟”是非恒定的抖动项。如果基准点 \(T_{anchor}\) 错了,后续所有周期的连接事件都会产生累积偏移,导致频繁掉线。
nRF52 芯片的底层实现:
nRF 系列芯片的底层驱动实现中,T_anchor是通过纯硬件联动(PPI)获取的,完全没有 CPU 参与的延迟:
对于 Slave (扫描方/接收者): RADIO 成功接收完数据包最后一个 bit 时,会硬件触发NRF_RADIO->EVENTS_END事件。驱动中会配置一条 PPI Link(如NRF_PPI->CH[x].EEP = (uint32_t)&NRF_RADIO->EVENTS_END和TEP = (uint32_t)&NRF_TIMER0->TASKS_CAPTURE[1]),将 END 事件直接路由去锁存当前的高频 Timer 计数器(CC 寄存器)。软件进入中断时,直接读取CC寄存器即可获得丝毫不差的接收完毕时刻。
对于 Master (广播方/发送者): 发送端也是同样的机制。Master 的 RADIO 在将CONNECT_IND最后一个 bit 送出天线时,同样会触发EVENTS_END,同样通过 PPI 捕获到 Timer 的CC寄存器中。
这样,Master 和 Slave 双方就基于同一次空口物理事件,各自在本地精确记录了绝对对齐的日历原点(相差仅为电磁波飞行时间,可忽略不计)。
2.2 T_start 与 T_end
首次接收窗口是一个基于 \(T_{anchor}\) 推算的闭区间:
T_start:窗口开始时刻,公式为:\(T_{start} = T_{anchor} + 1.25\,\text{ms} + WinOffset \times 1.25\,\text{ms}\)T_end:窗口结束时刻,公式为:\(T_{end} = T_{start} + WinSize \times 1.25\,\text{ms}\)
Slave 的任务是保证自己在整个 [T_start, T_end] 区间内处于接收就绪状态,从而覆盖 Master 的第一包。
前面那个固定的 1.25 ms是规范规定的强制偏移,必须无条件加上。因为 CONNECT_IND 结束之后,双方都需要一段最小准备时间,把自己从广播态切换到连接态。如果没有这 1.25 ms 的硬性缓冲,某些实现会把 WinOffset = 0 理解成“现在立刻开始首次连接事件”,从而让 Slave 来不及完成物理层重配置。
三、连接事件(Connection Event)与第一个锚点的诞生

3.1 连接事件的定义
在 Transmit Window 内,双方将完成第一次“乒乓交互”,也就是一次连接事件(Connection Event):
- 主设备发送:在锚点开始时,主设备先发送一个数据包(如果没有应用数据,则发送一个空包 Empty PDU)。
- 从设备回复:从设备在收到包的 150μs (T_IFS) 后,必须回复一个包(哪怕是空包)。
连接事件的精确定义如下(摘自 Bluetooth Core Specification v5.3, Vol 6, Part B, Section 4.5.1):
- 连接事件内只能发送数据包(Data Channel PDU)。
- 主从双方根据跳频算法确定每个连接事件使用哪个数据信道。
- 连接事件包含至少一个由主设备发送的数据包。在连接事件期间,主从双方交替发送和接收数据包。当双方继续发送数据包时,连接事件被认为是开放的。
- 无论 CRC 是否匹配,从设备在收到主设备的数据包后都必须发送一个数据包,除非连续多次 CRC 错误。无论 CRC 是否匹配,主设备在收到从设备的数据包后可以发送一个数据包。即使 CRC 错误,Header 中的 Length 字段也被认为是正确的。
- 如果主设备没有收到从设备的数据包,主设备应关闭连接事件。
3.2 锚点的定义
在第一个传输窗口内,Master 在这个窗口内的任意时刻(通常是起始点)发出第一个包,主机发出第一个包的那一刻,被定义为第一个锚点。
锚点是连接事件的时间基准。后续所有连接事件的时刻都是基于这个锚点按照 Interval 递推出来的。计算公式如下:
四、第一次连接事件的时序分析
从广播到首次连接事件的完整时序如下图:
只有当第一个 Connection Event 成功完成(一发一收),这条连接才在链路层被标记为 Established (已建立)。此时,Master 发包的起始时刻正式晋升为后续所有周期的 Anchor Point (锚点)。
本文版权归作者:ixbwer所有,转载请注明原文链接:https://www.cnblogs.com/ixbwer/p/19796650,否则保留追究法律责任的权利。

浙公网安备 33010602011771号