☰
BLE 连接建立与连接参数详解
2026/9/30 9:38:32 网站建设 项目流程

BLE 连接建立与连接参数详解:从 CONNECT_IND 到断开

  • 一、连接建立:CONNECT_IND 与第一次握手窗口
    • 1.1 CONNECT_IND 逐字段解析
    • 1.2 Access Address 与 CRC Init:连接前后的分界线
    • 1.3 时间单位:1.25 ms 与 10 ms
    • 1.4 第一次握手窗口:Window Size / Offset
  • 二、连接事件循环
    • 2.1 一问一答:T_IFS = 150 μs
    • 2.2 Empty PDU:给从机"说话的机会"
    • 2.3 MD 位:连接事件可以提前结束
    • 2.4 Slave Latency:从机可以"睡过"几个事件
  • 三、连接后的六次协商
    • 3.1 Feature Exchange:互相通报能力
    • 3.2 Version Exchange:实际版本取双方较小值
    • 3.3 DLE 协商:一个数据包能装多少字节
    • 3.4 连接参数更新:30 ms → 7.5 ms
      • 3.4.1 为什么切换需要 Window Size/Offset + Instant?
      • 3.4.2 为什么手机要加快?
      • 3.4.3 Offset 和 Preferred Periodicity 是干嘛的?
    • 3.5 PHY 更新:切到 2M 或 Coded
    • 3.6 Channel Map 更新:自适应跳频(AFH)
  • 四、断开连接:LL_TERMINATE_IND

全文脉络:

广播阶段 连接建立 连接后的协商(LL 控制过程) ────────── ───────── ───────────────────────── ADV_IND ──► CONNECT_IND ──► ① Feature Exchange(特性交换) (广播者) (发起者/手机) ② Version Exchange(版本交换) ③ DLE 协商(数据长度扩展) SCAN_RSP ◄── ④ Connection Parameter Update (可选) ▼ ⑤ PHY Update(切 2M / Coded) 连接事件循环 ⑥ Channel Map Update(AFH 自适应跳频) (每 connInterval 一次) ⑦ Terminate(断开连接) │ ▼ GATT 层:服务发现 / 订阅 / 通知

一句话概括:CONNECT_IND把连接参数一次性定下来,之后双方按connInterval周期碰头;碰头过程中再通过 LL 控制包不断"谈判"优化参数。

一、连接建立:CONNECT_IND 与第一次握手窗口

CONNECT_IND是手机(Initiator / Central)向板子(Advertiser / Peripheral)发出的连接请求。
它的 Payload 由三段组成:InitA(6) + AdvA(6) + LLData(22)——参数全在那 22 字节的 LLData 里。

1.1 CONNECT_IND 逐字段解析

字段抓到的值含义
Initiator Address4e:b2:86:dd:a2:3d手机的 MAC(随机地址)
Advertising Address7c:e8:b1:aa:da:36板子 的 MAC
Access Address0x742e6ed6★ 连接后的新同步字,后续数据包全用它
CRC Init0xb248a4连接后 CRC 的初始值
Window Size2(2.5 ms)第一个传输窗口的大小
Window Offset16(20 ms)从 CONNECT_IND 结束到第一个窗口的偏移
Interval24(30 ms)连接间隔,双方每 30 ms 通信一次
Latency0从机可以跳过的连接事件数
Timeout500(5000 ms)连接超时,5 秒没收到就断开
Channel Map1fff000000★ 可用数据信道图
Hop5跳频步长,规范要求是5~16的随机值
SCA31~50 ppm中央设备(手机)的睡眠时钟精度


LLData 的完整字段布局:

AA CRCInit WinSize WinOffset Interval Latency Timeout ChM Hop SCA (4 octets) (3 octets) (1 oct) (2 oct) (2 oct) (2 oct) (2 oct) (5 oct) (5 bits) (3 bits)

1.2 Access Address 与 CRC Init:连接前后的分界线

