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 数据流全景:从应用到天线

理解了各层职责,我们来看看数据是怎么从你的应用代码一路走到天线的。我画了一张图,帮你把整个流程串起来。

BLE协议栈数据流全景 Host 层 GAP GATT / ATT SM L2CAP HCI(Host Controller Interface) Controller 层 LL(链路层) PHY(物理层) 射频 / 天线 数据流向:应用 → GAP/GATT → L2CAP → HCI → LL → PHY → 天线 反向同理:天线 → PHY → LL → HCI → L2CAP → GATT/GAP → 应用

从这张图你可以看到:数据从应用层出发,经过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是它们之间的桥梁。每一层都有自己的参数和约束,连接参数的优化必须考虑所有层的协同。