1. 蓝牙协议栈概览:BLE协议栈架构、物理层与链路层基础、HCI接口解析

大家好,我是你们这门课的老朋友。今天咱们正式开篇,聊聊蓝牙协议栈的全貌。

说实话,我刚开始接触蓝牙开发那会儿,最头疼的就是这玩意儿。一堆分层、一堆术语,什么LL、HCI、GATT,看得人眼花缭乱。但后来我悟出一个道理:搞懂协议栈,就像搞懂一个公司的组织架构——你知道谁管谁、谁跟谁说话,出了问题就知道该找哪个部门。

好,咱们不扯远了,直接进入正题。

1.1 BLE协议栈的整体架构

BLE协议栈,说白了就是一套分层的软件体系。它把蓝牙通信这件事,拆成了几个独立的模块。每个模块各司其职,互不干扰。

我个人习惯把协议栈分成三大块:

  • Controller(控制器):负责底层的射频收发、链路管理。这部分通常是硬件+固件实现的。
  • Host(主机):负责上层的逻辑控制、数据封装、应用交互。跑在CPU上,纯软件。
  • Application(应用层):你写的业务代码,比如智能灯开关、心率监测。

你想想看,这三层之间怎么通信?Controller和Host之间,靠的就是HCI(Host Controller Interface)。这个接口,是咱们做嵌入式开发最常打交道的地方。

核心要点:BLE协议栈的分层设计,是为了让硬件厂商和软件开发者能独立工作。你换了一个蓝牙芯片,只要HCI接口不变,上层的Host代码基本不用动。

1.2 物理层(PHY)基础

物理层,就是最底层的“硬功夫”。它决定了信号怎么发、怎么收、用多快的速度。

BLE的物理层有几个关键参数,我列个表,大家一目了然:

参数 BLE 1M PHY BLE 2M PHY BLE Long Range (Coded PHY)
数据速率 1 Mbps 2 Mbps 125 kbps / 500 kbps
调制方式 GFSK GFSK GFSK
信道数量 40个(37个数据 + 3个广播) 40个 40个
典型距离 ~10米 ~10米 ~100米+

嗯,这里要注意:BLE的物理层工作在2.4GHz ISM频段,和Wi-Fi、Zigbee是邻居。所以干扰问题,你迟早会遇到。

我在项目中遇到过一件事:一个智能门锁,在办公室用得好好的,拿到家里就频繁断连。查了半天,发现是用户家路由器正好在BLE的信道37上狂发数据。后来我强制把门锁跳到信道39,问题就解决了。这就是物理层信道选择的重要性。

1.3 链路层(Link Layer)核心

链路层,是BLE协议栈里最“聪明”的一层。它负责管理设备之间的连接状态、数据包的收发时序。

链路层定义了五种状态机:

  1. Standby(待机):啥也不干,省电模式。
  2. Advertising(广播):向外喊话,“我在这儿,谁要连我?”
  3. Scanning(扫描):听别人广播,“谁在喊话?我看看。”
  4. Initiating(发起连接):听到广播后,主动说“咱俩连一下?”
  5. Connection(已连接):建立连接后,双方按约定时间收发数据。

你可能会问:为什么要有这么多状态?说白了,就是为了省电。设备大部分时间在Standby,只有需要时才切换到其他状态。

避坑指南:我曾经在做一个多连接项目时,发现设备连上3个从机后,第四个怎么也连不上。查了芯片手册才发现,链路层硬件只支持3个并发连接。所以选型时,一定要确认芯片的链路层支持多少个并发连接。

链路层还有一个关键概念——连接事件(Connection Event)。主从设备之间,每隔一个固定的时间间隔(Connection Interval)交换一次数据。这个间隔可以配置,范围从7.5ms到4s。

我建议:对实时性要求高的应用(比如游戏手柄),间隔设短一点,比如15ms;对功耗敏感的应用(比如温湿度传感器),间隔设长一点,比如100ms以上。

1.4 HCI接口解析

HCI,全称Host Controller Interface。它是连接Controller和Host的桥梁。

在嵌入式系统中,HCI通常通过以下几种方式实现:

  • UART(串口):最常见,比如用HC-05、CC2540这类模块。
  • USB:PC端蓝牙适配器常用。
  • SPI/SDIO:高速场景,比如Wi-Fi+蓝牙二合一芯片。

HCI的数据包格式很简单,就三种类型:

包类型 方向 用途
Command(命令) Host → Controller 让Controller做事情,比如“开始广播”
Event(事件) Controller → Host 告诉Host发生了什么,比如“连接已建立”
Data(数据) 双向 传输应用数据

举个例子,你写代码让蓝牙芯片开始广播,实际上就是通过UART发一个HCI Command包:

// 伪代码:发送HCI LE Set Advertising Enable命令
// 命令码:0x200A(LE Set Advertising Enable)
// 参数:0x01(启用广播)

uint8_t hci_cmd[] = {
    0x01,       // HCI Command包类型
    0x0A, 0x20, // Opcode(小端序)
    0x01,       // 参数长度
    0x01        // 启用
};
uart_send(hci_cmd, sizeof(hci_cmd));

然后Controller会回复一个Event包:

// 回复:Command Complete事件
// 事件码:0x0E
// 状态:0x00(成功)

uint8_t hci_event[] = {
    0x04,       // HCI Event包类型
    0x0E,       // 事件码
    0x04,       // 参数长度
    0x01,       // 已发送的命令包数
    0x0A, 0x20, // 对应命令的Opcode
    0x00        // 状态:成功
};

你看,就这么简单。但实际项目中,HCI的坑不少。

警告:我曾经在一个量产项目中,发现部分设备在UART通信时偶尔丢包。排查了三天,最后发现是UART波特率漂移导致的。BLE的HCI对时序要求很严格,UART的波特率误差必须控制在±2%以内,否则就会出现命令丢失或解析错误。

1.5 小结

好了,这一章的内容就到这里。咱们把BLE协议栈的骨架搭起来了:

  • 物理层:管信号收发,有1M、2M、Coded三种模式。
  • 链路层:管连接状态和时序,五种状态机来回切换。
  • HCI接口:管Host和Controller的通信,命令、事件、数据三种包。

下一章,咱们会深入链路层,聊聊广播和扫描的那些细节。到时候我会分享一个我踩过的坑——广播间隔设错了,导致手机死活搜不到设备。敬请期待!