广播阶段(信道 37/38/39)连接阶段(信道 0~36)
Access Address固定0x8E89BED6随机 32-bit,由 CONNECT_IND 指定
CRC 初值固定0x555555随机值,由 CONNECT_IND 的 CRCInit 指定

两者每次连接都不同—— 所以它们天然可以当作 “这是哪一次连接” 的指纹。

1.3 时间单位:1.25 ms 与 10 ms

BLE 物理层时隙是625 μs,连接参数以两个时隙为基本单位:

1.25 ms = 2 × 625 μs
参数单位抓到的原始值实际时间
transmitWindowSize1.25 ms22 × 1.25 =2.5 ms✅
transmitWindowOffset1.25 ms1616 × 1.25 =20 ms✅
connInterval1.25 ms2424 × 1.25 =30 ms✅
connSupervisionTimeout10 ms500500 × 10 =5000 ms = 5 s✅
connSlaveLatency无单位(次数)00 次

⚠️唯一例外:Timeout 的步长是10 ms,不是 1.25 ms。这是最容易记混的一个。

1.4 第一次握手窗口:Window Size / Offset

连接建立的瞬间有个根本问题:广播者和发起者的时钟不同步。

手机发完 CONNECT_IND 后,从机并不知道手机会在精确的哪一刻发来第一个数据包。所以 BLE 设计了一个容差窗口:

手机说:“我会在 CONNECT_IND 结束后约20 ms开始,在接下来的2.5 ms窗口内,找个时间发第一个包。你(从机)在那 2.5 ms 里把接收机打开等着。”

要点说明
只用一次Window Size/Offset只用于第一个连接事件。之后双方已同步,按精确 Interval 走
从机怎么做收到 CONNECT_IND → 等约 20 ms → 开接收机持续 2.5 ms → 收到就同步成功
窗口内没收到从机在下一个 Interval(30 ms 后)再开一次窗口,继续等

二、连接事件循环

连接建立后,主从周期性碰头。每一次碰头的机会就叫一个Connection Event:

|<-- connInterval 30ms -->|<-- connInterval 30ms -->| ▲ ▲ 连接事件 #1 连接事件 #2

在每个事件里:主机先发一个包 → 从机回一个包 → 如果还有数据就继续收发 → 双方都没数据了就关闭射频睡觉,等下一个 Interval。

2.1 一问一答:T_IFS = 150 μs

|<──────── 一个 Connection Event ────────>| 主机 ────[包]────► 从机 ← 主机发起(必须有) T_IFS=150μs 主机 ◄───[包]───── 从机 ← 从机回应(必须有)

规范原文:“The Inter Frame Space is designatedT_IFSand shall be150 µs.”
从机收到后必须在 150 μs 内回应,否则超时重传。

2.2 Empty PDU:给从机"说话的机会"

核心规则:BLE 连接建立后,从机永远不能主动发起通信,必须等主机先发包,然后在 150 μs 内回应。

所以就有了 Empty PDU:

主机 ────[Empty PDU]────► 从机 ← 主机:"我没啥要发的,你呢?" 主机 ◄───[心率 Notification]── 从机 ← 从机:"正好,我有数据!"

没有这个机制,从机有数据也只能干等着(BLE 不允许从机主动"打断"主机)。

💡 这也解释了为什么connInterval 直接决定了数据上报延迟。

2.3 MD 位:连接事件可以提前结束

数据信道 PDU 的 Header 里有个MD(More Data)位:MD = 1表示 “我还有数据,继续传”,MD = 0表示 “我没数据了”。

主机 ──[数据包 MD=1]──► 从机 主机 ◄─[数据包 MD=1]─── 从机 ← 还有数据,继续 主机 ──[数据包 MD=0]──► 从机 主机 ◄─[数据包 MD=0]─── 从机 ← 都没数据了 → 事件关闭 ┌──────────────────┐ │ 射频关闭,睡觉 │ │ 等下一个 30ms │ └──────────────────┘

