第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驱动。到时候我会把踩过的坑都告诉你。