第3章 BLE协议栈详解:从物理层到应用层
BLE协议栈,说白了就是一套分层的通信规则。我刚开始接触时也觉得层数太多,有点懵。但后来发现,每一层各司其职,理解起来并不复杂。
咱们先看整体架构,再逐层拆解。
核心要点:BLE协议栈分为三层——控制器(Controller)、主机(Host)和应用层(Application)。控制器负责射频和链路控制,主机负责逻辑链路和属性协议,应用层直接面向开发者。
3.1 物理层(PHY)—— 信号的起点
PHY层是协议栈的最底层。它负责把数据变成无线电波发出去,也负责把收到的无线电波还原成数据。
BLE工作在2.4GHz ISM频段,和Wi-Fi、ZigBee共用这个频段。BLE定义了40个信道,每个信道间隔2MHz。其中3个是广播信道(37/38/39),37个是数据信道。
| 参数 | BLE 1M PHY | BLE 2M PHY | BLE Coded PHY (S=2) | BLE Coded PHY (S=8) |
|---|---|---|---|---|
| 数据速率 | 1 Mbps | 2 Mbps | 500 kbps | 125 kbps |
| 调制方式 | GFSK | GFSK | GFSK + 编码 | GFSK + 编码 |
| 通信距离 | ~100m | ~50m | ~200m | ~400m |
| 典型场景 | 通用 | 高速数据传输 | 中等距离 | 远距离 |
我的经验:做智能门锁项目时,我一开始用了1M PHY,穿墙后信号衰减严重。后来换成Coded PHY (S=8),距离从30米提升到100米以上。代价是速率降到125kbps,但门锁那点数据量完全够用。
3.2 链路层(LL)—— 连接的指挥官
链路层是BLE协议栈里最核心的一层。它管理着设备的状态机,控制着广播、扫描、连接建立和数据传输。
LL层定义了5种设备状态:
- Standby:空闲状态,不发送也不接收
- Advertising:广播状态,向外发送广播包
- Scanning:扫描状态,监听广播包
- Initiating:发起连接状态,主动建立连接
- Connection:连接状态,已建立连接
设备可以在这些状态之间切换。比如一个传感器平时在广播状态,手机扫描到后发起连接,传感器进入连接状态。
注意:同一时刻,设备只能处于一种状态。不能一边广播一边扫描,除非你用了多模芯片。我曾经踩过这个坑——想用同一个芯片同时做广播和扫描,结果发现LL层不支持。
3.3 L2CAP层 —— 数据的分包与重组
L2CAP(逻辑链路控制与适配协议)位于主机层底部。它负责把上层的大数据包拆成LL层能处理的小包,反过来也负责把LL层的小包拼回大包。
BLE的L2CAP和经典蓝牙不太一样。经典蓝牙的L2CAP支持多种通道,BLE的L2CAP只支持面向连接的通道(CoC)和面向无连接的通道(CL)。
默认的MTU(最大传输单元)是23字节。但可以通过MTU协商增大到512字节甚至更大。
关键点:MTU协商是提升数据传输效率的重要手段。默认23字节意味着每次最多传20字节有效数据(3字节L2CAP头)。协商到247字节后,一次能传244字节,效率提升10倍以上。
3.4 ATT层 —— 属性的读写
ATT(属性协议)定义了设备之间如何读写数据。它把数据抽象成"属性"(Attribute),每个属性都有:
- 句柄(Handle):属性的唯一ID,16位
- 类型(UUID):标识属性是什么数据
- 权限(Permissions):可读、可写、可通知等
- 值(Value):实际数据
ATT定义了6种PDU(协议数据单元):
| 操作 | 方向 | 说明 |
|---|---|---|
| Read Request / Response | Client → Server → Client | 客户端读取属性值 |
| Write Request / Response | Client → Server → Client | 客户端写入属性值,需要确认 |
| Write Command | Client → Server | 客户端写入属性值,不需要确认 |
| Notify | Server → Client | 服务器主动通知,不需要确认 |
| Indicate / Confirm | Server → Client → Server | 服务器主动指示,需要确认 |
我的建议:Notify和Indicate的区别一定要搞清楚。Notify不确认,速度快但不可靠;Indicate有确认,可靠但慢。做传感器数据上报时,我一般用Notify,丢一帧无所谓。做控制指令时,必须用Indicate,确保对方收到了。
3.5 GATT层 —— 数据的组织方式
GATT(通用属性协议)在ATT之上,定义了数据的组织方式。它把属性组织成Service(服务)和Characteristic(特征)的层次结构。
一个典型的GATT结构是这样的:
- Profile:最顶层,定义了一组Service
- Service:一组相关的Characteristic,比如电池服务、设备信息服务
- Characteristic:一个具体的数据点,包含值、属性、描述符
- Descriptor:对Characteristic的补充说明,比如配置通知开关
举个例子,电池服务(Battery Service)包含一个电池电量特征(Battery Level),这个特征的值是0-100的整数,表示剩余电量百分比。
理解GATT的关键:GATT定义了"数据长什么样",ATT定义了"怎么读写这些数据"。两者配合,构成了BLE应用层通信的基础。
3.6 BLE广播与扫描 —— 发现彼此
广播和扫描是BLE设备互相发现的方式。广播者(Advertiser)在3个广播信道上轮流发送广播包,扫描者(Scanner)在这些信道上监听。
广播包的类型:
- 可连接广播(Connectable):其他设备可以发起连接
- 可扫描广播(Scannable):其他设备可以请求扫描响应
- 不可连接广播(Non-connectable):只广播,不接受连接
- 定向广播(Directed):指定目标设备
广播间隔通常设置在20ms到10.24s之间。间隔越短,被发现越快,但功耗越高。
避坑指南:我曾经把广播间隔设成20ms,结果功耗飙升,纽扣电池两天就没电了。后来改成100ms,功耗降了80%,发现延迟也才多了一两百毫秒。广播间隔不是越小越好,要平衡功耗和实时性。
3.7 BLE连接建立流程
连接建立是BLE通信的关键步骤。流程如下:
- 广播阶段:外设(Peripheral)在广播信道上发送广播包
- 扫描阶段:中央设备(Central)扫描到广播包
- 发起连接:中央设备发送连接请求(CONNECT_REQ)
- 连接确认:双方进入连接状态,开始按连接参数通信
连接请求中包含连接参数:
| 参数 | 范围 | 说明 |
|---|---|---|
| 连接间隔 | 7.5ms ~ 4s | 两个连接事件之间的时间 |
| 延迟 | 0 ~ 499 | 设备可以跳过的连接事件数 |
| 超时时间 | 100ms ~ 32s | 超过此时间未收到包则断开连接 |
我的经验:连接间隔设得越小,延迟越低,但功耗越高。做手环项目时,我设了30ms连接间隔,延迟低但电池撑不到一天。后来改成100ms,配合延迟参数,续航到了3天。用户感知不到那几十毫秒的差别。
3.8 BLE数据通道
连接建立后,双方在数据信道上通信。数据通道使用跳频机制,每发一个包就跳到下一个信道。BLE有37个数据信道,跳频算法由LL层管理。
数据通道的通信模式:
- 连接事件(Connection Event):每个连接间隔发生一次,双方交换数据
- 主从角色:中央设备是Master,外设是Slave。Master决定何时发送
- 数据包格式:前导码(1字节)+ 访问地址(4字节)+ PDU + CRC(3字节)
数据通道的PDU类型:
- LL Data PDU:承载L2CAP层的数据
- LL Control PDU:链路层控制命令,如更新连接参数、加密等
总结:BLE数据通道的核心是"连接事件+跳频"。每个连接事件里,Master先发,Slave在150μs后回复。这种机制保证了低功耗和抗干扰能力。