所以连接事件不一定占满整个 Interval—— 传完就睡,这是 BLE 省电的关键之一。

2.4 Slave Latency:从机可以"睡过"几个事件

问题:如果每 30 ms 都要唤醒射频收发一次(哪怕只是空包),功耗很可观。
解决:允许从机跳过一些连接事件。

Latency = 0(每次都醒):

主机: ● ● ● ● ● ● ● ● 从机: ● ● ● ● ● ● ● ● ↑ ↑ ↑ ↑ ↑ ↑ ↑ ↑ 每次都醒,每次都收发(哪怕只是空包) 有效通信周期 = 30 ms 功耗:最高 延迟:最低

Latency = 4(可跳 4 次,每 5 个事件响应 1 次):

主机: ● ● ● ● ● ● ● ● ● ● ● ← 主机每个事件都发 从机: ● ● ● ← 从机只醒第 1、6、11... |<----- 150 ms ----->| (5 × 30 ms = 从机实际醒来的间隔) 从机少醒了 4/5 = 80% 功耗:大幅降低 延迟:最高 150 ms

关键理解:Slave Latency 不是"主机不发",而是"从机可以不听"。
主机每个连接事件照常发(否则连接就断了),从机可以睡过 N 个事件,只在第 N+1 个醒来收。

约束公式:

connSupervisionTimeout > (1 + connSlaveLatency) × connInterval × 2

Latency 设得太大,导致左边接近甚至超过 Timeout,连接就会被误判为超时断开。

从机什么时候不能睡?有数据要上报时(比如心率有新值)必须立刻醒来发;主机发了需要响应的包也必须回。
所以Latency 是"没事的时候可以睡",不是"强制睡"。

工程上怎么选?核心权衡永远是「延迟 ↔ 功耗」:

场景推荐参数
心率手环(省电优先,延迟不敏感)Interval 大(100 ms ~ 1 s)+ Latency 高(4~20)
游戏手柄 / 遥控器(低延迟)Interval 小(7.5~15 ms)+ Latency = 0
固件升级 OTA(要吞吐)Interval 小(7.5 ms)+ Latency 0 + 开 DLE
传感器定时上报Interval 中等 + Latency 适中

三、连接后的六次协商

连接建立后,双方会按需进行一系列 LL 控制过程。它们有一个共同的套路:

① REQ ── 发起方探测/提议 ② RSP ── 回应方答复 ③ IND ── 中央设备拍板,并指定 Instant(从第几个连接事件起生效)

(只有"必须双方同时切换"的过程才需要第 ③ 步,比如 PHY 和连接参数。)

3.1 Feature Exchange:互相通报能力

连接建立后第一个协商过程:


FeatureSet = 0xf9ff,逐位解读:

Bit特性值说明
0LE Encryption✅支持加密(配对 / bonding 的基础)
1Connection Parameters Request✅支持连接参数更新流程
2Extended Reject Indication✅拒绝时能带原因码
3Peripheral Initiated Features Exchange✅从机可以主动发起特性交换
4LE Ping✅链路层保活探测
5LE Data Packet Length Extension (DLE)✅数据长度扩展
6LL Privacy✅链路层隐私(随机地址)
7Extended Scanner Filter Policies✅扩展扫描过滤策略
8LE 2M PHY✅2M 速率(需切换才生效)
9Stable Modulation Index - Tx❌发射机稳定调制指数
10Stable Modulation Index - Rx❌接收机稳定调制指数
11LE Coded PHY✅长距离模式(BLE 5.0)
12LE Extended Advertising✅扩展广播
13LE Periodic Advertising✅周期性广播
14Channel Selection Algorithm #2✅CSA #2
15LE Power Class 1✅支持 Class 1 发射功率

注意 bit 3 = 1,所以从机才会主动回一个LL_PERIPHERAL_FEATURE_REQ。
另外,LL_FEATURE_RSP的 FeatureSet 字段 =发送这个 RSP 的那一方自己支持的特性。

