第3章 BLE协议栈入门:核心概念与SDK初探
好,咱们进入第三章。说实话,这一章是很多TWS开发者的「劝退点」。为什么?因为BLE协议栈太抽象了。一堆缩写——GATT、GAP、HCI、L2CAP……刚接触时我也有点头大。
但别怕。咱们不搞学院派那套,我带你从实际开发的角度,把这些概念一个个拆开。说白了,BLE协议栈就是一套「蓝牙设备之间怎么聊天」的规则。你只要搞懂谁负责打招呼、谁负责传数据、谁负责底层跑腿,就差不多了。
3.1 BLE 5.0/5.2核心概念:GAP与GATT
这两个是BLE协议栈里最上层的两个角色。我习惯这么记:GAP管「能不能连」,GATT管「连上了能干啥」。
3.1.1 GAP——通用访问规范
GAP定义了蓝牙设备的角色和连接流程。在TWS耳机里,最常见的角色就是:
- Broadcaster(广播者):耳机开机后,不断发广播包,告诉手机「我在这儿」。
- Observer(观察者):手机扫描广播,发现耳机。
- Peripheral(外设):耳机作为从设备,等待手机连接。
- Central(中心):手机作为主设备,发起连接。
嗯,这里要注意:TWS耳机里,左右耳之间也有GAP角色。比如左耳做Central,右耳做Peripheral,这就是所谓的「监听模式」或「转发模式」。我在做第一代TWS时,就踩过这个坑——左右耳角色没配好,导致连接后右耳没声音。
GAP核心事件流程:
- 设备上电 → 进入广播状态
- 手机扫描到广播 → 发起连接请求
- 连接建立 → 进入已连接状态
- 连接断开 → 回到广播状态
3.1.2 GATT——通用属性规范
GATT才是真正干活的地方。它定义了一套「属性表」的结构。你想想看,手机连上耳机后,怎么知道耳机电量?怎么调节音量?都是通过GATT的读写操作。
GATT的核心概念有三个:
- Service(服务):比如「电池服务」、「设备信息服务」。每个服务有一个UUID。
- Characteristic(特征):服务下面的具体数据点。比如「电池电量百分比」就是一个特征。
- Descriptor(描述符):对特征的补充说明,比如配置通知开关。
举个例子,TWS耳机里最常见的GATT结构:
服务:电池服务 (0x180F)
└── 特征:电池电量 (0x2A19)
└── 描述符:客户端特征配置 (0x2902)
手机读这个特征,就能拿到电量值。手机写这个描述符,就能开启电量变化通知。
避坑指南:我曾经在开发时,把特征属性配成了「只读」,结果手机发写请求时一直返回错误。后来查了半天才发现是属性位没配对。GATT的属性位有读、写、通知、指示等,一定要按需配置。
3.2 广播与扫描:设备怎么找到对方
广播和扫描,是BLE连接的第一步。说白了,就是「喊话」和「听喊话」。
3.2.1 广播(Advertising)
广播包分两种:
- 广播包(Advertising PDU):包含设备名、UUID、厂商自定义数据等。
- 扫描响应包(Scan Response PDU):手机扫描时,设备可以额外回复更多信息。
广播间隔是个关键参数。间隔越短,手机发现越快,但功耗越高。TWS耳机里,我一般设成100ms左右。连接后可以切换到更慢的广播模式,省电。
注意:BLE 5.0引入了扩展广播,数据包长度从31字节提升到255字节。但TWS耳机里用得不多,因为大部分场景31字节够用了。除非你要广播大量厂商数据。
3.2.2 扫描(Scanning)
手机扫描时,有两种模式:
- 被动扫描:只收广播包,不发请求。省电。
- 主动扫描:收到广播后,发扫描请求,获取扫描响应包。信息更全。
我记得有一次,客户反馈耳机连不上手机。排查后发现是手机端扫描窗口设得太短,漏掉了耳机的广播包。嗯,这种问题最难查,因为看起来两边都没错。
3.3 连接:建立稳定的数据通道
连接建立后,手机和耳机之间就有了一个稳定的数据通道。连接参数包括:
| 参数 | 说明 | TWS典型值 |
|---|---|---|
| 连接间隔 | 两个连接事件之间的时间 | 7.5ms ~ 30ms |
| 延迟 | 设备可以跳过的连接事件数 | 0 ~ 4 |
| 超时时间 | 超过此时间无数据则断开 | 4s ~ 6s |
连接间隔越小,数据延迟越低,但功耗越高。TWS耳机里,音频传输时一般用7.5ms或10ms。待机时可以放宽到30ms以上。
个人经验:连接参数不是随便设的。手机端和耳机端要协商一致。我遇到过耳机端设了7.5ms,但手机端不支持,结果连接后频繁断连。后来加了连接参数更新请求,才解决。
3.4 HCI层与L2CAP层的作用
这两个层在协议栈中间,平时开发不直接碰,但出了问题就得找它们。
3.4.1 HCI——主机控制器接口
HCI是Host(主机)和Controller(控制器)之间的接口。在nRF5 SDK里,Host是CPU跑的协议栈上层,Controller是蓝牙射频芯片。
HCI定义了命令、事件、数据三种包格式。比如:
- Host发HCI命令给Controller:启动广播、建立连接等。
- Controller发HCI事件给Host:连接完成、断开通知等。
- Host和Controller之间传ACL数据:音频数据、ATT协议包等。
说白了,HCI就是一条「命令通道」和「数据通道」。调试时,抓HCI日志能看清每一笔交互。我习惯用nRF Sniffer抓HCI包,排查连接失败问题特别好用。
3.4.2 L2CAP——逻辑链路控制与适配协议
L2CAP在HCI上面,负责把上层数据打包成固定长度的包,传给HCI。它支持:
- 分段与重组:大包拆小包,接收端再拼回去。
- 协议多路复用:同一个连接上,可以同时跑ATT、SMP、RFCOMM等多种协议。
- 流控与重传:保证数据可靠传输(可选)。
在TWS耳机里,L2CAP最常用的就是ATT协议通道。音频数据走的是另一个通道——Isochronous Channel(BLE 5.2新增)。
避坑指南:我曾经在调试时,发现手机发过来的ATT包总是丢。后来查L2CAP的MTU(最大传输单元)才发现,手机端MTU是512字节,耳机端默认是23字节。包太大,L2CAP分段没处理好,就丢了。所以连接后第一件事,就是交换MTU。
3.5 Nordic nRF5 SDK与Realtek Amebad BLE SDK架构初探
好了,理论讲完了,咱们看看实际开发用的SDK。
3.5.1 Nordic nRF5 SDK
nRF5 SDK是Nordic官方提供的BLE开发包。它的架构大概是:
- Application层:你的业务代码,比如按键处理、电量上报。
- SoftDevice层:Nordic的BLE协议栈,以二进制形式提供,用户不能改。
- HCI层:SoftDevice和Application之间的接口,通过API调用。
开发时,你主要跟Application层打交道。调用SoftDevice的API来启动广播、处理连接、读写GATT。比如:
// 启动广播
sd_ble_gap_adv_start(&adv_params, APP_BLE_CONN_CFG_TAG);
// 处理连接事件
void ble_evt_handler(ble_evt_t const * p_ble_evt) {
switch (p_ble_evt->header.evt_id) {
case BLE_GAP_EVT_CONNECTED:
// 连接建立
break;
case BLE_GAP_EVT_DISCONNECTED:
// 连接断开
break;
}
}
我个人习惯用nRF5 SDK的例程做起点,比如ble_app_uart或ble_app_hrs。改改服务UUID和特征,就能快速搭起一个原型。
3.5.2 Realtek Amebad BLE SDK
Realtek的SDK跟Nordic不太一样。它把协议栈和应用代码放在一起,你可以直接修改协议栈参数。架构上:
- Application层:你的业务代码。
- GAP/GATT层:Realtek封装好的API,比Nordic更上层。
- HCI层:直接暴露HCI命令接口,方便调试。
Realtek SDK的一个特点是,它提供了很多配置宏,改改头文件就能调参数。比如:
// 配置广播间隔
#define GAP_ADV_INTERVAL_MIN 160 // 100ms
#define GAP_ADV_INTERVAL_MAX 320 // 200ms
// 配置连接间隔
#define GAP_CONN_INTERVAL_MIN 12 // 15ms
#define GAP_CONN_INTERVAL_MAX 24 // 30ms
嗯,这里要注意:Realtek的文档没有Nordic那么全。我刚开始用的时候,经常翻源码找API定义。但好处是,代码完全开源,你可以直接改协议栈行为。
总结一下:
- Nordic:文档好、社区活跃、SoftDevice稳定。适合快速原型开发。
- Realtek:代码开源、可定制性强、成本低。适合量产产品。
选哪个?看你的产品定位和团队经验。我个人建议,新手先从Nordic入手,熟悉了再转Realtek。
3.6 本章小结
这一章内容不少,但都是基础中的基础。GAP管连接,GATT管数据,广播和扫描是发现机制,HCI和L2CAP是底层支撑。SDK只是工具,理解了协议栈,换哪个平台都不怕。
下一章,咱们要动手写代码了——搭建开发环境,跑第一个BLE例程。到时候你会发现,理论懂了,写代码其实挺快的。
公众号:蓝海资料掘金营,微信deep3321