4、蓝牙5.0链路层(LL):数据包格式、状态机与链路层控制过程
蓝牙5.0的链路层(Link Layer, LL)是蓝牙低功耗(BLE)协议栈的核心引擎,它直接运行在物理层之上,负责控制设备的射频状态、定义空中数据包的封装格式、管理设备间的连接建立与维护。如果说物理层决定了信号如何发送,那么链路层则决定了“何时发、发什么、如何协调”。本章将深入剖析LL层的数据包格式、五大状态机模型以及关键的链路层控制过程,帮助开发者从底层理解BLE通信的运作机制。
4.1 链路层数据包格式:从比特到字节的精确封装
BLE 5.0的空中数据包格式是链路层最基础的知识点。无论是广播信道还是数据信道,数据包的封装结构都遵循统一的规范,但具体字段长度和用途有所差异。理解数据包格式是进行协议分析、抓包调试和低功耗优化的前提。
4.1.1 核心原理:数据包的五段式结构
BLE 5.0的物理层数据包(Packet)由五个字段组成:前导码(Preamble)、访问地址(Access Address)、协议数据单元(PDU)、循环冗余校验(CRC)以及可选的恒定音调扩展(Constant Tone Extension, CTE)。其中,CTE是5.0新增的用于测向(Direction Finding)的功能,普通数据传输不使用。
- 前导码(Preamble):用于接收端的频率同步和位同步。在1M PHY下为1字节(0xAA或0x55),在2M PHY下为2字节。编码PHY(125kbps/500kbps)下前导码为10个重复比特。
- 访问地址(Access Address):4字节。广播物理信道PDU使用固定的访问地址0x8E89BED6;数据物理信道PDU使用链路层在连接建立时生成的随机地址,用于区分不同的连接。
- PDU(Protocol Data Unit):包含报头(Header)和可变长度的有效载荷(Payload)。广播PDU最大37字节,数据PDU最大255字节(5.0支持的最大数据长度扩展)。
- CRC(Cyclic Redundancy Check):3字节(24位)。用于检测数据包在传输过程中是否发生错误。CRC的初始值(CRCInit)在连接建立时由双方协商确定。
4.1.2 判断方法:如何区分广播PDU与数据PDU
在抓包工具(如 Ellisys、Frontline、nRF Sniffer)中,快速区分广播包和数据包是基本功。判断依据主要有三点:
- 访问地址:广播包的访问地址固定为0x8E89BED6,数据包的访问地址是随机生成的。
- PDU Header中的LLID字段:广播PDU的LLID(Logical Link Identifier)为00或01,数据PDU的LLID为10(数据开始)或01(数据继续)。
- 信道类型:广播包在37/38/39三个广播信道上发送,数据包在0-36共37个数据信道上跳频传输。
4.1.3 案例拆解:一个典型的广播数据包解析
假设我们使用nRF Sniffer抓取到一个广播包,原始十六进制数据如下:
D6 BE 89 8E 00 36 02 01 06 03 03 09 18 05 09 42 4C 45 20 35 2E 30
解析过程如下:
| 字段 | 长度 | 值 | 说明 |
|---|---|---|---|
| Preamble | 1B | 0xAA | 接收端同步用 |
| Access Address | 4B | 0x8E89BED6 | 广播固定地址 |
| PDU Header | 2B | 0x0036 | PDU类型=ADV_IND,长度=0x36=54字节 |
| AdvA | 6B | 设备MAC地址 | 广播者地址 |
| AdvData | 可变 | 02 01 06 ... | 包含Flags、Service UUID等 |
| CRC | 3B | 0xXXXXXX | 校验值 |
注意:PDU Header中的长度字段只包含Payload的长度,不包含Header本身和CRC。此例中Payload长度为0x36=54字节,但广播PDU最大为37字节,因此该包实际是5.0的扩展广播包(ADV_EXT_IND),其Payload长度字段含义不同。
4.2 链路层状态机:五大状态的迁移逻辑
BLE链路层定义了五种基本状态:待机态(Standby)、广播态(Advertising)、扫描态(Scanning)、发起连接态(Initiating)和连接态(Connection)。设备在任何时刻只能处于一种状态,状态之间的迁移由内部事件或外部命令触发。
4.2.1 核心原理:状态定义与角色划分
- Standby:初始状态,设备不发送也不接收任何数据包。这是功耗最低的状态。
- Advertising:设备在37/38/39三个广播信道上周期性发送广播包。广播者(Advertiser)可以携带自身信息或扫描响应数据。
- Scanning:设备在广播信道上监听来自广播者的数据包。扫描者(Scanner)可以主动发送扫描请求(SCAN_REQ)获取更多信息。
- Initiating:设备在广播信道上监听特定设备的广播包,一旦收到匹配的广播包,立即发送连接请求(CONNECT_REQ),随后迁移到连接态。发起者(Initiator)只能针对一个目标设备。
- Connection:设备已建立连接,按照约定的连接间隔在数据信道上跳频通信。连接态中,发起者成为主设备(Master),广播者成为从设备(Slave)。
4.2.2 判断方法:状态迁移的触发条件
状态迁移不是随意的,必须满足特定条件。以下是常见的迁移路径:
| 当前状态 | 目标状态 | 触发条件 |
|---|---|---|
| Standby | Advertising | 上层调用LE_Set_Advertising_Enable命令 |
| Standby | Scanning | 上层调用LE_Set_Scan_Enable命令 |
| Standby | Initiating | 上层调用LE_Create_Connection命令 |
| Advertising | Standby | 广播超时或上层禁用广播 |
| Scanning | Standby | 扫描超时或上层禁用扫描 |
| Initiating | Connection | 收到匹配的广播包并成功发送CONNECT_REQ |
| Connection | Standby | 连接超时、收到LL_TERMINATE_IND或链路层错误 |
4.2.3 实战场景:从广播到连接的完整状态流转
以一个典型的BLE心率计连接过程为例:
- 心率计上电后进入Standby,随后调用广播使能命令进入Advertising状态,在37/38/39信道上交替发送ADV_IND包。
- 手机(作为Central)调用扫描使能命令进入Scanning状态,在三个广播信道上监听。收到ADV_IND后,手机可以发送SCAN_REQ获取设备名称等额外信息。
- 用户点击连接按钮,手机调用LE_Create_Connection进入Initiating状态,指定目标设备地址。
- 心率计在下一个广播事件发送ADV_IND时,手机收到后立即发送CONNECT_REQ,包含连接参数(连接间隔、从机延迟、监控超时等)。
- 双方进入Connection状态,手机作为Master,心率计作为Slave,按照约定的连接间隔在数据信道上通信。
4.3 链路层控制过程:连接管理与参数更新
链路层控制过程(LL Control Procedures)是连接建立后,主从设备之间进行参数协商、安全管理、连接维护的机制。这些过程通过发送LL Control PDU(LLID=11)来实现,每个控制过程都有明确的请求-响应或请求-确认流程。
4.3.1 核心原理:LL控制PDU的分类与功能
BLE 5.0定义了多种LL控制PDU,常用的包括:
- LL_CONNECTION_UPDATE_IND:更新连接参数(连接间隔、从机延迟、监控超时)。由Master发起,Slave必须接受。
- LL_CHANNEL_MAP_IND:更新数据信道映射表。由Master发起,用于避开干扰严重的信道。
- LL_TERMINATE_IND:终止连接。任何一方都可以发起,需携带错误码。
- LL_ENC_REQ/RSP:启动链路层加密。Master发送请求,Slave响应。
- LL_PAUSE_ENC_REQ/RSP:暂停加密(用于重新协商密钥)。
- LL_PHY_REQ/RSP(5.0新增):切换物理层PHY模式(1M/2M/编码)。
- LL_PERIPHERAL_FEATURES_REQ:从设备请求更新连接参数(5.0新增,允许Slave主动请求参数更新)。
4.3.2 操作流程:连接参数更新的完整交互
以Master发起连接参数更新为例,流程如下:
- Master发送LL_CONNECTION_UPDATE_IND,包含新的连接间隔、从机延迟、监控超时和瞬时值(Instant)。
- Slave收到后,在指定的Instant(即第N个连接事件)应用新参数。
- 双方从Instant开始使用新的连接参数进行通信。
注意:LL_CONNECTION_UPDATE_IND不需要Slave发送确认包,但Master必须确保Slave收到该包(通过CRC校验和重传机制)。如果Slave在Instant之前未收到该包,则参数更新失败,双方继续使用旧参数。
4.3.3 案例拆解:从设备主动请求参数更新(5.0新特性)
背景:某BLE外设(如智能手环)在连接后,由于数据量突然增大(如开启实时心率监测),需要更短的连接间隔以提高吞吐量。但传统机制中,只有Master能发起参数更新,Slave只能通过L2CAP层的Connection Parameter Update Request间接请求,且Master可能忽略。
5.0解决方案:Slave可以直接发送LL_PERIPHERAL_FEATURES_REQ,请求Master更新连接参数。具体步骤如下:
- Slave发送LL_PERIPHERAL_FEATURES_REQ,携带期望的连接间隔范围(如7.5ms-15ms)。
- Master收到后,根据自身策略决定是否接受。如果接受,Master发送LL_CONNECTION_UPDATE_IND,指定新的参数。
- Slave在Instant后应用新参数。
结果对比:使用该机制后,手环的数据传输间隔从30ms缩短到10ms,实时心率数据的丢包率从5%降低到0.5%,用户体验显著提升。
风险与代价:频繁的参数更新会增加空中开销和功耗。建议仅在必要时发起更新,且更新间隔应大于1秒。
4.4 常见误区与总结复盘
链路层是BLE协议栈中最容易出错的部分,以下是开发者常遇到的几个问题:
| 误区 | 正确理解 | 后果 |
|---|---|---|
| 认为广播包可以无限大 | 传统广播PDU最大37字节,扩展广播才支持更大 | 数据被截断或无法广播 |
| 混淆Initiating和Scanning | Initiating只监听特定设备,Scanning监听所有设备 | 无法建立连接 |
| 忽略Instant参数 | 参数更新必须在指定连接事件生效 | 参数不一致导致通信失败 |
| 认为Slave不能请求参数更新 | 5.0支持LL_PERIPHERAL_FEATURES_REQ | 无法优化连接性能 |
| 忽视CRCInit的作用 | CRCInit在连接建立时协商,用于校验 | 抓包工具无法正确解析数据 |