3.2 Version Exchange:实际版本取双方较小值

手机: BLE 6.0 板子: BLE 5.0 ──────────────── 有效版本: BLE 5.0

3.3 DLE 协商:一个数据包能装多少字节

DLE =Data Length Extension(数据长度扩展)。

无 DLE(BLE 4.0/4.1)有 DLE(BLE 4.2+)
数据信道 PDU 最大 Payload27 字节251 字节
单包有效数据(ATT 层)20 字节244 字节
传 1 KB 数据需要~51 个包~5 个包

协商用的四个字段:

字段含义
Max RX octets我作为接收方,最多能收的 payload 字节数
Max RX time我作为接收方,最多能收的射频时长
Max TX octets我作为发送方,最多能发的 payload 字节数
Max TX time我作为发送方,最多能发的射频时长

① 手机 → 板子


② 板子 → 手机


③ 手机 → 板子


④ 板子 → 手机


★ 关键公式:每个方向的有效长度

某方向的有效长度 = min( 对端的 MaxRX , 本端的 MaxTX )

套用到本次抓包:

手机 → 板子 = min(板子 MaxRX=251, 手机 MaxTX=27) = 27 字节 板子 → 手机 = min(手机 MaxRX=251, 板子 MaxTX=251) = 251 字节

结论:这次连接是「非对称」的—— 板子给手机发(心率通知)可以一次发 251 字节,手机给板子发(写特征值)一次只能 27 字节。

3.4 连接参数更新:30 ms → 7.5 ms

这是抓包里连接参数更新的完整流程(手机主动要求把连接加快):

手机 ── LL_CONNECTION_PARAM_REQ ──► 板子 "我想把连接间隔改成 7.5ms" 手机 ◄── LL_CONNECTION_PARAM_RSP ── 板子 "同意" 手机 ── LL_CONNECTION_UPDATE_IND ──► 板子 "从现在起第 17 个连接事件开始生效"

① LL_CONNECTION_PARAM_REQ(手机发起)

字段值含义
Interval Min / Max6 / 66 × 1.25 ms =7.5 ms
Latency0不跳过任何连接事件
Timeout500500 × 10 ms = 5000 ms
Preferred Periodicity0手机对事件偏移"无偏好"
Reference Connection Event Count6计算偏移的参考锚点
Offset 0~55, 0, 1, 3, 4, 65535建议的连接事件偏移(65535 = 无效)


② LL_CONNECTION_PARAM_RSP(板子回应)

大部分和 REQ 相同,只有Preferred Periodicity变成1(板子表示"我接受你的参数,并且偏好周期性为 1")。

板子接受了手机提议的 7.5 ms 间隔,所以 RSP 几乎照抄 REQ。
如果板子不接受,RSP 里会填不同的 Interval Min/Max—— 这时手机可以选择接受板子的提议,或者发起断开。


③ LL_CONNECTION_UPDATE_IND(手机下发更新指令)

字段值含义
Window Size1更新后的第一个事件窗口 = 1.25 ms
Window Offset5更新后的第一个事件偏移 = 6.25 ms
Interval6新连接间隔 = 7.5 ms
Latency0不变
Timeout5005000 ms,不变
Instant17第 17 个连接事件时切换新参数

3.4.1 为什么切换需要 Window Size/Offset + Instant?

参数从 30 ms 切到 7.5 ms,时序会发生跳变。切换的那一瞬间,双方需要一个 “重新对齐的窗口”:

  • Instant = 17:双方都从 CONNECT_IND 之后开始数连接事件,数到第 17 个时同时切换。这样不需要额外通信就能保证同步。
  • Window Size = 1(1.25 ms):板子在这个窗口内打开接收机,等待新参数的第一个包。

3.4.2 为什么手机要加快?

最可能的原因是:nRF Connect App 进入了活跃状态。

