BLE协议栈架构:分层模型与数据流

聊BLE协议栈之前,我先问大家一个问题:为什么蓝牙这么复杂的通信协议,最终能被几十亿设备用起来?

答案其实就四个字——分层设计。每一层只干自己的事,层与层之间通过标准接口通信。这样硬件厂商可以专注做底层,应用开发者只管上层API,互不干扰。

我个人习惯把BLE协议栈想象成一栋三层小楼:Controller(控制器层)、Host(主机层)、Application(应用层)。今天我们就一层一层拆开看。

核心观点:BLE协议栈的分层模型,本质上是把「无线收发」和「数据业务」解耦。Controller管物理信号,Host管逻辑连接,Application管用户场景。

一、协议栈三层总览

先看一张整体架构图,我亲手画的,帮你建立全局印象:

BLE协议栈分层架构图 BLE协议栈分层模型 Application 应用层 用户自定义Profile、业务逻辑、数据解析 (心率计、温度计、灯控、键盘等具体场景) Host 主机层 GAP(通用访问规范) GATT(通用属性规范) ATT(属性协议) L2CAP(逻辑链路控制) SM(安全管理) HCI(主机控制器接口) Controller 控制器层 LL(链路层)、PHY(物理层) (射频收发、信道管理、包处理、状态机)

这张图我建议你保存下来。每次调试BLE问题,先定位是哪一层出的问题,能省一半时间。

二、Controller层:最底层的硬功夫

Controller层是离硬件最近的一层,通常由蓝牙芯片固件实现。它包含两个核心模块:PHY(物理层)和LL(链路层)。

2.1 PHY(物理层)

PHY负责最原始的比特流收发。说白了,就是决定用什么频率、什么调制方式、多大功率把0和1发出去。

  • 工作频段:2.4GHz ISM频段,40个信道(37个数据信道 + 3个广播信道)
  • 调制方式:GFSK(高斯频移键控),简单可靠
  • 速率:1Mbps(LE 1M PHY)或2Mbps(LE 2M PHY,蓝牙5.0引入)
  • 发射功率:典型0dBm到+10dBm,视芯片而定

避坑指南:我曾经在一个穿戴项目里,PHY层发射功率设到了+8dBm,结果功耗超标。后来发现场景只需要3米通信距离,降到0dBm完全够用,功耗直接砍半。别盲目开大功率。

2.2 LL(链路层)

LL层是BLE协议栈的大脑。它负责:

  • 状态机管理:Standby、Advertising、Scanning、Initiating、Connection五个状态
  • 信道选择:自适应跳频(AFH),避开干扰信道
  • 包处理:前导码、访问地址、PDU、CRC的组装与解析
  • 连接管理:连接建立、连接参数更新、断开连接

你想想看,两个BLE设备怎么知道什么时候该听、什么时候该说?就是LL层在背后调度。它用跳频+时间槽的方式,让主从设备在约定好的时间和频率上碰头。

三、HCI:连接Controller和Host的桥梁

HCI(Host Controller Interface)不是一层协议,而是一个标准接口。它定义了Host和Controller之间怎么通信。

实际项目中,HCI的物理形式有三种:

接口类型 典型场景 特点
UART(H4) 双芯片方案(Host MCU + 蓝牙芯片) 简单、通用、速率受限
USB(H2) PC蓝牙适配器、开发板 高速、即插即用
SPI/SDIO 高性能嵌入式系统 吞吐量大、延迟低

注意:如果你用的是单芯片方案(比如Nordic nRF52、TI CC26xx),HCI是内部接口,不需要你操心。但双芯片方案里,HCI的波特率、流控、缓冲区大小经常是坑。我遇到过UART波特率设太高导致丢包,降到115200就稳了。

四、Host层:协议栈的中枢

Host层跑在MCU上,通常由蓝牙协议栈软件实现。它包含五个关键模块:L2CAP、SM、ATT、GATT、GAP。

4.1 L2CAP(逻辑链路控制与适配协议)

L2CAP是Host层的交通调度员。它负责:

  • 协议复用:区分上层是ATT流量还是SM流量
  • 分段重组:大包拆小、小包拼大
  • MTU管理:协商最大传输单元(默认23字节,可协商到更大)

嗯,这里要注意:BLE的L2CAP和经典蓝牙的L2CAP不完全一样。BLE的L2CAP简化了很多,不支持面向连接的信道(除了LE Credit Based Flow Control模式)。

4.2 SM(安全管理)

