3. BLE协议栈核心 - LL层(链路层)

各位好,今天我们来啃一块硬骨头——LL层,也就是链路层。

说实话,很多做蓝牙开发的工程师,应用层写得飞起,但一谈到LL层就有点发怵。我个人觉得,LL层才是BLE协议栈真正的灵魂。你想想看,所有的数据收发、连接建立、功耗控制,底层全靠它撑着。

这一章,我会把LL层的几个核心模块掰开揉碎了讲。包括它的状态机怎么跑、空中飞的数据包长什么样、CRC怎么算、白名单怎么配。嗯,都是实战中绕不开的东西。

3.1 LL层状态机

LL层状态机,说白了就是蓝牙芯片的「行为模式切换器」。它一共就5种状态:Standby(待机)、Advertising(广播)、Scanning(扫描)、Initiating(发起连接)、Connection(连接)

我刚开始学的时候,觉得这玩意儿不就是个状态切换嘛,有啥难的?后来在项目中踩了坑才发现,状态切换的时机和条件,稍微搞错一点,设备就连不上了。

3.1.1 Standby 状态

这是最基础的状态。芯片上电后默认就待在这里,啥也不干,功耗最低。你可以把它理解成「休眠待命」模式。从Standby可以跳到任意其他状态,反过来,其他状态也都能回到Standby。

3.1.2 Advertising 状态

广播状态,就是设备在「喊话」:「我在这里,谁要连我?」

广播又分两种:

  • 可连接广播:允许其他设备发起连接
  • 不可连接广播:只发数据,不接受连接(比如信标Beacon)

我在项目中遇到过一个问题:广播间隔设得太短,功耗飙升;设得太长,手机扫描半天找不到设备。后来我总结了一个经验——广播间隔建议设在100ms~200ms之间,兼顾功耗和发现速度。

3.1.3 Scanning 状态

扫描状态,就是设备在「听」——监听周围有没有设备在广播。

扫描也分两种:

  • 被动扫描:只听,不发请求
  • 主动扫描:听到广播后,发一个扫描请求,让对方回复更多数据

你想想看,主动扫描虽然能拿到更多信息,但也会增加空中的碰撞概率。所以,如果你的应用只需要知道「周围有没有设备」,被动扫描就够了。

3.1.4 Initiating 状态

发起连接状态。设备在扫描到特定广播后,主动发送连接请求。

这里有个关键点:发起连接时,设备必须知道对方的蓝牙地址。白名单机制就是在这里起作用的,后面会细说。

3.1.5 Connection 状态

连接建立成功后的状态。此时两个设备之间有了稳定的数据通道。

连接状态下,又分两个角色:

  • Master(主机):负责决定连接参数(如连接间隔、延迟等)
  • Slave(从机):跟随主机的节奏收发数据

核心要点:连接建立后,主机和从机并不是一直发数据的。它们按照「连接事件」的节奏,每隔一个连接间隔才交换一次数据。这个机制大大降低了功耗。

3.2 空中数据包格式

BLE在空中传输的数据包,结构其实挺简单的。我画了一张图,方便大家理解。

前导码 1字节 访问地址 4字节 PDU 2~257字节 CRC 3字节 2字节 有效载荷 0~255字节 MIC 4字节(可选) BLE空中数据包结构 PDU内部展开

数据包分四段:

  1. 前导码(1字节):用于接收端做时钟同步。固定是0xAA或0x55。
  2. 访问地址(4字节):这是数据包的「身份证」。广播包用固定的0x8E89BED6,连接包用随机生成的地址。
  3. PDU(协议数据单元):真正的数据内容。里面又包含头、有效载荷、可选的MIC(消息完整性校验)。
  4. CRC(3字节):循环冗余校验,用于检测数据在传输过程中有没有出错。

实战技巧:抓包分析时,我习惯先看访问地址。如果是0x8E89BED6,那肯定是广播包;如果是其他值,那就是连接后的数据包。这个判断方法百试百灵。

3.3 CRC校验机制

CRC,全称Cyclic Redundancy Check。说白了,就是给数据包算一个「指纹」,接收端收到后重新算一遍,看看指纹对不对得上。

BLE用的CRC算法是CRC-24,多项式是0x5B6B6。嗯,这个数字你不需要背,但要知道它的作用。

我曾经遇到过一个诡异的问题:两台设备在近距离通信时一切正常,但距离拉到10米以上,偶尔会出现数据错乱。后来一查,是CRC校验没通过,但上层协议没做重传处理。从那以后,我养成了一个习惯——在应用层也加一层校验,双重保险。

3.4 白名单机制

白名单,就是一张「允许列表」。只有列表里的设备,才能和你的芯片通信。

白名单在三个场景下特别有用:

  • 过滤广播:只处理指定设备的广播包
  • 过滤扫描:只回应指定设备的扫描请求
  • 过滤连接:只接受指定设备的连接请求

白名单的配置方式很简单,就是把设备的蓝牙地址写进去。但要注意:

注意:白名单的容量是有限的。一般芯片支持8~32个地址。如果你的设备需要连接大量外围设备,白名单可能不够用。这时候可以考虑用「动态白名单」——在运行时动态增删地址。

我在做智能门锁项目时,就用到了白名单。门锁只允许主人的手机连接,其他人的手机扫描到也连不上。实现方式就是在LL层配置白名单,只放行主人的手机地址。这样即使应用层有漏洞,底层也帮你挡了一道。

3.5 实战避坑指南

最后,分享几个我踩过的坑:

  1. 状态切换时序:从广播切换到连接时,一定要确保广播已经停止。我见过有人没等广播关闭就发连接请求,结果芯片死机了。
  2. CRC计算错误:如果你自己实现LL层,CRC的初始值一定要设对。BLE规定CRC初始值是0x555555,不是0x000000。
  3. 白名单与隐私地址:如果设备用了随机地址(Privacy功能),白名单里存的地址会变。这时候需要用「解析列表」配合白名单使用。

嗯,LL层的内容就讲到这里。这些知识点,你在实际开发中一定会遇到。记住一句话:LL层是BLE的基石,搞懂了它,上层协议就是锦上添花


公众号:蓝海资料掘金营,微信deep3321