阶段典型 Interval场景
刚连接30~50 ms低功耗,后台维持
用户打开 App 交互7.5~15 ms低延迟,服务发现、读写
稳定传输数据7.5~30 ms根据应用调整
长时间空闲100~1000 ms超低功耗

反过来,很多产品连接成功后,从机会主动发起参数更新,把 Interval 拉大(100 ms ~ 1 s)、Latency 设成 4~10,进入省电模式。

3.4.3 Offset 和 Preferred Periodicity 是干嘛的?

这是给手机同时连接多个 BLE 设备时用的 —— 它希望这些设备的连接事件错开,不要撞在一起:

设备 A 的连接事件:t=0, 7.5ms, 15ms, ... 设备 B 的连接事件:t=2.5ms, 10ms, 17.5ms, ... ← 错开
  • Preferred Periodicity = 0:发起方(手机)没偏好
  • Preferred Periodicity = 1:从机希望按某个周期错开
  • Offset 0 ~ 5:具体偏置值列表,65535 表示 “这个选项无效”

本例的 Offset 列表是[5, 0, 1, 3, 4, 65535],说明有5 个有效偏置建议,单位 1.25 ms。
从LL_CONNECTION_UPDATE_IND的 Window Offset = 5 可以看出:主机最终接受了 Offset0 = 5(6.25 ms)。

3.5 PHY 更新:切到 2M 或 Coded

连接建立后想把 1M 换成 2M(或 Coded)

① LL_PHY_REQ(手机 → 板子)

字段值含义
TX PHYs0x07手机发送时支持 1M / 2M / Coded
RX PHYs0x02手机希望接收时只用 2M


② LL_PHY_RSP(板子 → 手机)

字段值含义
TX PHYs0x07板子发送时支持 1M / 2M / Coded
RX PHYs0x07板子接收时支持 1M / 2M / Coded


③ LL_PHY_UPDATE_IND(手机 → 板子,最终决定)

字段值含义
Central → Peripheral PHY0x02手机发给板子用2M
Peripheral → Central PHY0x02板子发给手机用2M
Instant60在第 60 个连接事件时切换 PHY

3.6 Channel Map 更新:自适应跳频(AFH)

手机(Central)告诉板子(Peripheral):

“新的可用数据信道图是0xffcff1f,从第 125 个连接事件开始生效。”

字段值含义
Control Opcode0x01LL_CHANNEL_MAP_IND
Channel Map0xffcff1f数据信道 0~36 的可用性位图
Instant125新信道图在第 125 个连接事件生效


Channel Map 的正确解码方法

① bit N ←→ 数据信道 N (N = 0 ~ 36) ② 1 = 可用(Used),0 = 不可用(Unused) ③ 字节按【小端】拼成一个整数(octet0 对应信道 0~7)

以0x0ffcffff1f(即0x0FFCFFFF1F,10 位十六进制 = 5 字节)为例,逐字节拆开:

字节(小端序)值二进制对应信道状态
octet0(最右字节)0x1F0001 11110~70~4 ✅ 可用;5~7 ❌
octet10xFF1111 11118~15全部 ✅
octet20xFF1111 111116~23全部 ✅
octet30xFC1111 110024~3126~31 ✅ 可用;24~25 ❌
octet4(最左字节)0x0F0000 111132~3632~35 ✅ 可用;36 ❌

四、断开连接:LL_TERMINATE_IND

字段值含义
LLID0x3Control PDU(链路层控制报文)
Control Opcode0x02LL_TERMINATE_IND
Error Code0x13Remote User Terminated Connection
Length2只有 Opcode + Error Code 两个字节


Error Code 0x13=Remote User Terminated Connection—— 有一方(手机或板子)的用户/应用层主动点了断开,然后通过这个链路层 PDU 通知对方。

注意:这里的 “Error” 不是"故障",BLE 里断开原因统一用这个字段表示。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询