3、BLE协议基础(上):物理层、链路层、主机控制接口层(HCI)基本概念

好,咱们正式开始啃协议栈了。很多初学者一上来就被蓝牙协议栈的层次结构吓住,觉得太抽象。其实你想想看,它就是一个分层分工的通信模型,每一层只干自己那点事,层与层之间通过标准接口对话。今天我们先聊最下面的三层:物理层(PHY)、链路层(LL)和主机控制接口层(HCI)。

我个人习惯把这三层比作一个快递系统:PHY 是马路和卡车,LL 是快递分拣中心,HCI 就是快递单上的地址标签。这样是不是好理解多了?

3.1 物理层(PHY)—— 信号怎么在空中飞

物理层是协议栈的最底层,直接跟天线打交道。它负责把二进制比特流变成无线电波发射出去,也负责把收到的无线电波还原成比特流。

BLE 工作在 2.4GHz ISM 频段,这个频段也是 Wi-Fi、Zigbee、微波炉的地盘。所以干扰是家常便饭。我在项目中遇到过好几次,设备在微波炉旁边死活连不上,后来一查就是 PHY 层的信道被干扰了。

3.1.1 关键参数

参数 BLE 4.0/4.2 BLE 5.0 说明
调制方式 GFSK GFSK 高斯频移键控,抗干扰好
符号速率 1 Mbps 1 Mbps / 2 Mbps 2 Mbps 模式吞吐量翻倍
信道数 40 个(37 数据 + 3 广播) 40 个 信道间隔 2 MHz
发射功率 典型 0 dBm 最大 +20 dBm 功率越大距离越远,但耗电
接收灵敏度 -70 dBm 典型 -70 dBm 典型 好的芯片能做到 -95 dBm
我的经验:BLE 5.0 的 2 Mbps 模式确实快,但代价是接收灵敏度会下降约 3-5 dB。如果你做的是长距离产品,老老实实用 1 Mbps 模式更靠谱。

3.1.2 信道与跳频

BLE 把 2.4GHz 频段分成 40 个信道,编号 0-39。其中 37、38、39 三个信道专门用于广播,剩下的 37 个信道用于数据通信。

为什么要跳频?说白了就是为了躲干扰。每次连接事件,主从双方都会按照一个伪随机序列跳到下一个信道。这个序列由链路层管理,PHY 只管执行。

注意:广播信道固定在三信道上轮流发送,不是跳频的。所以广播信道的拥塞问题一直是个痛点。我曾经在一个展会现场,几十台设备同时广播,结果互相撞包撞到怀疑人生。

3.2 链路层(LL)—— 连接的核心大脑

链路层是 BLE 协议栈里最核心的一层。它负责设备发现、广播、连接建立、数据包收发、加密、重传……几乎所有跟连接相关的逻辑都在这里。

我刚开始做 BLE 开发时,总觉得链路层离应用层太远,不用太关心。直到有一次设备连接后频繁断连,查了三天才发现是链路层的连接参数没配好。嗯,从那以后我再也不敢轻视 LL 了。

3.2.1 链路层的两种角色

  • Advertiser(广播者):在广播信道上发送广播包,告诉别人“我在这里”。
  • Scanner(扫描者):在广播信道上监听广播包,发现周围的设备。
  • Master(主机):发起连接的一方,负责管理连接时序。
  • Slave(从机):接受连接的一方,按主机的时序响应。

一个设备可以同时扮演多个角色。比如你的手机既是 Scanner 又是 Master,而一个传感器节点可能既是 Advertiser 又是 Slave。

3.2.2 连接事件与连接参数

连接建立后,主从双方不会一直开着射频收发,那样太耗电了。它们约定好每隔一段时间交换一次数据,这个时间间隔叫连接间隔(Connection Interval)。

每次数据交换称为一个连接事件(Connection Event)。在连接事件里,主机会先发一个包,从机收到后可以回复一个包。如果没收到,双方会在下一个连接事件重试。

参数 范围 典型值 影响
连接间隔 7.5 ms - 4 s 30-50 ms 间隔越小,延迟越低,功耗越高
从机延迟 0 - 499 0-5 允许从机跳过若干连接事件,省电
监督超时 100 ms - 32 s 2-6 s 超过此时间未收到包,认为连接断开

避坑指南:我曾经把连接间隔设成 7.5 ms,结果设备功耗飙升到 3 mA 以上,电池两天就耗光了。后来改成 50 ms,功耗降到 200 μA,功能完全不受影响。所以连接间隔不是越小越好,要根据实际需求权衡。

3.2.3 数据包结构

链路层的数据包格式很简单,我直接给你看:

