第1章:蓝牙协议栈架构——从分层设计到实战落地

各位同学,大家好。我是老张,在嵌入式蓝牙领域摸爬滚打了十几年。今天咱们开始聊蓝牙协议栈架构,这是整个课程的地基。你想想看,如果连Host和Controller都分不清,后面写驱动肯定要踩坑。

1.1 Host层与Controller层:蓝牙的“大脑”和“手脚”

蓝牙协议栈最核心的划分,就是Host和Controller。我个人习惯把Host想象成大脑,负责逻辑决策;Controller就是手脚,负责具体干活。

Host层跑在主CPU上,处理高层协议。比如L2CAP的数据分包、SMP的配对加密、ATT的属性读写、GATT的profile管理。说白了,你写的蓝牙应用代码,大部分都在Host层。

Controller层通常是一个独立的芯片或模块,负责射频收发、基带处理、链路控制。它只管把Host发来的数据包发出去,或者把收到的数据包交给Host。我早期做项目时,曾把Controller当成黑盒,结果遇到射频干扰问题,排查了整整三天——后来才发现是Controller的跳频参数没配好。

关键点:Host和Controller之间通过HCI通信。HCI就是它们之间的“翻译官”。

1.2 HCI接口详解:协议栈的“高速公路”

HCI(Host Controller Interface)是蓝牙协议栈里最实在的接口。它定义了Host怎么给Controller发命令,Controller怎么给Host回事件,以及两边怎么传数据。

HCI有三种物理传输方式:

  • UART:最常见,成本低,我项目中90%都用它。注意波特率要匹配,不然数据会乱。
  • USB:适合PC端蓝牙适配器,吞吐量大。
  • SDIO/SPI:用于高性能嵌入式场景,比如WiFi+蓝牙 combo芯片。

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

  • 命令包:Host→Controller,比如“开始扫描”。
  • 事件包:Controller→Host,比如“扫描到设备”。
  • 数据包:双向,传ACL(异步)或SCO(同步)数据。
  • ISO数据包:BLE Audio时代新增的,用于等时传输。

避坑指南:我曾经在调试HCI UART时,发现Controller一直不回事件。查了半天,原来是Host发送命令包时,忘了在包前面加HCI类型字节(0x01)。记住:HCI包的第一个字节必须是类型标识,否则Controller会直接丢弃。

1.3 协议栈分层结构:L2CAP、SMP、ATT、GATT

蓝牙协议栈的分层,说白了就是“各司其职”。每一层只关心自己的事,把复杂问题拆解掉。

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

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

  • 将上层大包拆成小包(MTU协商),再交给Controller发送。
  • 复用多个上层协议(比如同时跑ATT和SMP)。
  • 提供可靠传输(重传、流控)。

我记得有个项目,设备连上后老是断连。抓HCI日志发现,L2CAP的MTU协商失败——Host说我能收1000字节,Controller说只能收200。最后双方没谈拢,直接断链。所以MTU协商一定要做对。

1.3.2 SMP(安全管理协议)

SMP负责蓝牙配对、加密和密钥分发。它定义了三种配对方式:

  • Just Works:无用户交互,安全性最低。
  • Passkey Entry:输入6位数字PIN码。
  • Numeric Comparison:显示6位数字,用户确认。

你想想看,如果做智能门锁,用Just Works配对,那等于把钥匙挂门上。所以选配对方式时,一定要根据产品安全等级来。

1.3.3 ATT(属性协议)

ATT是BLE的灵魂。它定义了“属性”这个概念——每个属性有UUID、句柄、权限和值。ATT的操作就四种:读、写、通知、指示。

我刚开始学ATT时,总觉得“属性”很抽象。后来做心率计项目才明白:心率值就是一个属性,UUID是0x2A37,句柄是0x0012,权限是只读,值就是心率数据。就这么简单。

1.3.4 GATT(通用属性配置文件)

GATT在ATT之上,定义了更高级的概念:Service(服务)、Characteristic(特征)、Descriptor(描述符)。

举个例子:一个电池服务(Battery Service)包含一个电池电量特征(Battery Level),这个特征又带一个客户端特征配置描述符(CCCD),用来使能通知。GATT就是把这些东西组织成树形结构。

注意:GATT和ATT的区别很多人搞混。ATT是传输协议,GATT是数据组织方式。ATT负责“怎么传”,GATT负责“传什么”。

1.4 主流协议栈介绍:选型决定成败

做蓝牙开发,选协议栈就像选武器。选对了事半功倍,选错了天天加班。下面是我用过的几个主流协议栈:

协议栈 适用平台 特点 我的评价
BlueZ Linux 官方标准,功能最全,支持经典蓝牙+BLE PC和服务器首选,但嵌入式移植麻烦
Zephyr RTOS 开源,模块化,支持多厂商芯片 物联网项目首选,我最近两个项目都用它
TI CC13xx/CC26xx 成熟稳定,文档齐全,有图形化配置工具 适合快速原型,但闭源,定制困难
Nordic nRF5/nRF52/nRF53 性能强,功耗低,SDK丰富 BLE开发者的最爱,社区活跃

1.4.1 BlueZ

BlueZ是Linux内核自带的蓝牙协议栈。它实现了完整的Host层,包括HCI、L2CAP、RFCOMM、GATT等。如果你在树莓派或Ubuntu上做蓝牙开发,BlueZ是默认选择。

但要注意,BlueZ的API比较底层,用起来有点繁琐。我习惯用它的D-Bus接口,或者封装一层libbluetooth。

1.4.2 Zephyr

Zephyr的蓝牙协议栈是纯原生的,不依赖Linux。它支持BLE 5.x、Mesh、Audio等新特性。我最喜欢它的Kconfig配置系统——想开什么功能,直接menuconfig勾选就行。

举个例子,你要支持BLE长包(LE Data Length Extension),在Zephyr里只需要配一个宏:CONFIG_BT_CTLR_DATA_LENGTH_MAX=251。而在其他协议栈里,可能要改一堆底层代码。

1.4.3 TI和Nordic

这两家是芯片厂商,协议栈和硬件绑定。TI的协议栈叫BLE-Stack,Nordic的叫SoftDevice。它们的特点是:

  • 性能优化到极致,功耗控制非常好。
  • 但闭源,你只能调用API,不能看内部实现。
  • 升级协议栈版本时,有时要跟着换SDK,挺折腾的。

我个人建议:做产品原型时用TI/Nordic的SDK快速验证,量产前如果对成本敏感,可以考虑迁移到Zephyr或自研协议栈。

总结一下:协议栈选型没有银弹。如果你做Linux网关,BlueZ是唯一选择;如果你做低功耗传感器,Nordic的SoftDevice最省心;如果你做多平台产品,Zephyr的移植性最好。

好了,这一章我们聊了蓝牙协议栈的骨架——Host和Controller怎么分工,HCI怎么通信,各层协议干什么,以及主流协议栈怎么选。下一章,咱们要深入HCI驱动层,手把手写一个UART HCI驱动。到时候我会把踩过的坑都告诉你。