浅谈蓝牙的连接基石
本篇文章我们来聊聊蓝牙是怎么连接的,就拿手机和耳机连接来说,我们知道在手机蓝牙设置列表搜索到耳机后,点击就可以进行连接了,但这在蓝牙芯片是怎么工作的呢,我们将聚焦在最关键的两个底层环节:设备发现(查询/查询扫描)和连接发起(寻呼/寻呼扫描)。这就像是在派对上,你先环顾四周看看有谁(搜索到耳机),然后锁定目标,径直走过去打招呼(连接耳机)。
第一部分:查询与查询扫描 - “有人吗?”
这个过程的目标是:主设备如何在一个完全未知的环境中,找到那些愿意被发现的从设备。
1. 主设备行为 - 发起查询
目标:发送查询请求,并收集响应。
HCI 命令:
嵌入式工程师在主机上会调用一个核心的HCI命令来启动这个过程:
HCI_Inquiry
这个命令通常需要参数:
1. LAP:查询访问码,固定为0x9E8B33,像是一个广播频道的通用ID。
2. Inquiry_Length:一次查询持续的最长时间(如10.24秒)。时间越长,发现设备的概率越高,功耗也越大。
3.Num_Responses:期望收到的最大响应数量,达到后提前结束查询。
控制器行为:
1.切换状态:蓝牙控制器(芯片)收到HCI_Inquiry 命令后,会进入 查询状态。
2.发送ID Packet:在接下来的时间内,控制器按照 查询跳频序列,在32个信道上轮流停留足够长的时间(通常是1.28秒的整数倍)。在每个信道上,它都会重复发送一种非常简单的数据包——ID Packet。这个包只包含两个东西:LAP 和 主设备自身的时钟估计。它就像一个灯塔,在特定频率上闪烁“有人吗?”的信号。
3.监听响应:发送完ID包后,主设备会短暂切换到接收模式,监听来自从设备的响应。
空口包展示:
2. 从设备行为 - 查询扫描
目标:监听查询请求,并做出响应。
HCI 命令:
要让一个从设备能够被发现,开发者需要将其设置为可发现模式。这通常通过以下命令实现:
HCI_Write_Scan_Enable
参数设置为 0x03( Inquiry Scan enabled, Page Scan enabled)。
控制器行为:
FHS Packet是什么? 这是从设备的“身份证”,包含了最关键的信息:
BD_ADDR:从设备的48位蓝牙地址。
Class of Device:设备类型(如手机、耳机、电脑)。
本地时钟:从设备自身的时钟信息,这对后续的寻呼过程至关重要!
1.周期性监听:从设备不会一直监听,那样太耗电。它会周期性地(例如,每1.28秒或2.56秒)进入 查询扫描状态,持续一个窗口时间(如11.25毫秒)。
2.监听ID Packet:在查询扫描窗口内,控制器按照 查询响应跳频序列 在32个信道上进行扫描。当它监听到一个与通用查询LAP(0x9E8B33)匹配的ID包时,就知道有主设备在找它。
3.随机延迟与响应:为了避免多个从设备同时响应造成冲突,从设备会等待一个随机长度的时隙(0到639.375毫秒),然后跳频到下一个约定的信道,发送 FHS Packet 作为响应。
4. 查询过程的结束: 主设备在收到足够数量的响应(Num_Responses)或达到超时时间(Inquiry_Length)后,查询过程结束。控制器会向主机发送 HCI_Inquiry_Complete 事件,并附带所有收集到的从设备信息。
空口包展示:
第二部分:寻呼与寻呼扫描 - “你好,我们聊聊?
在查询阶段,主设备已经拿到了心仪从设备的 BD_ADDR 和 时钟信息。寻呼过程的目标就是:基于这些信息,快速与指定的从设备建立稳定的物理链路(ACL连接)。
1. 物理基础:时钟预测与同步
这是蓝牙最精妙的设计之一。每个蓝牙设备都有一个内部28位的自由运行的时钟。寻呼的核心思想是:主设备利用获取到的从设备时钟信息,预测从设备当前在哪个信道上“监听呼叫”,从而直接去那个频道上“精准敲门”。
同样,也有两套对应的跳频序列:
寻呼跳频序列:主设备使用,由从设备的 BD_ADDR 和 预测的时钟 计算得出。
寻呼扫描跳频序列:从设备使用,由自身的 BD_ADDR 和 本地时钟 计算得出。
2. 主设备行为 - 发起寻呼
目标:与指定BD_ADDR的从设备建立连接。
HCI 命令:
最直接的命令是:
HCI_Create_Connection
参数包括:从设备的 BD_ADDR、包类型、角色切换策略等。
控制器行为:
1.切换状态:控制器进入 寻呼状态。
2.预测与发送:控制器根据从设备的BD_ADDR和之前获取的时钟信息,预测从设备当前正在监听哪个寻呼扫描信道。然后,它就在这个预测的信道上发送 ID Packet。但这次,ID Packet里包含的不再是通用查询LAP,而是从设备BD_ADDR的低24位地址(LAP)!这是一个“点名呼叫”。
3.Trains机制:由于时钟预测可能存在微小偏差,主设备会在一系列(通常为16个)相关的信道上重复发送这个ID包,形成一个“呼叫序列”,确保从设备一定能听到。
4.等待并确认:发送后,主设备监听从设备的响应。一旦收到从设备的回应(一个包含自身BD_ADDR的ID包),主设备会立即发送一个 FHS Packet,告诉从设备:“我是主设备(我的BD_ADDR),这是我的时钟,以后我们用这个新的信道跳频序列通信”。
5.链路建立:从设备确认FHS包后,双方就切换到新的信道跳频序列,进入连接状态。此时,物理链路(ACL链路)已经建立。
空口包展示:
3. 从设备行为 - 寻呼扫描
目标:监听是否有主设备想与自己建立连接。
HCI 命令:
同样由 HCI_Write_Scan_Enable 命令控制(参数通常为 0x03)。
控制器行为:
1.周期性监听:与查询扫描类似,从设备也周期性地进入 寻呼扫描状态。
2.监听专属ID Packet:在扫描时,它计算自身的寻呼扫描序列,并监听包含 自身LAP 的ID包。
3.响应与同步:一旦听到主设备在“喊自己的名字”,从设备会立即回应一个ID包,并开始与主设备交换时钟信息,最终同步到主设备发来的新跳频序列上,进入连接状态,并默认成为从角色。
4. 寻呼扫描的模式
从设备可以配置不同的寻呼扫描模式,以在响应速度和功耗之间取得平衡:
R0:强制监听较长时间,响应速度慢,但更可靠。
R1:标准模式,在速度和功耗间平衡。
R2:监听窗口很短,响应最快,但功耗最高。
空口包展示:
总结与流程图
让我们用一张图来清晰地展示这两个过程的交互: