【WCH蓝牙系列芯片】-基于CH592开发板—动态协商更新连接间隔
------------------------------------------------------------------------------------------------------------------------------------
连接间隔参数:在一个 BLE 连接中跳频机制需要被使用,让两个设备之间可以在一个特定的通道上进行数据收发,在一个特定的时间之后会跳到一个新的通道上,LL 层负责通道切换。这个设备收发数据就是连接事件。尽管没有应用程序数据需要收发,两个设备之间仍然会交换链路层数据来保持连接。连接间隔是两个连接事件之间的时间,使用一个单元值为1.25ms 的步进。连接间隔从最小值 6(7.5ms)到最大值 3200(4.0s)。不同的应用也许需要不同的连接间隔,如果一个长时间的连接间隔将会节约更多的功耗,因为设备可以在两个连接事件之间睡眠更长的时间。但是会导致数据发送不及时,如果有数据要发送那么只能够在下一次连接事件到来时才能被发送。
从机潜伏周期:这个参数描述了从机跳过连接事件的次数。可以让外围设备具有一定的灵活性,如果它不具有任何数据传送,它可以选择跳过连接事件,并保持睡眠,从而降低功耗。这一决定取决于外围设备,也可以成为从设备延迟,从设备能够忽略主设备的链接事件的最大值。比如:从设备延迟为 6 的话,那么从设备每隔 6 个连接事件可以监听到主设备发送来的数据包,如果从设备延迟为 0,从设备在每个连接事件都可以监听主设备的数据包
监督超时时间:这是两个成功的连接事件之间间隔的最大值。如果超过这个时间还未出现成功的连接事件,那么设备将会考虑失去连接,返回一个未连接状态。这个参数值使用 10ms一个单位。监督超时时间从最小 10(100ms)到最大 3200(32.0s)。同时超时时间必须大于有效连接事件。
有效连接事件时间 = 连接间隔 * (1 + 从机延迟值)
在某些情况下,蓝牙主机与蓝牙从机建立连接包含连接参数对从机是不利的。蓝牙从机可以在连接过程,可以主动请求蓝牙主机改变连接参数,设置连接参数的更新请求。这个请求是被协议栈的 L2CAP 处理的。这个更新请求的四个参数就是包含:最小连接间隔、最大连接间隔、从机潜伏周期、连接超时时间。这些值代表了从机针对连接的期望参数,连接间隔是以范围的形式提供的,当蓝牙主机接收到这个请求,主机有权利决定是接受还是拒绝这些参数,这些参数对通信质量和通信功耗有很大的影响。

// 建立连接后,延迟多久开始检查初始快速档 #define PARAM_INITIAL_DELAY MS1_TO_SYSTEM_TIME(1000) // 每个连接档位保持10秒 #define PARAM_MODE_HOLD_TIME MS1_TO_SYSTEM_TIME(10000) // 参数请求未成功时,每3秒重试一次 #define PARAM_RETRY_DELAY MS1_TO_SYSTEM_TIME(6000) // 每个档位最多请求5次 #define PARAM_MAX_RETRY 5 // 快速连接间隔:连接间隔单位为1.25ms // 8 × 1.25ms = 10ms // 12 × 1.25ms = 15ms #define PARAM_FAST_MIN_INTERVAL 8 #define PARAM_FAST_MAX_INTERVAL 12 #define PARAM_FAST_LATENCY 0 #define PARAM_FAST_TIMEOUT 600 // 6秒 // 慢速连接间隔: // 396 × 1.25ms = 495ms // 408 × 1.25ms = 510ms #define PARAM_SLOW_MIN_INTERVAL 300 #define PARAM_SLOW_MAX_INTERVAL 400 #define PARAM_SLOW_LATENCY 0 // 监督超时单位10ms: #define PARAM_SLOW_TIMEOUT 600 // 6秒 /* 整个动态过程允许的总范围 */ #define PARAM_ALLOWED_MIN_INTERVAL PARAM_FAST_MIN_INTERVAL #define PARAM_ALLOWED_MAX_INTERVAL PARAM_SLOW_MAX_INTERVAL typedef enum { PARAM_PHASE_IDLE = 0, // 刚连接,正在确认或请求初始快速档 PARAM_PHASE_REQUEST_INITIAL_FAST, // 快速档保持10秒 PARAM_PHASE_FAST_HOLD, // 请求慢速档 PARAM_PHASE_REQUEST_SLOW, // 慢速档保持10秒 PARAM_PHASE_SLOW_HOLD, // 请求重新回到快速档 PARAM_PHASE_REQUEST_FAST, // 本轮动态切换完成 PARAM_PHASE_FINISHED } paramPhase_t; static paramPhase_t paramPhase = PARAM_PHASE_IDLE; static uint8_t paramRetryCount = 0; //重试次数 static uint8_t peripheralIntervalInRange(uint16_t interval, uint16_t minInterval, uint16_t maxInterval); //判断主机最终采用的连接间隔是否落入目标范围 static uint8_t peripheralIntervalInRange(uint16_t interval,uint16_t minInterval, uint16_t maxInterval) { return ((interval >= minInterval) && (interval <= maxInterval)); } //确认当前实际连接间隔满足目标,停止重试,并进入下一个状态 static void peripheralParamTargetReached(void) { // 当前目标参数已经达到,不需要继续更新参数 tmos_stop_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT); paramRetryCount = 0; //清零重试次数 //状态机判断 switch(paramPhase) { case PARAM_PHASE_REQUEST_INITIAL_FAST: //刚连接,正在确认或请求初始快速档 { paramPhase = PARAM_PHASE_FAST_HOLD; //切换为保持 快速档保持10秒 tmos_stop_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT); //先停止 tmos_start_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT, PARAM_MODE_HOLD_TIME); //再启动 ,10秒的任务运行 PRINT("Initial fast interval reached; hold for 10 seconds\r\n"); break; } case PARAM_PHASE_REQUEST_SLOW: //慢速档达到 { paramPhase = PARAM_PHASE_SLOW_HOLD; // 慢速档保持10秒 tmos_stop_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT); tmos_start_task(Peripheral_TaskID,SBP_PARAM_SWITCH_EVT, PARAM_MODE_HOLD_TIME); //启动更新任务 PRINT("Slow interval reached; hold for 10 seconds\r\n"); break; } case PARAM_PHASE_REQUEST_FAST: // 请求重新回到快速档 { paramPhase = PARAM_PHASE_FINISHED; // 本轮动态切换完成 tmos_stop_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT); //停止任务事件 PRINT("Fast interval restored; dynamic sequence finished\r\n"); break; } default: break; } }
在设备成功连接之后,将连接间隔的参数改为快速挡位,也就是将最大连接间隔和最小连接间隔都调整到8和12,这样整体的连接间隔是在10MS到15MS之间。

