4、链路层(LL)基础:状态机、数据包与过滤策略

链路层,也就是咱们常说的 LL 层,是蓝牙协议栈里最核心的模块之一。它直接跑在物理层之上,负责设备之间的连接建立、数据收发和功耗控制。说白了,没有 LL 层,两个蓝牙设备连最基本的“握手”都做不到。

我个人习惯把 LL 层看作一个“有限状态机”——设备在任一时刻只能处于一种状态,状态之间通过特定事件跳转。理解了这个状态机,你就掌握了蓝牙通信的骨架。

4.1 链路层状态机

蓝牙 LL 层定义了 5 种基本状态:Standby、Advertising、Scanning、Initiating 和 Connection。它们之间的关系可以用下面这张图来概括。

核心要点:所有蓝牙通信都从 Standby 开始,最终要么回到 Standby,要么进入 Connection。

蓝牙链路层状态机 Standby Advertising Scanning Initiating Connection 进入广播 进入扫描 发起连接 收到连接请求 连接成功 断开/超时 停止扫描 停止广播

4.2 各状态详解

4.2.1 Standby(待机状态)

这是设备的初始状态和默认状态。此时设备不发送任何数据,也不接收任何数据。功耗最低,但什么也干不了。所有其他状态都可以直接回到 Standby。

4.2.2 Advertising(广播状态)

设备在广播信道上周期性发送广播包。广播包可以包含设备名称、服务 UUID、厂商自定义数据等。我遇到过不少初学者把广播包塞得满满当当,结果导致连接建立变慢——广播包越长,被其他设备扫描到的概率越低。

经验之谈:广播间隔建议在 20ms 到 10.24s 之间。间隔越短,被发现越快,但功耗也越高。做低功耗产品时,我一般把间隔设在 100ms 以上。

4.2.3 Scanning(扫描状态)

扫描设备在广播信道上监听广播包。扫描分为两种:

  • 被动扫描:只收不发,不回复任何内容。
  • 主动扫描:收到广播包后,会发送扫描请求,获取更详细的广播数据。

嗯,这里要注意:主动扫描会多一次空中交互,功耗更高,但能拿到更多信息。做 Beacon 类产品时,我通常用被动扫描就够了。

4.2.4 Initiating(发起连接状态)

设备在广播信道上监听特定设备的广播包,一旦收到就发送连接请求。这个状态是连接建立的“临门一脚”。

4.2.5 Connection(连接状态)

连接建立后,设备进入连接状态。此时双方在数据信道上按约定的时序收发数据。连接状态又分为:

  • Master Role(主设备):决定连接时序,控制数据发送时机。
  • Slave Role(从设备):跟随主设备的时序,在指定时间窗口内收发数据。

4.3 空中数据包格式

蓝牙 LL 层的数据包结构非常固定。每个包由 4 个部分组成:

字段 长度 说明
Preamble 1 字节 前导码,用于接收端同步时钟。固定为 0xAA 或 0x55
Access Address 4 字节 接入地址。广播包固定为 0x8E89BED6;数据包使用随机生成的地址
PDU 2~257 字节 协议数据单元,包含实际数据和控制信息
CRC 3 字节 循环冗余校验,用于检测数据完整性

避坑指南:我曾经在一个项目中,因为 Access Address 配置错误,导致两个设备明明在同一个房间,却死活连不上。后来用抓包工具一看,数据包的 Access Address 和广播包的完全不一样——接收端直接丢弃了所有包。记住:广播包的 Access Address 是固定的,数据包的必须由主设备在连接建立时分配。

4.3.1 PDU 详解

PDU 是数据包的核心。它又分为:

  • 广播信道 PDU:用于广播、扫描和发起连接。包含 ADV_IND、SCAN_REQ、CONNECT_REQ 等类型。
  • 数据信道 PDU:用于连接状态下的数据收发。包含 LL_DATA 和 LL_CONTROL 两种。

一个典型的广播包 PDU 结构如下:

广播信道 PDU 格式:
+--------+--------+------------------+
| 头(2B) | 负载   | 可选(如扫描响应) |
+--------+--------+------------------+

头字段:
- PDU 类型 (4 bit)
- RFU (2 bit)
- ChSel (1 bit)
- TxAdd (1 bit)
- RxAdd (1 bit)
- 长度 (8 bit)
- RFU (3 bit)

4.4 白名单与过滤策略

白名单是 LL 层提供的一种硬件级过滤机制。说白了,就是告诉蓝牙芯片:我只跟名单里的设备通信,其他的统统忽略。

4.4.1 白名单的作用

  • 减少干扰:只处理目标设备的包,降低 CPU 负载。
  • 提升安全性:防止未授权设备接入。
  • 降低功耗:不需要的包直接丢弃,芯片不用唤醒。

4.4.2 白名单的配置

白名单可以基于设备地址(BD_ADDR)或地址类型(Public/Random)进行过滤。配置时通常需要:

  1. 将目标设备的地址写入白名单列表。
  2. 设置过滤策略(如:只接受白名单内的广播包)。
  3. 启动广播或扫描时启用白名单。

实际案例:我在做智能门锁项目时,门锁作为从设备,只接受手机 App 的连接。我把手机的蓝牙地址写入门锁的白名单,其他设备发来的连接请求全部被 LL 层自动丢弃。这样既省电又安全。

4.4.3 过滤策略对比

策略 描述 适用场景
无过滤 处理所有收到的包 调试阶段、通用扫描器
白名单过滤 只处理白名单内的设备 配对设备、私有网络
地址类型过滤 只处理 Public 或 Random 地址 区分设备类型
RSSI 过滤 只处理信号强度高于阈值的包 近距离触发、防误触

小技巧:白名单的条目数有限(通常 8~32 条)。如果你的设备需要连接多个设备,可以考虑用软件层做二次过滤,或者使用 Resolvable Private Address(可解析私有地址)配合 IRK 进行动态匹配。

好了,链路层的基础就讲到这里。记住状态机的流转、数据包的格式、白名单的用法,后面讲连接建立和 LE Audio 时,你会反复用到这些知识。