1. 蓝牙协议栈概览:从Controller到Host
做蓝牙开发这么多年,我见过不少工程师一上来就调参数、改连接间隔,结果问题越改越多。为什么?说白了,很多人没搞懂协议栈到底长什么样。你连数据从哪来到哪去都不清楚,怎么优化?
这一章,我们就从最底层开始,把BLE协议栈的骨架搭起来。我会结合自己踩过的坑,帮你把每一层的职责和流转路径理清楚。
1.1 协议栈的分层哲学
BLE协议栈分两层:Controller 和 Host。中间有个接口叫HCI(Host Controller Interface)。
为什么要分层?我个人的理解是——分工明确。Controller管射频、管时序,Host管应用、管逻辑。你想想看,如果所有代码揉在一起,换个蓝牙芯片就得重写整个应用,那得多痛苦?
| 层级 | 核心职责 | 典型组件 |
|---|---|---|
| Controller | 射频收发、链路控制、物理信道管理 | PHY、LL、HCI(底层) |
| Host | 协议封装、安全管理、应用数据交互 | L2CAP、ATT、GATT、SM、GAP |
关键认知:Controller是硬实时部分,Host是软实时部分。优化连接参数时,你动的主要是Host层的配置,但瓶颈往往出在Controller层的时序上。
1.2 Controller层:硬实时的心脏
Controller层是离天线最近的部分。我刚开始做蓝牙项目时,总觉得这层是芯片厂商的事,跟我没关系。直到有一次产品在强干扰环境下频繁断连,我才意识到——Controller层的参数直接决定了连接的物理稳定性。
1.2.1 PHY(物理层)
PHY负责最原始的比特流收发。BLE在1Mbps模式下用GFSK调制,2Mbps模式用更高效的调制方式。嗯,这里要注意:PHY层决定了你能跑多快,也决定了接收灵敏度。
1.2.2 LL(链路层)
LL是Controller层的核心。它管理着连接状态机、数据重传、加密起始、信道跳频。我在项目中遇到过一个问题:设备连接后每隔几秒就丢包,查了半天发现是LL层的连接事件间隔设置得太短,导致重传窗口不够。
- 连接状态机:Standby、Advertising、Scanning、Initiating、Connection
- 数据重传:每个数据包最多重传4次(LL层硬限制)
- 信道跳频:37个数据信道,按固定算法跳转
个人经验:调试LL层问题时,我习惯先看连接事件的实际间隔和重传次数。很多所谓的“连接不稳定”,其实是LL层的重传耗尽导致的断连。
1.3 HCI:承上启下的桥梁
HCI是Controller和Host之间的通信接口。它可以是UART、USB、SDIO等物理总线。HCI定义了命令、事件、数据三种类型的包。
我曾经在一个项目里,Host和Controller之间用UART通信,波特率设成了115200。结果数据量大时,HCI层频繁丢包。后来我把波特率提到921600,问题就解决了。你想想看,HCI本身也会成为瓶颈。
// HCI命令包格式示例
// OpCode: 2字节 (OGF + OCF)
// Parameter Total Length: 1字节
// Parameters: 可变长度
typedef struct {
uint16_t opcode; // 操作码
uint8_t param_len; // 参数长度
uint8_t params[255]; // 参数数据
} hci_cmd_pkt_t;
1.4 Host层:协议与应用的舞台
Host层跑在微控制器的应用处理器上。它不直接参与射频时序,但决定了你能做什么样的应用。
1.4.1 L2CAP(逻辑链路控制与适配协议)
L2CAP把LL层的数据包封装成更灵活的协议数据单元。它支持分段重组、协议多路复用、流控。说白了,它让上层不用关心底层包的大小限制。
1.4.2 ATT(属性协议)与GATT(通用属性协议)
ATT定义了客户端-服务器模型,GATT则在这个模型上定义了服务、特征、描述符的层次结构。我建议初学者先理解GATT的树形结构:一个Profile包含多个Service,一个Service包含多个Characteristic,一个Characteristic包含Value和多个Descriptor。
1.4.3 SM(安全管理器)
SM负责配对、绑定、加密。它定义了三种配对方式:Just Works、Passkey Entry、Out of Band。我个人习惯在开发阶段先用Just Works,等产品定型后再换成更安全的方式。
1.4.4 GAP(通用访问协议)
GAP定义了设备的角色和广播行为。它决定了你的设备是广播者、扫描者、发起者还是外设/中心。连接参数的协商,本质上就是GAP层的交互。
避坑指南:我曾经在GAP层把广播间隔设成了20ms,结果设备功耗飙升。后来才发现,广播间隔太短会导致射频连续工作,电流根本降不下来。记住:广播间隔和连接间隔是两个不同的参数,别搞混了。
1.5 数据流全景:从应用到天线
理解了各层职责,我们来看看数据是怎么从你的应用代码一路走到天线的。我画了一张图,帮你把整个流程串起来。
从这张图你可以看到:数据从应用层出发,经过GAP/GATT的封装,进入L2CAP的分段重组,然后通过HCI跨越到Controller侧。在Controller里,LL层负责连接管理和重传,PHY层负责调制解调,最后通过天线发射出去。
反过来,接收路径也一样。我建议你在调试时,先确认数据到了哪一层。比如,如果HCI层能收到事件但应用层没反应,那问题大概率出在Host层的协议解析上。
1.6 各层对连接参数的影响
连接参数不是某一个层的专属配置。它贯穿了整个协议栈。我整理了一张表,帮你快速定位每层的影响范围。
| 参数 | 所属层 | 典型范围 | 我的建议 |
|---|---|---|---|
| 连接间隔 | LL | 7.5ms ~ 4s | 不要低于15ms,否则重传窗口不够 |
| 延迟 | LL | 0 ~ 499 | 低功耗场景可以设到10以上 |
| 监督超时 | LL | 100ms ~ 32s | 建议是连接间隔的3倍以上 |
| MTU大小 | L2CAP | 23 ~ 247 | 能大就大,减少分包开销 |
| ATT MTU | ATT | 23 ~ 247 | 与L2CAP MTU保持一致 |
个人习惯:我每次拿到一个新平台,第一件事就是打印出LL层的连接事件日志。看看实际的连接间隔、重传次数、丢包率。这些数据比任何理论分析都管用。
1.7 本章小结
BLE协议栈的分层设计,让Controller专注于硬实时,Host专注于应用逻辑。HCI是它们之间的桥梁。每一层都有自己的参数和约束,连接参数的优化必须考虑所有层的协同。