2. BLE协议栈架构:从物理层到安全管理
BLE协议栈,说白了就是一套分层的软件协议。每一层各司其职,上层依赖下层提供的服务。我个人习惯把协议栈想象成一栋楼——物理层是地基,应用层是顶层的住户。今天我们就一层层拆开来看。
核心要点:BLE协议栈分为三大部分——Controller(控制器)、Host(主机)和Application(应用)。Controller负责射频和底层链路,Host负责逻辑链路和属性交互,Application则是你写的业务代码。
2.1 物理层(PHY)—— 最底层的射频硬功夫
物理层负责最原始的比特流收发。BLE工作在2.4GHz ISM频段,共40个信道,每个信道2MHz带宽。为什么是40个?我记得当年看规范时也纳闷——经典蓝牙是79个信道,BLE砍了一半还多。后来做项目才明白,信道少意味着扫描和广播更快,功耗更低。
| 参数 | BLE 1M PHY | BLE 2M PHY | BLE Coded PHY |
|---|---|---|---|
| 数据速率 | 1 Mbps | 2 Mbps | 125 / 500 kbps |
| 调制方式 | GFSK | GFSK | GFSK |
| 编码方式 | 无 | 无 | FEC + 模式映射 |
| 典型距离 | ~10m | ~5-8m | ~100m+ |
| 应用场景 | 通用 | 高速传输 | 远距离/穿墙 |
选型建议:如果做室内定位或音频传输,我建议优先考虑2M PHY。如果是智能家居网关,Coded PHY的125kbps模式能让你在穿两堵墙后依然保持连接。
2.2 链路层(LL)—— 连接的调度中心
链路层是BLE协议栈里最忙的模块。它负责设备发现、广播、连接建立、数据重传和加密。你想想看,两个设备怎么知道对方存在?怎么约定什么时候收发数据?这些都是LL干的活。
LL定义了五种状态机:待机、广播、扫描、发起和连接。我在项目中遇到过一个问题——设备广播间隔设得太短,导致功耗飙升。后来把广播间隔从20ms调到100ms,电池续航直接翻倍。
避坑指南:我曾经在量产前发现广播包里忘了加Flags字段,导致某些手机扫描不到设备。记住——广播包的结构必须严格遵循规范,少一个AD Type都可能出兼容性问题。
2.3 主机控制接口(HCI)—— Host和Controller的桥梁
HCI是Host和Controller之间的通信接口。它定义了命令、事件和数据的传输格式。HCI可以通过UART、USB、SPI或SDIO实现。我个人习惯在调试阶段用UART HCI,抓log方便。
HCI命令包、事件包和数据包的格式是固定的。举个例子,发送一个连接请求,Host会构造一个HCI命令包,通过传输层发给Controller。Controller执行完后,返回一个HCI事件包告诉Host结果。
// HCI命令包格式示例
// 操作码(2字节) + 参数总长度(1字节) + 参数(可变)
// 例如:LE Create Connection 命令
// 操作码: 0x200D (LE Controller命令组)
// 参数: 扫描间隔、窗口、初始化地址等
2.4 逻辑链路控制与适配协议(L2CAP)—— 数据的分包与复用
L2CAP位于Host层,负责将上层数据封装成LL能处理的PDU。它支持两种模式:基本L2CAP模式和面向连接的通道(CoC)模式。基本模式用于ATT和SM,CoC模式用于高速数据传输。
L2CAP的MTU(最大传输单元)默认是23字节。为什么这么小?因为LL的PDU最大就是27字节,减去4字节的L2CAP头,剩下23字节给上层。嗯,这里要注意——如果你要传大块数据,必须通过L2CAP的分包重组机制。
性能关键:L2CAP的MTU协商是连接建立后的第一步。我建议在连接后立即发起MTU请求,把MTU提升到247字节甚至更大。这样后续的数据传输效率会高很多。
2.5 属性协议(ATT)—— 数据的读写规则
ATT定义了设备之间如何读写属性。属性是一个带UUID、句柄和权限的数据单元。ATT有6种PDU类型:请求、响应、命令、通知、指示和确认。
举个例子,手机读取传感器的温度值:手机发送ATT Read Request,传感器返回ATT Read Response。如果传感器主动上报温度变化,就用ATT Notification。
我记得有一次调试,发现通知总是丢包。后来查了规范才知道——ATT Notification不需要确认,如果LL层丢包了,上层根本不知道。换成ATT Indication就好了,因为Indication要求对方回复确认。
2.6 通用属性配置文件(GATT)—— 数据的组织方式
GATT在ATT之上定义了一套数据组织框架。它引入了Service(服务)、Characteristic(特征)和Descriptor(描述符)的概念。一个设备可以包含多个Service,每个Service包含多个Characteristic,每个Characteristic可以有多个Descriptor。
| GATT元素 | 说明 | 示例 |
|---|---|---|
| Service | 一组相关功能的集合 | 电池服务 (0x180F) |
| Characteristic | 一个数据点,包含值和属性 | 电池电量 (0x2A19) |
| Descriptor | 对Characteristic的描述或配置 | 客户端特征配置 (0x2902) |
设计建议:定义GATT Service时,尽量复用标准Service UUID。如果必须用自定义UUID,128位UUID要保证唯一性。我习惯用在线UUID生成器生成,然后固化在代码里。
2.7 安全管理(SM)—— 连接的安全卫士
SM负责配对、绑定和加密。它定义了三种配对方式:Just Works、Passkey Entry和Out of Band (OOB)。Just Works最简单,但安全性最低。Passkey Entry需要用户输入6位数字,安全性中等。OOB通过NFC或二维码交换密钥,安全性最高。
配对过程分为三个阶段:第一阶段是配对特征交换,第二阶段是认证和密钥生成,第三阶段是密钥分发。我在做智能门锁项目时,选择了Passkey Entry + 绑定。用户第一次配对时输入门锁上显示的6位数字,之后自动重连,体验很好。
安全提醒:千万不要在生产环境中使用Just Works配对,除非你的产品不涉及任何隐私数据。我曾经见过一个智能灯泡用Just Works,结果被人在楼下轻松连上,控制了整栋楼的灯光。
2.8 各层之间的协作关系
说了这么多层,它们到底怎么配合?我画个流程你就明白了:
- 应用层调用GATT API,比如读取温度值。
- GATT找到对应的Characteristic,构造ATT Read Request。
- ATT将请求封装成ATT PDU,交给L2CAP。
- L2CAP加上L2CAP头,检查MTU,如果需要则分包。
- HCI将L2CAP数据封装成HCI数据包,通过传输层发给Controller。
- LL将数据封装成LL PDU,安排到对应的连接事件中发送。
- PHY将比特流调制成射频信号,通过天线发出去。
接收端反过来走一遍。你看,每一层只关心自己的事,这就是分层设计的好处。
总结:BLE协议栈的每一层都有明确的职责。PHY管射频,LL管连接,HCI管通信,L2CAP管数据封装,ATT管属性读写,GATT管数据组织,SM管安全。理解这些层的分工和协作,是设计BLE应用的基础。