SM负责配对、绑定、加密。它定义了三种配对方式:

  • Just Works:无用户交互,安全性最低
  • Passkey Entry:输入6位数字PIN码
  • Out of Band(OOB):通过NFC等带外方式交换密钥

我记得有一次做门锁项目,客户要求必须用Passkey Entry。结果发现用户每次开门都要输6位密码,体验极差。后来改成了OOB+手机NFC碰一下,用户满意度直线上升。选配对方式一定要考虑实际场景。

4.3 ATT(属性协议)

ATT定义了客户端-服务器通信模型。服务器端暴露属性(Attribute),客户端读写这些属性。每个属性包含:

  • 句柄(Handle):16位唯一标识
  • 类型(UUID):16位或128位,标识属性含义
  • 权限:读、写、加密读、加密写等
  • 值(Value):实际数据

4.4 GATT(通用属性规范)

GATT在ATT之上定义了数据组织方式。它把属性组织成:

  • Service(服务):一组相关功能的集合(如电池服务、心率服务)
  • Characteristic(特征):一个具体的数据点(如电池电量百分比)
  • Descriptor(描述符):特征的附加信息(如单位、配置)

说白了,GATT就是一张数据表格。设备A告诉设备B:「我的电池服务在句柄0x0010,你读它就能拿到电量。」

4.5 GAP(通用访问规范)

GAP定义了设备如何被发现、如何建立连接。它规定了四种角色:

角色 职责 典型设备
Broadcaster(广播者) 只发广播,不连接 信标(Beacon)
Observer(观察者) 只扫描广播,不连接 扫描仪
Peripheral(外设) 发广播,等待连接 心率带、温度传感器
Central(中心) 扫描并发起连接 手机、平板

五、数据流在协议栈中的传递过程

理解了各层职责,我们来看一个完整的数据流。假设手机(Central)要读取心率带(Peripheral)的心率值。

BLE数据流传递过程 数据流传递过程(手机读取心率值) 心率带(Peripheral) 手机(Central) App:心率值=72 bpm (应用层) App:读取心率特征 (应用层) GATT:组织心率服务数据 ATT:响应Read Request GATT:找到心率服务 ATT:发送Read Request L2CAP:封装ATT PDU 分配CID=0x0004 L2CAP:解封装ATT PDU 提取CID=0x0004 HCI:封装HCI数据包 添加HCI Header HCI:解析HCI数据包 去除HCI Header LL:组装链路层包 PHY:GFSK调制→天线发射 LL:解析链路层包 PHY:天线接收→GFSK解调 空中无线传输

整个流程可以概括为「层层封装,层层解析」:

  1. 手机App调用GATT API,发起「读取心率特征」请求
  2. GATT层找到对应Service和Characteristic,交给ATT层
  3. ATT层构造Read Request PDU(包含句柄0x0010)
  4. L2CAP层给ATT PDU加上L2CAP头(CID=0x0004表示ATT)
  5. HCI层封装成HCI数据包,通过UART/SPI发给Controller
  6. LL层组装链路层包(加前导码、访问地址、CRC),交给PHY
  7. PHY层GFSK调制,通过天线发射出去
  8. 心率带收到后,逆向解析:PHY解调→LL校验→HCI拆包→L2CAP提取→ATT解析→GATT查表→App返回心率值72 bpm

关键点:每一层只关心自己的头信息。LL层不看ATT数据,ATT层不管L2CAP分段。这种「各扫门前雪」的设计,让协议栈的维护和升级变得非常灵活。

六、实际项目中的分层思维

最后分享一个我自己的经验。有一次调试一个BLE门锁,发现手机连上后经常断连。一开始我怀疑是射频干扰,折腾了三天天线匹配。后来静下心来看日志,发现是LL层的连接间隔(Connection Interval)设得太小,手机端处理不过来。

你看,如果不懂分层,你可能会在PHY层瞎忙活。但如果你知道连接参数属于LL层,直接改连接间隔从7.5ms到30ms,问题就解决了。

我的建议:遇到BLE问题,先问自己三个问题:

  • 是连不上?(大概率GAP/LL层问题)
  • 是连上后数据读不到?(大概率GATT/ATT层问题)
  • 是连上后频繁断连?(大概率LL层连接参数问题)

定位到层,再深入查,效率翻倍。

好了,协议栈的分层模型就讲到这里。下一层一层拆开看,你会发现BLE其实没那么神秘。后面几章我们会深入每个模块的细节,包括怎么配广播包、怎么写GATT服务、怎么处理配对加密——都是实战干货。