BLE 蓝牙连接参数详解
学习一下蓝牙连接参数相关知识 ...... 矜辰所致
前言
在我们学习蓝牙示例的时候,只要是从机示例,在最初初始化的时候都会设置两个参数 :GAPROLE_MIN_CONN_INTERVAL
和 GAPROLE_MAX_CONN_INTERVAL
,最小连接间隔和最大连接间隔,它们属于蓝牙的连接参数,那么它们在蓝牙协议中究竟有何意义,与他们相关联的参数还有哪些?本文我们就来了解一下 Ble 蓝牙连接参数相关知识。
沁恒微 RISC-V 蓝牙专栏目录:【导航】沁恒微 RISC-V 蓝牙 入门教程目录 【快速跳转】
❤️
蓝牙篇系列相关博文:
CH585 蓝牙 示例工程 Central 全解析
.
我是矜辰所致,全网同名,尽量用心写好每一系列文章,不浮夸,不将就,认真对待学知识的我们,矜辰所致,金石为开!
目录
- 前言
- 一、 基础介绍
- 1.1 基本概念
- 1.1.1 连接间隔
- 1.1.2 从设备延迟
- 1.1.3 监督超时
- 1.2 连接参数更新
- 1.3 关于保活
- 1.4 连接间隔的实际时间
- 1.4.1 连接间隔的确定
- 1.4.1 连接间隔时间是多少?
- 1.4.2 为什么 Ble 协议需要范围间隔
- 二、 CH585 示例体现
- 2.1 初始化
- 2.2 参数更新
- 2.2.1 从机
- 2.2.2 主机
- 2.3 更新结果查看
- 2.4 更新流程
- 结语
一、 基础介绍
连接参数,连接连接,顾名思义,只有 Ble 蓝牙建立了连接才会生效的参数,用于控制从机(Peripheral)与主机(Central)之间的通信频率。
1.1 基本概念
在沁恒微官方 EVT 中示例 BLE 文件夹下面有一份 《沁恒低功耗蓝牙软件开发参考手册》,在低功耗蓝牙协议栈部分章节对连接参数有描述。我们这里也过一遍基本概念。
1.1.1 连接间隔
连接间隔(Connection Interval) 是指主机与从机之间每次通信的时间间隔。
低功耗蓝牙采用的是跳频方案,设备在特定的时间在特定的通道上发送和接收数据。两个设备的一次数据发送与接收成为一个连接事件。连接间隔则为两个连接事件之间的时间,
时间单位: 1.25ms (比如 100 → 125ms )
连接间隔的范围: 7.5ms~4s。
不同的应用可能会需要不同的连接间隔,连接间隔的影响:
连接间隔小 | 连接间隔大 |
---|---|
通信频繁,响应快 | 通信稀疏,响应慢 |
功耗高 | 功耗低 |
适合高吞吐量应用(如OTA、音频) | 适合低功耗应用(如传感器定时上报) |
1.1.2 从设备延迟
从设备延迟(SlaveLatency)是指 从设备可以跳过的最大事件数(就是可以跳过几次间隔不回响应)。
此参数可以让从机跳过部分连接事件。如果设备没有数据需要用发送,那么从机延时可以跳过连接事件并在连接事件期间停止射频,从而降低功耗。
从设备延迟范围: 0~499 ,但需保证有效连接间隔小于16s。
比如文档示例如下:
1.1.3 监督超时
监督超时(SupervisionTime-out) 两个有效连接事件间的最大时间。超过这个时间没有收到包,则认为连接断开,设备退回未连接状态。
时间单位: 10 ms
监督超时的范围: 10(100ms)~3200(32s)。
超时时间必须大于有效连接间隔。
必须满足:
timeout × 10 ms > (1 + SlaveLatency) × ConnectionInterval × 1.25 ms
1.2 连接参数更新
在主机和从机连接以后,是可以更新上面的几个连接参数的,但是都需要协商,在官方文档中又说到 “在一些应用中,从机可能需要在连接期间根据应用程序更改连接参数。从机可以将连接参数更新请求发送至主机以更新连接参数。”
官方只说到,从机更改参数,但是主机也可以更改连接参数。
在主机示例工程中,使用下面函数更新参数:
/*** @brief Update the link connection parameters.** @param connHandle - connection handle* @param connIntervalMin - minimum connection interval in 1.25ms units* @param connIntervalMax - maximum connection interval in 1.25ms units* @param connLatency - number of LL latency connection events* @param connTimeout - connection timeout in 10ms units** @return SUCCESS: Connection update started started.<BR>* bleIncorrectMode: No connection to update.<BR>*/
extern bStatus_t GAPRole_UpdateLink( uint16_t connHandle, uint16_t connIntervalMin,uint16_t connIntervalMax, uint16_t connLatency, uint16_t connTimeout );
在从机示例工程中,使用下面函数更新参数:
/*** @brief Update the parameters of an existing connection** @param connHandle - the connection Handle* @param connIntervalMin - minimum connection interval in 1.25ms units* @param connIntervalMax - maximum connection interval in 1.25ms units* @param latency - the new slave latency* @param connTimeout - the new timeout value* @param taskId - taskID will recv L2CAP_SIGNAL_EVENT message** @return SUCCESS, bleNotConnected or bleInvalidRange*/
extern bStatus_t GAPRole_PeripheralConnParamUpdateReq( uint16_t connHandle, uint16_t connIntervalMin,uint16_t connIntervalMax, uint16_t latency, uint16_t connTimeout, uint8_t taskId );
更新包含以下参数:最小连接间隔、最大连接间隔、从设备延迟、监督超时
维度 | PeripheralConnParamUpdateReq | UpdateLink |
---|---|---|
谁调用 | 仅 Peripheral 角色 | 仅 Central 角色 |
底层行为 | 发 请求 → 等主机 Accept/Rsp → 再生效 | 发 UPDATE_IND → 立即生效(或对端拒) |
协商阶段 | ✅ 有,从机向主机协商 | ❌ 无,直接通知 |
从机能否拒绝 | ✅ 主机可拒,请求不生效 | ✅ 底层自动拒(若超范围),不断线 |
返回值 | SUCCESS / bleNotConnected / bleInvalidRange | SUCCESS /bleIncorrectMode |
结果通知 | 通过 peripheralParamUpdateCB() 回调 | Central 无回调;通过事件GAP_LINK_PARAM_UPDATE_EVENT 返回 |
1.3 关于保活
建立连接以后, 保活永远是主机先发起,从机回应! 不管有没有从设备延迟,主机永远按每个简介间隔询问。
如果设定了从设备延迟,从机可以在期间不回应,但是主机必须按时出现询问。
1.4 连接间隔的实际时间
上面虽然我们知道了什么事连接间隔,但是在我们示例中,都需要定义两个时间:最小连接间隔和最大连接间隔,他是一个范围,那么连接上以后,他到低是多少时间呢?
1.4.1 连接间隔的确定
连接间隔是如何确定的,我们能够很直观的看到,从机设备都是需要给定最小最大范围的, 主机会在自己策略允许的前提下,挑一个落在从机区间里的值。
虽然是不可控的,但是也不是随机的,主机内部有自己的算法+用户体验策略,但它必须先把你给的区间扫一遍,主机会根据自己的性能定位在区间内挑一个 ≥ min 且 ≤ max 的 1.25ms 整数倍,且固定不变直到下一次更新,考虑的方面比如:
- 自己系统性能档位(iOS ≥15 ms、音频 20 ms、鼠标 30 ms …)
- 多从机排班(避开已有设备的跳频点)
- 功耗策略(前台 App 要快、后台要省)
- Wi-Fi 共存(避开 100/200 ms 倍数)
连接间隔的确定 是主机根据自身策略做的确定性选择,只是从机无法预知具体会选哪一点。
如果想确定一个准确的时间间隔, 从机可以把最小连接间隔和最大连接间隔设定为相同的时间,这样就把连接间隔给固定下来。但是呢主机有可能拒绝的,这个要看主机自己的配置和状态,比如自己多设备从图,占用了某些固定设备的间隔等等。
1.4.1 连接间隔时间是多少?
上面说了实际的连接间隔,会在从机和主机建立连接以后由主机确定,但是这个时间是多少,主机并不会主动告诉从机,但是在示例中,从机可以通过自己注册的回调函数peripheralParamUpdateCB
查看:
static void peripheralParamUpdateCB(uint16_t connHandle, uint16_t connInterval,uint16_t connSlaveLatency, uint16_t connTimeout)
{if(connHandle == peripheralConnList.connHandle){peripheralConnList.connInterval = connInterval;peripheralConnList.connSlaveLatency = connSlaveLatency;peripheralConnList.connTimeout = connTimeout;PRINT("Update %x - Int %x \n", connHandle, connInterval);}else{PRINT("ERR..\n");}
}
1.4.2 为什么 Ble 协议需要范围间隔
为什么连接间隔不能是一个固定值,反正从机确定,主机按照固定值连接就好了。
其实上面也说到了,如果主机连接多个从机,大家有冲突的话就会有设备连接不上了,而且这样做是为了在 “用户体验”、“射频共存” 和 “功耗” 三者之间留动态调节空间。
比如一些常用的设备:
设备 | 从机声明区间 | 手机实际分配 |
---|---|---|
耳机 | 6 – 12 | 10 ms |
鼠标 | 8 – 50 | 30 ms |
温湿度计 | 100 – 2000 | 1 s |
手环 | 40 – 200 | 200 ms |
手机把四段区间做 TDM(时分复用),让不同设备错开跳频点,避免撞包。
另外,苹果/安卓设备有 “硬门槛” :
iOS ≥ 15 ms(HID 除外)
某些 Android 旧机 ≤ 24 ms 会触发 Wi-Fi 共存模块报警
如果从机写死 10 ms,iPhone 直接砍掉 15 ms,
此时协议栈需要再挑一个“离你最近且我合法”的值;
有区间就能 “向上取整”,没区间就只能连不上。
二、 CH585 示例体现
下面来结合实际代码,看看上面的这些参数在程序中用到的地方:
2.1 初始化
在从机例程 peripheral
中,有4个宏定义,分别对应了最小连接间隔,最大连接间隔,从设备延迟,监督超时:
// Minimum connection interval (units of 1.25ms, 6=7.5ms)
//最小连接间隔
#define DEFAULT_DESIRED_MIN_CONN_INTERVAL 6// Maximum connection interval (units of 1.25ms, 100=125ms)
//最大连接间隔
#define DEFAULT_DESIRED_MAX_CONN_INTERVAL 100// Slave latency to use parameter update
//从设备延迟,默认不延迟
#define DEFAULT_DESIRED_SLAVE_LATENCY 0// Supervision timeout value (units of 10ms, 100=1s)
//监督超时 1S 超时
#define DEFAULT_DESIRED_CONN_TIMEOUT 100
但是初始化的时候,从机只能确实最小连接间隔和最大连接间隔,和从设备延迟(默认为0)。
连接建立前从机无法把监督超时直接塞进广播数据(广播里没有 AD Type
能放 SupervisionTime-out),
上面没有设置的从机设备延迟,默认为0 ,如果从机不设置最小最大值就使用默认值,官方默认值如下:
//!< Minimum Connection Interval to allow (n * 1.25ms).
//Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80).
//Read/Write. Size is uint16_t.
//Default is 7.5 milliseconds (0x0006).
#define GAPROLE_MIN_CONN_INTERVAL 0x311
//!< Maximum Connection Interval to allow (n * 1.25ms).
//Range: 7.5 msec to 4 seconds (0x0006 to 0x0C80).
//Read/Write. Size is uint16_t.
//Default is 4 seconds (0x0C80).
#define GAPROLE_MAX_CONN_INTERVAL 0x312
主机初始化会有监督超时参数:
监督超时它只在连接建立时由主机设置,从机只能接受或拒连,事后才能通过更新请求去改。
2.2 参数更新
对于我们主机和从机,都可以发起参数更新,从机是协商请求,主机类似于下达通知。
2.2.1 从机
从机使用GAPRole_PeripheralConnParamUpdateReq
更新连接参数,在示例中,步骤如下:
//启动任务事件
tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT, SBP_PARAM_UPDATE_DELAY);//执行事件
if(events & SBP_PARAM_UPDATE_EVT){// Send connect param update request// When the current connection parameters already meet the requirements for update, return 0x18(InvalidRange)GAPRole_PeripheralConnParamUpdateReq(peripheralConnList.connHandle,DEFAULT_DESIRED_MIN_CONN_INTERVAL,DEFAULT_DESIRED_MAX_CONN_INTERVAL,DEFAULT_DESIRED_SLAVE_LATENCY,DEFAULT_DESIRED_CONN_TIMEOUT,Peripheral_TaskID);return (events ^ SBP_PARAM_UPDATE_EVT);}
2.2.2 主机
主机使用GAPRole_UpdateLink
更新连接参数,在示例中,步骤如下:
//启动任务事件
tmos_start_task(centralTaskId, START_PARAM_UPDATE_EVT, DEFAULT_PARAM_UPDATE_DELAY);//执行事件
if(events & START_PARAM_UPDATE_EVT)
{// start connect parameter updateGAPRole_UpdateLink(centralConnHandle,DEFAULT_UPDATE_MIN_CONN_INTERVAL,DEFAULT_UPDATE_MAX_CONN_INTERVAL,DEFAULT_UPDATE_SLAVE_LATENCY,DEFAULT_UPDATE_CONN_TIMEOUT);return (events ^ START_PARAM_UPDATE_EVT);
}
2.3 更新结果查看
这里直接在最前面说清楚:
从机设备 连接参数更新成功是会触发系统注册的回调函数peripheralParamUpdateCB
,在回调函数中处理。
主机设备 连接参数更新成功是在任务事件里处理:GAP_LINK_PARAM_UPDATE_EVENT
。
角色 | 接收方式 | 所在文件 | 事件/回调名 |
---|---|---|---|
Peripheral | 回调函数 | peripheral_main.c | peripheralParamUpdateCB() |
Central | 任务事件 | central_main.c | GAP_LINK_PARAM_UPDATE_EVENT |
在 peripheral 角色 下,CH585 的官方库固定走回调指针,根本不会把
GAP_LINK_PARAM_UPDATE_EVENT
塞进任务队列,从机想拿结果必须注册回调函数。
主机为什么走任务事件不走回调函数呢?
因为中心设备要同时管理多条连接,如果每条连接都开一个回调指针,每一个都需要占用 RAM 空间,浪费RAM空间,而且后期操作的时候不方便管理。
这里额外说明一下,对于主机连接多个从机:
// 主机保存着 8 条连接的上下文,参数更新
for (i = 0; i < MAX_CONNECTIONS; i++) {if (centralConnList[i].state == GAPROLE_CONNECTED &¢ralConnList[i].paramUpdateNeeded) {// 只更新这一条从机GAPRole_UpdateLink(centralConnList[i].connHandle,newMin, newMax, newLatency, newTimeout);centralConnList[i].paramUpdateNeeded = FALSE;}
}//结果
case GAP_LINK_PARAM_UPDATE_EVENT:p = (gapLinkUpdateEvent_t *)pMsg;// p->connHandle 告诉你“到底是哪条连接”PRINT("从机 %04X 新间隔 %d ms\n", p->connHandle, p->connInterval);break;
2.4 更新流程
上面我们知道了,主机和从机都有自己的更新成功接收的地方。
那么它们自己发出的更新请求,自己是否会触发回调或者事件呢?
其实这个是异步的,这个只要记住一点:不管参数更新请求是谁先发,只要通过协议栈的协商确定通过后,库就会通过事件/回调告诉应用层 “参数已换” 。
比如:
主机发送GAPRole_UpdateLink
-> 从机接收到并且同意(协议栈自动完成) -> 同一连接事件末尾:主机进GAP_LINK_PARAM_UPDATE_EVENT
同时从机进 peripheralParamUpdateCB()
。
从机发送GAPRole_PeripheralConnParamUpdateReq
-> 主机接收到并且同意(协议栈自动完成)-> 同一连接事件末尾:主机进GAP_LINK_PARAM_UPDATE_EVENT
同时从机进 peripheralParamUpdateCB()
。
中间如果不同意,两边都不会发生。
结语
到这里,关于 BLE 蓝牙连接参数的内容基本全部讲清楚了,通过本文的学习,大家在以后应用中应该能够根据自己的应用适当的设置对应的参数。
当然,博主也是学习阶段,通过示例代码,以及参考了前辈们的文章,对于主机发起的参数更新请求,除了示例网上好像没有太多解释,如果文章中有错误,希望大家指出!
好了,本文就到这里,谢谢大家!