1. BLE 协议栈概览:从物理层到应用层
做 BLE 开发这些年,我见过太多人一上来就调代码,遇到问题就抓瞎。说白了,你不懂协议栈的分层结构,出了问题根本不知道从哪查起。这一章,咱们先把 BLE 协议栈的骨架搭起来。
BLE 协议栈分三层:Controller、Host、Application。每一层各司其职,层与层之间通过标准接口通信。我习惯把这套结构想象成一个快递系统——物理层是卡车和公路,链路层是分拣中心,HCI 是快递单,而应用层就是收件人。
核心要点:BLE 协议栈的分层设计,让硬件厂商和软件开发者可以独立工作。你写应用代码时,根本不用管底层是 Nordic 还是 TI 的芯片。
1.1 Controller 层——硬件的管家
Controller 层是离硬件最近的一层。它包含 物理层(PHY) 和 链路层(LL)。我刚开始做 BLE 时,总觉得这层跟我没关系——芯片厂商都封装好了嘛。直到有一次产品在强干扰环境下频繁断连,我才意识到,不懂 PHY 和 LL,你连问题出在哪都说不清。
物理层(PHY)基础
PHY 负责最原始的比特流收发。BLE 工作在 2.4GHz ISM 频段,一共 40 个信道,每个信道 2MHz 带宽。你想想看,Wi-Fi 和 ZigBee 也挤在这个频段,干扰是家常便饭。
| 参数 | BLE 1M PHY | BLE 2M PHY | BLE Long Range (Coded PHY) |
|---|---|---|---|
| 数据速率 | 1 Mbps | 2 Mbps | 125 / 500 kbps |
| 调制方式 | GFSK | GFSK | GFSK + 编码 |
| 接收灵敏度 | -96 dBm(典型) | -93 dBm(典型) | -103 dBm(典型) |
| 典型距离 | ~100 米 | ~80 米 | ~400 米 |
我的经验:做室内定位项目时,我踩过 2M PHY 的坑。2M 速率快,但灵敏度低 3 dB。在穿墙场景下,1M PHY 反而更稳。别盲目追求高速率,先看你的应用场景。
链路层(LL)状态机
链路层是 BLE 的「大脑」。它管理着设备之间的连接状态。LL 状态机有 5 个核心状态:
- Standby——空闲状态,啥也不干
- Advertising——广播状态,设备在发「我在这儿」的信号
- Scanning——扫描状态,设备在听周围有没有广播
- Initiating——发起连接状态,主动去连一个广播者
- Connection——连接状态,双方建立稳定链路
我记得有一次调试一个门锁产品,设备老是连不上。抓空包一看,链路层一直在 Advertising 和 Standby 之间来回跳,根本没进入 Connection。后来发现是广播间隔设得太短,芯片来不及处理。嗯,这里要注意——广播间隔不能小于 20 ms,这是协议规定的硬限制。
避坑指南:我曾经在量产前发现一批设备连接成功率只有 70%。查了三天,最后定位到链路层状态机在 Initiating 阶段超时。原因是扫描窗口和扫描间隔配得不匹配,导致错过广播包。记住:扫描窗口 ≤ 扫描间隔,且扫描窗口至少要覆盖一个广播事件。
1.2 Host 层——协议的翻译官
Host 层跑在 CPU 上,处理高层协议。它包括 L2CAP、ATT、GATT、GAP 和 SMP。说白了,Controller 负责「怎么发」,Host 负责「发什么」和「发给谁」。
- L2CAP:把上层数据拆成小包,加上协议头,交给 Controller
- ATT:定义了客户端-服务器模型,用来读写属性
- GATT:基于 ATT,定义了 Service 和 Characteristic 的组织方式
- GAP:控制设备的角色(广播者、扫描者、外围设备、中心设备)
- SMP:负责配对和加密,保证通信安全
我习惯把 GATT 比作一个文件系统。Service 是文件夹,Characteristic 是文件,而 Descriptor 是文件的属性说明。你读写一个温度传感器的数据,本质上就是在 GATT 层操作一个 Characteristic。
1.3 HCI 接口——Host 和 Controller 的桥梁
HCI(Host Controller Interface)是 Host 和 Controller 之间的通信协议。它定义了命令、事件和数据包的格式。为什么要有 HCI?因为 Host 和 Controller 可能跑在不同的芯片上——比如手机 SoC 跑 Host,蓝牙芯片跑 Controller。
HCI 的传输方式有几种:
| 传输方式 | 典型场景 | 特点 |
|---|---|---|
| UART | 模块 + MCU | 简单,速率受限 |
| USB | PC 蓝牙适配器 | 高速,即插即用 |
| SDIO | Wi-Fi/BT 组合芯片 | 高吞吐,功耗高 |
| SPI | 嵌入式系统 | 低延迟,适合实时控制 |
关键点:HCI 是调试的「黄金窗口」。我每次遇到连接问题,第一件事就是打开 HCI 日志。通过 HCI 事件,你能看到链路层状态机的每一次跳转、每一个错误码。这比用示波器抓波形快多了。
举个例子,当你调用 ble_gap_connect() 时,Host 会通过 HCI 发送 LE Create Connection 命令给 Controller。Controller 收到后进入 Initiating 状态,开始扫描目标广播。如果连接成功,Controller 返回 LE Connection Complete 事件。如果超时,返回 LE Connection Complete 事件但状态码是 0x3E(连接超时)。
// HCI 命令示例:LE Create Connection
// 这是 Host 发给 Controller 的命令
HCI_CMD:
Opcode: 0x200D (LE Create Connection)
Parameters:
Scan Interval: 0x0060 (60 ms)
Scan Window: 0x0030 (30 ms)
Initiator Filter Policy: 0x00 (不使用白名单)
Peer Address: 0x112233445566
Conn Interval Min: 0x0006 (7.5 ms)
Conn Interval Max: 0x00C8 (200 ms)
Conn Latency: 0x0000
Supervision Timeout: 0x0C80 (3200 ms)
你看,一个简单的连接命令,背后有这么多参数。我见过有人把 Supervision Timeout 设成 100 ms,结果设备动不动就断连。为什么?因为连接间隔是 50 ms,丢一个包就超时了。Supervision Timeout 至少要大于连接间隔的 6 倍,这是协议规定的。
1.4 小结:分层设计的意义
BLE 协议栈的分层设计,说白了就是「各管各的」。芯片厂商管 Controller,操作系统管 Host,你管 Application。出了问题,先定位是哪一层——是射频干扰(PHY),还是连接参数不对(LL),还是应用逻辑有 bug(Application)。
我个人习惯在项目初期就搭好 HCI 日志抓取环境。这样后续调试时,能快速定位问题。别等到产品量产了才去补课,那时候改一个 PHY 参数都得重新流片,成本太高了。
实用建议:刚开始学 BLE 协议栈时,别急着看代码。先拿一个抓包工具(比如 Ellisys 或 Nordic 的 nRF Sniffer),看看真实的空中包长什么样。你会惊讶地发现,书上讲的状态机、HCI 命令,在抓包工具里一目了然。