4. BLE协议栈概览:从物理层到GATT服务
BLE协议栈,说白了就是一套分层的通信规则。每一层各司其职,又互相配合。我在做嵌入式开发的头几年,总觉得协议栈这东西太抽象,直到亲手调过几次空包、抓过几轮log,才真正理解它为什么这么设计。
今天我们就从下往上,把BLE协议栈的核心层捋一遍。你想想看,一个蓝牙设备从发出信号到应用层拿到数据,中间到底经历了什么?
BLE协议栈核心分层(自上而下):
- GATT/GAP — 应用层接口,定义服务和数据交互方式
- ATT — 属性协议,负责读写属性数据
- L2CAP — 逻辑链路控制与适配,封装/分段上层数据
- HCI — 主机-控制器接口,软硬件分界线
- Link Layer — 链路层,状态机、连接管理、重传
- Physical Layer — 物理层,调制解调、射频收发
4.1 Physical Layer — 物理层
物理层是协议栈的最底层,负责把数据变成无线电波发出去。BLE工作在2.4GHz ISM频段,一共40个信道,每个信道间隔2MHz。
我个人习惯把物理层的关键参数记成一张表,调试时随时翻看:
| 参数 | BLE 4.x/5.0 | 说明 |
|---|---|---|
| 频段 | 2.400 – 2.4835 GHz | ISM免授权频段 |
| 信道数 | 40(37个数据 + 3个广播) | 37、38、39为广播信道 |
| 调制方式 | GFSK | 高斯频移键控 |
| 数据速率 | 1 Mbps / 2 Mbps (LE 2M) | BLE 5.0支持2M |
| 发射功率 | 典型 0 dBm (1 mW) | 可配置,最大+10 dBm |
| 接收灵敏度 | -70 dBm ~ -97 dBm | 取决于速率和芯片 |
经验之谈: 我在一个穿戴设备项目中,遇到过天线匹配没调好,导致通信距离只有3米。后来发现是物理层的发射功率虽然设了0 dBm,但天线阻抗不匹配,实际辐射功率掉了近10 dB。所以别光看寄存器,天线匹配才是物理层的命门。
4.2 Link Layer — 链路层
链路层是BLE协议栈里最核心的一层。它负责设备发现、连接建立、数据重传、加密等关键任务。说白了,没有链路层,两个蓝牙设备连认识都做不到。
链路层有5种状态机:
- Standby — 空闲状态,不收发任何数据
- Advertising — 广播状态,向外发送广播包
- Scanning — 扫描状态,监听广播包
- Initiating — 发起连接,收到广播后请求建立连接
- Connection — 连接状态,双方按固定间隔交换数据
我记得刚接触BLE时,最困惑的就是连接状态下的跳频机制。BLE在连接状态下会按固定的跳频序列在37个数据信道上切换,每7.5ms到4s跳一次。这样做的好处是抗干扰能力强,但调试时抓包就很头疼——你得跟着跳频走。
避坑指南: 我曾经在一个项目中,把连接间隔设成了7.5ms,结果两个设备在强干扰环境下频繁断连。后来把连接间隔放宽到30ms,配合重传机制,稳定性明显提升。记住:连接间隔越小,功耗越高,抗干扰能力越差。
4.3 HCI — 主机-控制器接口
HCI是软硬件之间的分界线。主机(Host)运行在CPU上,控制器(Controller)通常是蓝牙芯片的硬件模块。HCI定义了它们之间的通信协议。
常见的HCI传输方式有:
- UART — 最常用,比如HC-05模块就是通过串口发HCI命令
- USB — 用于USB蓝牙适配器
- SDIO — 用于高速嵌入式系统
- SPI — 部分SoC内部使用
HCI命令包、事件包、数据包的格式是固定的。我建议你在调试时,先用逻辑分析仪抓一下HCI UART的波形,确认命令是否被正确发送。有一次我发现设备不响应,查了半天,结果是HCI命令的CRC校验算错了。
4.4 L2CAP — 逻辑链路控制与适配
L2CAP位于HCI之上,负责把上层的ATT数据包封装成L2CAP帧,再交给HCI发送。它主要做三件事:
- 分段与重组 — 上层数据包太大时,拆成多个小包发送
- 多路复用 — 多个上层协议(ATT、SMP等)共用同一个链路
- 流量控制 — 防止接收方来不及处理
L2CAP的默认MTU是23字节,但可以通过MTU交换协商到更大的值。我在做OTA升级时,就把MTU从23提到了247,传输速度直接翻了10倍。
关键点: L2CAP本身不关心数据内容,它只负责把数据从一端搬到另一端。真正的数据解析在ATT/GATT层完成。
4.5 ATT — 属性协议
ATT是BLE的灵魂。它定义了一种“属性”的数据结构,每个属性都有一个UUID、一组权限和一段值。设备之间通过读写这些属性来交换数据。
ATT有6种操作类型:
| 操作 | 方向 | 说明 |
|---|---|---|
| Request | Client → Server | 请求读/写属性 |
| Response | Server → Client | 响应请求 |
| Command | Client → Server | 写属性,无需响应 |
| Notification | Server → Client | 通知属性变化,无需确认 |
| Indication | Server → Client | 指示属性变化,需要确认 |
| Confirm | Client → Server | 确认收到Indication |
你想想看,Notification和Indication有什么区别?Notification发出去就不管了,适合传感器数据这种允许丢包的场景。Indication要求对方确认,适合配置参数这种必须可靠传输的场景。我在项目中就吃过亏——用Notification下发配置,结果丢包了设备没收到,用户一脸懵。
4.6 GATT — 通用属性协议
GATT是在ATT之上建立的一套服务框架。它定义了Service、Characteristic、Descriptor三级结构:
- Service — 一组相关功能的集合,比如电池服务、设备信息服务
- Characteristic — 一个具体的数据点,比如电池电量百分比
- Descriptor — 对Characteristic的描述或配置,比如通知开关
GATT的角色分为Client和Server。Server持有属性数据,Client发起读写请求。在大多数场景下,传感器设备是GATT Server,手机是GATT Client。
实战建议: 设计GATT服务时,尽量复用标准Service UUID(比如电池服务0x180F),这样手机端不用写私有App也能识别。只有标准UUID不够用的时候,才申请自定义128位UUID。我见过一个团队,所有数据都塞在一个自定义Service里,结果手机端解析起来非常痛苦。
4.7 各层之间的协作流程
为了让你更直观地理解各层如何配合,我画了一个典型的数据交互流程:
你看,一个简单的读操作,从手机App发起,经过GATT、ATT、L2CAP、HCI、Link Layer、Physical Layer六层,才变成无线电波发出去。传感器收到后,再一层层往上解析,最终把数据返回给手机。整个过程在几十毫秒内完成,这就是分层设计的威力。
4.8 小结
BLE协议栈的分层设计,让每一层各司其职。物理层管收发,链路层管连接,HCI管软硬件通信,L2CAP管数据封装,ATT/GATT管数据交互。你在实际开发中,大部分时间会跟GATT和Link Layer打交道——一个管业务逻辑,一个管连接稳定性。
嗯,这一章的内容就到这里。记住这张分层图,后面讲LTE和BLE融合的时候,你会反复用到这些概念。