| 前导码 (1B) | 访问地址 (4B) | PDU (2-257B) | CRC (3B) |
  • 前导码:用于接收端同步时钟,固定为 0xAA 或 0x55。
  • 访问地址:连接的唯一标识。广播包用固定值 0x8E89BED6,数据包用随机生成的地址。
  • PDU:协议数据单元,包含实际的广播数据或用户数据。
  • CRC:循环冗余校验,用于检测传输错误。

PDU 内部又分头部和负载,头部里包含了 LLID(链路层标识)、NESN/SN(序列号,用于重传控制)等关键字段。这些字段在链路层自动处理,应用层基本不用操心。

3.3 主机控制接口层(HCI)—— 软硬件的分界线

HCI 是蓝牙协议栈里一个非常特殊的分层。它把控制器(Controller)和主机(Host)分开。控制器通常包含 PHY 和 LL,跑在蓝牙芯片内部;主机包含上层的 L2CAP、ATT、GATT 等,跑在应用处理器上。

HCI 就是这两者之间的通信接口。它定义了一套标准的命令、事件和数据包格式。

3.3.1 为什么需要 HCI?

你想想看,蓝牙芯片厂商(比如 Nordic、TI、Dialog)只提供 PHY 和 LL 的硬件实现,而上层的协议栈和应用是开发者自己写的。如果没有一个标准接口,每个厂商的 API 都不一样,开发者得累死。

HCI 的出现就是为了解决这个问题。只要芯片厂商实现了标准的 HCI 接口,上层协议栈就可以无缝移植。我在项目中换过三次蓝牙芯片,每次只需要改 HCI 底层的驱动代码,上层协议栈几乎不用动。

3.3.2 HCI 的三种数据包

包类型 方向 用途 示例
命令包 Host → Controller 下发配置和操作指令 设置广播参数、发起连接
事件包 Controller → Host 上报状态变化和结果 连接完成、断开连接
数据包 双向 传输 ACL 用户数据 读写特征值

HCI 的物理传输方式可以是 UART、USB、SPI 或 SDIO。最常用的是 UART,因为实现简单,只需要三根线(TX、RX、GND)。

我的习惯:调试 HCI 时,我会用逻辑分析仪抓 UART 数据,直接看 HCI 命令和事件的交互。这比看代码日志更直观,能快速定位是 Host 没发命令,还是 Controller 没响应。

3.3.3 HCI 命令示例

下面是一个典型的 HCI 命令格式,用于设置广播参数:

OpCode: 0x2006 (HCI_LE_Set_Advertising_Parameters)
参数:
  - Advertising_Interval_Min: 0x00A0 (160 ms)
  - Advertising_Interval_Max: 0x00A0 (160 ms)
  - Advertising_Type: 0x00 (可连接广播)
  - Own_Address_Type: 0x00 (公共地址)
  - Peer_Address_Type: 0x00
  - Peer_Address: 0x0000000000
  - Advertising_Channel_Map: 0x07 (三个广播信道全开)
  - Advertising_Filter_Policy: 0x00

Host 把这个命令通过 HCI 发给 Controller,Controller 执行成功后返回一个 Command Complete 事件。整个过程是同步的,但实际执行是异步的——Host 发完命令就可以干别的事,等事件回来再处理。

3.4 三层之间的关系

为了让你更直观地理解这三层怎么配合,我画了一张图:

BLE 协议栈底层三层架构 应用层(Profiles / App) 主机(Host):L2CAP / ATT / GATT / SMP HCI 层(主机控制接口) 链路层(LL) 物理层(PHY) HCI 是软硬件的分界线:Host 跑在应用处理器上,Controller(LL + PHY)跑在蓝牙芯片上

从这张图你可以看到:

  • PHY 在最底层,负责收发无线电信号。
  • LL 在中间,管理连接、广播、数据包收发和重传。
  • HCI 是 Host 和 Controller 之间的桥梁,Host 通过 HCI 命令控制 Controller,Controller 通过 HCI 事件上报状态。

实际开发中,如果你用的是 SoC 方案(比如 Nordic nRF52 系列),Host 和 Controller 跑在同一颗芯片上,HCI 只是内部的一个软件接口,你基本感觉不到它的存在。但如果你用的是双芯片方案(比如 Host 跑在 Linux 上,Controller 用一颗独立的蓝牙芯片),HCI 就是通过 UART 或 USB 连接的物理接口,这时候 HCI 的调试就变得非常重要了。

总结一下:PHY 管信号,LL 管连接,HCI 管通信。这三层构成了 BLE 协议栈的基石。后面的 L2CAP、ATT、GATT 都是建立在它们之上的。理解这三层,你就掌握了 BLE 通信的底层逻辑。