/**********************发起协商,更新连接参数*****************************/ if(events & SBP_PARAM_UPDATE_EVT) { uint16_t minInterval; uint16_t maxInterval; uint8_t targetIsFast; bStatus_t ret; uint16_t targetLatency; uint16_t targetTimeout; if(peripheralConnList.connHandle == GAP_CONNHANDLE_INIT) //检查当前是否存在有效连接.如果没有连接 { paramPhase = PARAM_PHASE_IDLE; //状态机恢复为 IDLE paramRetryCount = 0; //清零重试次数 PRINT("Param update skipped: not connected\r\n"); return (events ^ SBP_PARAM_UPDATE_EVT); //清除本次任务 } // 根据当前阶段决定要请求哪一档参数 if((paramPhase == PARAM_PHASE_REQUEST_INITIAL_FAST) || (paramPhase == PARAM_PHASE_REQUEST_FAST)) //连接间隔快速挡位 { minInterval = PARAM_FAST_MIN_INTERVAL; //最小连接间隔 maxInterval = PARAM_FAST_MAX_INTERVAL; //最大连接间隔 targetLatency = PARAM_FAST_LATENCY; //从机潜伏周期 targetTimeout = PARAM_FAST_TIMEOUT; //超时时间 targetIsFast = TRUE; } else if(paramPhase == PARAM_PHASE_REQUEST_SLOW) //连接间隔慢速挡请求 { minInterval = PARAM_SLOW_MIN_INTERVAL; maxInterval = PARAM_SLOW_MAX_INTERVAL; targetLatency = PARAM_SLOW_LATENCY; targetTimeout = PARAM_SLOW_TIMEOUT; targetIsFast = FALSE; } else { return (events ^ SBP_PARAM_UPDATE_EVT); } PRINT("Param check: current=%d (%d ms), phase=%d, retry=%d\r\n", peripheralConnList.connInterval, peripheralConnList.connInterval * 5 / 4, paramPhase, paramRetryCount); //打印当前的连接参数 // 如果手机已经采用目标范围,不需要再次发送请求 if(peripheralIntervalInRange(peripheralConnList.connInterval, minInterval, maxInterval)) { peripheralParamTargetReached(); //确认当前实际连接间隔满足目标,停止重试,并进入下一个状态 return (events ^ SBP_PARAM_UPDATE_EVT); } if(paramRetryCount >= PARAM_MAX_RETRY) //判断是否超过最大请求次数 { PRINT("Param negotiation failed: phase=%d, current=%d (%d ms)\r\n", paramPhase, peripheralConnList.connInterval, peripheralConnList.connInterval * 5 / 4); //打印当前的连接参数 paramPhase = PARAM_PHASE_FINISHED; paramRetryCount = 0; //清除重试参数 return (events ^ SBP_PARAM_UPDATE_EVT); } ret = GAPRole_PeripheralConnParamUpdateReq( peripheralConnList.connHandle, minInterval, maxInterval, targetLatency, targetTimeout, Peripheral_TaskID); //调用协议栈发起更新 paramRetryCount++; //增加请求计数 PRINT("Request %s interval %d/%d: " "min=%dms, max=%dms, latency=%d, " "timeout=%dms, ret=0x%02x\r\n", targetIsFast ? "FAST" : "SLOW", paramRetryCount, PARAM_MAX_RETRY, minInterval * 5 / 4, maxInterval * 5 / 4, targetLatency, targetTimeout * 10, ret); //打印请求后的连接参数,和发起更新的状态 // 请求是异步的,3秒以后再检查回调更新后的实际参数 tmos_start_task(Peripheral_TaskID,SBP_PARAM_UPDATE_EVT,PARAM_RETRY_DELAY); // 参数请求未成功时,每6秒重试一次 return (events ^ SBP_PARAM_UPDATE_EVT); }
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); PRINT("Connection parameters updated:\r\n"); PRINT(" handle = 0x%04x\r\n", connHandle); PRINT(" interval = %d, %d ms\r\n", connInterval, connInterval * 5 / 4); PRINT(" latency = %d\r\n", connSlaveLatency); PRINT(" timeout = %d, %d ms\r\n", connTimeout, connTimeout * 10); //打印最终实际生效的参数 } else { PRINT("ERR..\n"); } //根据当前状态机阶段,判断新的连接间隔 // 初始快速档或最后恢复快速档 if(((paramPhase == PARAM_PHASE_REQUEST_INITIAL_FAST) || (paramPhase == PARAM_PHASE_REQUEST_FAST)) && peripheralIntervalInRange(connInterval, PARAM_FAST_MIN_INTERVAL, PARAM_FAST_MAX_INTERVAL)) ////判断主机最终采用的连接间隔是否落入目标范围 { peripheralParamTargetReached(); //停止重试、清零重试次数,并推进状态机: } // 慢速档 else if((paramPhase == PARAM_PHASE_REQUEST_SLOW) && peripheralIntervalInRange(connInterval, PARAM_SLOW_MIN_INTERVAL, PARAM_SLOW_MAX_INTERVAL)) { peripheralParamTargetReached(); //停止重试、清零重试次数,并推进状态机: } // 快速档保持期间,手机又把参数改出了目标范围 else if((paramPhase == PARAM_PHASE_FAST_HOLD) && !peripheralIntervalInRange(connInterval, PARAM_FAST_MIN_INTERVAL, PARAM_FAST_MAX_INTERVAL)) { tmos_stop_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT); //停止切换慢速挡位的事件任务 paramPhase = PARAM_PHASE_REQUEST_INITIAL_FAST; //重新获取快速挡位标志 paramRetryCount = 0; tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT,PARAM_RETRY_DELAY); //完整保持10秒,启动任务切到慢速档 PRINT("Phone changed fast interval; renegotiate FAST\r\n"); } // 慢速档保持期间,手机又把参数改出了目标范围 else if((paramPhase == PARAM_PHASE_SLOW_HOLD) && !peripheralIntervalInRange(connInterval, PARAM_SLOW_MIN_INTERVAL, PARAM_SLOW_MAX_INTERVAL)) { tmos_stop_task(Peripheral_TaskID, SBP_PARAM_SWITCH_EVT); //停止切换快速挡位的事件任务 paramPhase = PARAM_PHASE_REQUEST_SLOW; //重新获取慢速挡位标志 paramRetryCount = 0; tmos_start_task(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT,PARAM_RETRY_DELAY); //完整保持10秒,启动任务切到快速档 PRINT("Phone changed slow interval; renegotiate SLOW\r\n"); } }
在SBP_PARAM_SWITCH_EVT的任务事件中,切换不同挡位,保持时间运行结束后的档位切换的操作
/**********************切换不同挡位,保持时间运行,保持结束后的档位切换任务*****************************/ if(events & SBP_PARAM_SWITCH_EVT) { if(peripheralConnList.connHandle == GAP_CONNHANDLE_INIT) //检查连接是否仍存在 { paramPhase = PARAM_PHASE_IDLE; return (events ^ SBP_PARAM_SWITCH_EVT); //结束本次任务事件 } if(paramPhase == PARAM_PHASE_FAST_HOLD) //快速档保持结束 { // 快速档已经保持10秒,开始请求约500ms paramPhase = PARAM_PHASE_REQUEST_SLOW; //请求慢速挡位 paramRetryCount = 0; PRINT("Fast hold finished; request slow interval\r\n"); tmos_set_event(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT); //启动更新连接参数任务事件 } else if(paramPhase == PARAM_PHASE_SLOW_HOLD) //慢速档保持结束 { // 慢速档已经保持10秒,重新请求约10~15ms paramPhase = PARAM_PHASE_REQUEST_FAST; // 请求重新回到快速档 paramRetryCount = 0; PRINT("Slow hold finished; request fast interval\r\n"); tmos_set_event(Peripheral_TaskID, SBP_PARAM_UPDATE_EVT); //启动更新连接参数任务事件 } return (events ^ SBP_PARAM_SWITCH_EVT); }
通过串口日志和功耗分析仪的电流显示,整个连接间隔的参数的协商更新过程,与手机的APP建立连接之后,先是协商快速的连接间隔参数,连接间隔更新至15ms,运行10秒之后,有重新协商更新连接间隔参数,慢速的连接间隔为460ms,再运行10秒之后,又再次协商到快速的15ms的连接间隔参数。





浙公网安备 33010602011771号