目录:
1. 前言
在阿波罗的项目中,当设备作为从机时,在主设备连接后一段设定时间后发起连接参数的请求,如果请求成功且自身接收主机设定参数,则认为请求成功,如果请求返回失败或主机设定的参数自身不接受,则重新请求。
接下来我们先熟悉低功耗蓝牙的连接参数有哪些以及他们的作用,然后看看APOLLO作为从机时是如何使用它的。
2. 连接参数
关于本节更加详细请进入 BLE连接参数详解
2.1 Connection interval
1.25ms为单位,取值范围为 6~3200。不同系统及版本的手机对连接参数的要求不同,在连接的时候需要注意。
2.2 Slave latency
取值范围为 0~499,同时有效连接间隔<=16s。有效连接间隔公式: Effective Connection Interval = (Connection Interval) × (1 + [Slave Latency])。
2.3 Supervisor timeout
单位为10ms,取值范围为10~3200。需满足以下公式:Supervision Timeout > (1 +slaveLatency)* (connectionInterval)
2.4 作用
连接间隔有最大最小值范围,目的是为了能让主机自由选择一个它认为合适的值,可以把min和max设置成相同值,以设置主机固定在该Connection interval。
设置为范围的好处是:主机有更多的自主性,可以根据具体的需求选择连接间隔。比如说有很多数据需要传输时,主机会选择较小的连接间隔,以快速传递数据;如果比较空闲,则会选择较大的连接间隔,以节省功耗。
3. 连接参数请求流程
连接成功后启动连接请求定时器,当定时器到期后发起请求,请求结果中判断是否有效。
- appSlaveProcConnOpen
- appConnUpdateTimerStart
- appSlaveConnUpdateTimeout
- appSlaveConnUpdate
3.1 连接参数结构体
/*! \brief Configurable parameters for connection parameter update */
typedef struct
{
wsfTimerTicks_t idlePeriod; /*!< \brief 连接成功后 idlePeriod ms 发起连接参数请求*/
uint16_t connIntervalMin; /*!< \brief 连接最小间隔 in 1.25ms units */
uint16_t connIntervalMax; /*!< \brief 连接最大间隔 in 1.25ms units */
uint16_t connLatency; /*!< \brief 连接延迟 */
uint16_t supTimeout; /*!< \brief Supervision timeout in 10ms units */
uint8_t maxAttempts; /*!< \brief 如果请求失败或参数不符合预期,尝试重新请求的次数 */
} appUpdateCfg_t;
3.2 连接成功后指定时间发起连接参数请求
static void appSlaveProcConnOpen(dmEvt_t *pMsg, appConnCb_t *pCb)
{
/* store connection ID */
pCb->connId = (dmConnId_t) pMsg->hdr.param;
/* check if we should do connection parameter update */
if ((pAppUpdateCfg->idlePeriod != 0) &&
((pMsg->connOpen.connInterval < pAppUpdateCfg->connIntervalMin) ||
(pMsg->connOpen.connInterval > pAppUpdateCfg->connIntervalMax) ||
(pMsg->connOpen.connLatency != pAppUpdateCfg->connLatency) ||
(pMsg->connOpen.supTimeout != pAppUpdateCfg->supTimeout)))
{
pCb->connWasIdle = FALSE;
pCb->attempts = 0;
appConnUpdateTimerStart(pCb->connId);//开启连接参数请求的定时器,当定时器到期时发起请求。
}
}
3.3 定时器到期后发起连接请求
void appSlaveProcMsg(wsfMsgHdr_t *pMsg)
{
appConnCb_t *pCb;
/* look up app connection control block from DM connection ID */
pCb = &appConnCb[pMsg->param - 1];
switch(pMsg->event)
{
case APP_CONN_UPDATE_TIMEOUT_IND:
/* Check state to determine if read remote features is needed */
if (pCb->updateState == APP_CU_STATE_UPDATING)
{
appSlaveConnUpdateTimeout(pMsg, pCb);
}
else
{
pCb->updateState = APP_CU_STATE_WAIT_FEATURES;
}
break;
default:
break;
}
}
static void appSlaveConnUpdateTimeout(wsfMsgHdr_t *pMsg, appConnCb_t *pCb)
{
hciConnSpec_t connSpec;
bool_t idle;
/* Change state to updating */
pCb->updateState = APP_CU_STATE_UPDATING;
/* check if connection is idle */
idle = (DmConnCheckIdle(pCb->connId) == 0);
/* if connection is idle and was also idle on last check */
if (idle && pCb->connWasIdle)
{
/* do update */
pCb->attempts++;
connSpec.connIntervalMin = pAppUpdateCfg->connIntervalMin;
connSpec.connIntervalMax = pAppUpdateCfg->connIntervalMax;
connSpec.connLatency = pAppUpdateCfg->connLatency;
connSpec.supTimeout = pAppUpdateCfg->supTimeout;
connSpec.minCeLen = 0;
connSpec.maxCeLen = 0xffff;
DmConnUpdate(pCb->connId, &connSpec);
}
else
{
pCb->connWasIdle = idle;
appConnUpdateTimerStart(pCb->connId);
}
}
3.4 请求结果返回以及失败的话重新请求
static void appSlaveConnUpdate(dmEvt_t *pMsg, appConnCb_t *pCb)
{
if (pAppUpdateCfg->idlePeriod != 0)
{
/* if successful */
if (pMsg->hdr.status == HCI_SUCCESS)
{
// appSlaveConnUpdate can be called as well
// when master itself initiate a connection parameter update.
// so here we need to check if the connection parameter
// meets slave's requirement, if not, we should not stop
// the timer.
// only check connection interval for now
if ((pAppUpdateCfg->connIntervalMin <= ((hciLeConnUpdateCmplEvt_t*)pMsg)->connInterval)
&&(pAppUpdateCfg->connIntervalMax >= ((hciLeConnUpdateCmplEvt_t*)pMsg)->connInterval))
{
appConnUpdateTimerStop(pCb);
}
else {
/* start timer and try again */
appConnUpdateTimerStart(pCb->connId);
}
}
/* else if update failed but not pending and still attempting to do update */
else if ((pMsg->hdr.status != HCI_ERR_CMD_DISALLOWED) && (pCb->attempts < pAppUpdateCfg->maxAttempts))
{
/* start timer and try again */
appConnUpdateTimerStart(pCb->connId);
}
}
}
4. 总结
连接参数是由主机端确定,但是从机端可以发起更新连接参数请求。在建立连接之后,从机可以要求主机更新连接参数,即只要连接建立了,随时可以再次更新连接参数。