Part Number:CC2640R2F
你好,我用CC2640R2F做无线传感器,ADC采集信号已经能顺利传输到手机(iphone ios16, LightBlue APP)。想进一步尝试Long Range时遇到问题,传输距离只有15-20米,一超过这个距离,就Disconnected。我借了同事的三星Galaxy S21,Android 14, LightBlue测试,同样会断开。
我是基于BLE5 simple peripheral这个例程来写的代码,其中SimplePeripheral_processGapMessage方程里有这么一段原代码:
static void SimplePeripheral_processGapMessage(gapEventHdr_t *pMsg)
{
switch(pMsg->opcode)
{
case GAP_DEVICE_INIT_DONE_EVENT:
{
bStatus_t status = FAILURE;
gapDeviceInitDoneEvent_t *pPkt = (gapDeviceInitDoneEvent_t *)pMsg;
if(pPkt->hdr.status == SUCCESS)
{
// Store the system ID
uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
// use 6 bytes of device address for 8 bytes of system ID value
systemId[0] = pPkt->devAddr[0];
systemId[1] = pPkt->devAddr[1];
systemId[2] = pPkt->devAddr[2];
// set middle bytes to zero
systemId[4] = 0x00;
systemId[3] = 0x00;
// shift three bytes up
systemId[7] = pPkt->devAddr[5];
systemId[6] = pPkt->devAddr[4];
systemId[5] = pPkt->devAddr[3];
// Set Device Info Service Parameter
DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Initialized");
// Setup and start Advertising
// For more information, see the GAP section in the User's Guide:
// software-dl.ti.com/…/
// Temporary memory for advertising parameters for set #1. These will be copied
// by the GapAdv module
GapAdv_params_t advParamLegacy = GAPADV_PARAMS_LEGACY_SCANN_CONN;
// Create Advertisement set #1 and assign handle
status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLegacy,
&advHandleLegacy);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Load advertising data for set #1 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Load scan response data for set #1 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLegacy, GAP_ADV_DATA_TYPE_SCAN_RSP,
sizeof(scanRspData), scanRspData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Set event mask for set #1
status = GapAdv_setEventMask(advHandleLegacy,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);
// Enable legacy advertising for set #1
status = GapAdv_enable(advHandleLegacy, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Use long range params to create long range set #2
GapAdv_params_t advParamLongRange = GAPADV_PARAMS_AE_LONG_RANGE_CONN;
// set the secPhy to S8, primPhy在“GAPADV_PARAMS_AE_LONG_RANGE_CONN”预设中已经指定为S2,这边只修改secPhy,从S2改为S8. 反正无论改不改secPhy都不能进入Coded PHY
advParamLongRange.secPhy = GAP_ADV_SEC_PHY_CODED_S8;
// Create Advertisement set #2 and assign handle
status = GapAdv_create(&SimplePeripheral_advCallback, &advParamLongRange,
&advHandleLongRange);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Load advertising data for set #2 that is statically allocated by the app
status = GapAdv_loadByHandle(advHandleLongRange, GAP_ADV_DATA_TYPE_ADV,
sizeof(advertData), advertData);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
// Set event mask for set #2
status = GapAdv_setEventMask(advHandleLongRange,
GAP_ADV_EVT_MASK_START_AFTER_ENABLE |
GAP_ADV_EVT_MASK_END_AFTER_DISABLE |
GAP_ADV_EVT_MASK_SET_TERMINATED);
// Enable long range advertising for set #2
status = GapAdv_enable(advHandleLongRange, GAP_ADV_ENABLE_OPTIONS_USE_MAX , 0);
SIMPLEPERIPHERAL_ASSERT(status == SUCCESS);
//Debug: CODED PHY, JL
Display_printf(dispHandle, SP_ROW_STATUS_1, 0, "Coded PHY Advertising Started with status: %d", status);
……
【方程后半部分还含有其他case,无关、省略不抄】
问题1:这里可以看到simple_peripheral.c 中写了两个Adv Set, 一个是legacy 一个是longrange。我在build/run,从CCS Terminal看见的总是Adv Set 1 Enabled,没有看到过Adv Set 2被enabled。我在SimplePeripheral_startAutoPhyChange方程中看到comment写了 //Set Default PHY to 1M,也找到了SimplePeripheral_addConn(uint16_t connHandle)这个方程,把Default 1M的设置connList[i].currPhy = HCI_PHY_1_MBPS; 改成了connList[i].currPhy = HCI_PHY_CODED;但是terminal依然是“Adv Set 1 Enabled”。请问原因是?
另外,我看到SimplePeripheral_processCmdCompleteEvt方程如下:
static void SimplePeripheral_processCmdCompleteEvt(hciEvt_CmdComplete_t *pMsg)
{
uint8_t status = pMsg->pReturnParam[0];
//Find which command this command complete is for
switch (pMsg->cmdOpcode)
{
case HCI_READ_RSSI:
{
int8 rssi = (int8)pMsg->pReturnParam[3];
// Display RSSI value, if RSSI is higher than threshold, change to faster PHY
if (status == SUCCESS)
{
uint16_t handle = BUILD_UINT16(pMsg->pReturnParam[1], pMsg->pReturnParam[2]);
uint8_t index = SimplePeripheral_getConnIndex(handle);
SIMPLEPERIPHERAL_ASSERT(index < MAX_NUM_BLE_CONNS);
if (rssi != LL_RSSI_NOT_AVAILABLE)
{
connList[index].rssiArr[connList[index].rssiCntr++] = rssi;
connList[index].rssiCntr %= SP_MAX_RSSI_STORE_DEPTH;
int16_t sum_rssi = 0;
for(uint8_t cnt=0; cnt<SP_MAX_RSSI_STORE_DEPTH; cnt++)
{
sum_rssi += connList[index].rssiArr[cnt];
}
connList[index].rssiAvg = (uint32_t)(sum_rssi/SP_MAX_RSSI_STORE_DEPTH);
uint8_t phyRq = SP_PHY_NONE;
uint8_t phyRqS = SP_PHY_NONE;
uint8_t phyOpt = LL_PHY_OPT_NONE;
if(connList[index].phyCngRq == FALSE)
{
if((connList[index].rssiAvg >= RSSI_2M_THRSHLD) &&
(connList[index].currPhy != HCI_PHY_2_MBPS) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to higher data rate
phyRqS = phyRq = HCI_PHY_2_MBPS;
}
else if((connList[index].rssiAvg < RSSI_2M_THRSHLD) &&
(connList[index].rssiAvg >= RSSI_1M_THRSHLD) &&
(connList[index].currPhy != HCI_PHY_1_MBPS) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to legacy regular data rate
phyRqS = phyRq = HCI_PHY_1_MBPS;
}
else if((connList[index].rssiAvg >= RSSI_S2_THRSHLD) &&
(connList[index].rssiAvg < RSSI_1M_THRSHLD) &&
(connList[index].currPhy != SP_PHY_NONE))
{
// try to go to lower data rate S=2(500kb/s)
phyRqS = HCI_PHY_CODED;
phyOpt = LL_PHY_OPT_S2;
phyRq = BLE5_CODED_S2_PHY;
}
else if(connList[index].rssiAvg < RSSI_S2_THRSHLD )
{
// try to go to lowest data rate S=8(125kb/s)
phyRqS = HCI_PHY_CODED;
phyOpt = LL_PHY_OPT_S8;
phyRq = BLE5_CODED_S8_PHY;
}
if((phyRq != SP_PHY_NONE) &&
// First check if the request for this phy change is already not honored then don't request for change
(((connList[index].rqPhy == phyRq) &&
(connList[index].phyRqFailCnt < 2)) ||
(connList[index].rqPhy != phyRq)))
{
//Initiate PHY change based on RSSI
SimplePeripheral_setPhy(connList[index].connHandle, 0,
phyRqS, phyRqS, phyOpt);
connList[index].phyCngRq = TRUE;
// If it a request for different phy than failed request, reset the count
if(connList[index].rqPhy != phyRq)
{
// then reset the request phy counter and requested phy
connList[index].phyRqFailCnt = 0;
}
if(phyOpt == LL_PHY_OPT_NONE)
{
connList[index].rqPhy = phyRq;
}
else if(phyOpt == LL_PHY_OPT_S2)
{
connList[index].rqPhy = BLE5_CODED_S2_PHY;
}
else
{
connList[index].rqPhy = BLE5_CODED_S8_PHY;
}
} // end of if ((phyRq != SP_PHY_NONE) && …
} // end of if (connList[index].phyCngRq == FALSE)
} // end of if (rssi != LL_RSSI_NOT_AVAILABLE)
Display_printf(dispHandle, SP_ROW_RSSI, 0,
"RSSI:%d dBm, AVG RSSI:%d dBm",
(uint32_t)(rssi),
connList[index].rssiAvg);
} // end of if (status == SUCCESS)
break;
}
case HCI_LE_READ_PHY:
{
if (status == SUCCESS)
{
Display_printf(dispHandle, SP_ROW_RSSI + 2, 0, "RXPh: %d, TXPh: %d",
pMsg->pReturnParam[3], pMsg->pReturnParam[4]);
}
break;
}
default:
break;
} // end of switch (pMsg->cmdOpcode)
}
问题2:所以根据上述代码,我认为simple peripheral这个例程,原本的设定就是会自动根据程序读到的RSSI的值自动切换成合适的PHY,请问是这样的吗?如果是,为什么我测试的时候信号只能到15-20米距离(没有屏蔽没有墙,大平层,空旷的办公环境)。前面我提到,peripheral设备广播时,我只看见Adv Set 1 Enabled。在我使用手机连接后,我也总是看到Terminal信息: PHY updated to 2M,这是来自SimplePeripheral_processStackMsg方程下的这一段Display_printf。
// A Phy Update Has Completed or Failed
if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
{
if (pPUC->status != SUCCESS)
{
Display_printf(dispHandle, SP_ROW_STATUS_1, 0,
"PHY Change failure");
}
else
{
// Only symmetrical PHY is supported.
// rxPhy should be equal to txPhy.
Display_printf(dispHandle, SP_ROW_STATUS_2, 0,
"PHY Updated to %s",
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2M" :
(pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value");
}
SimplePeripheral_updatePHYStat(HCI_BLE_PHY_UPDATE_COMPLETE_EVENT, (uint8_t *)pMsg);
}
break;
所以我认为设备是有能力更新PHY的。只是不清楚为什么无法在传输距离长的时候切换到Coded PHY,而是直接断开。我网上查了很多功课,其中有提及到apple在ios 13以后不支持Coded Phy,所以我借了同事的三星来测(Galaxy S21,Android 14),但是结果还是一样。所以只能上来发帖询问一下,代码本身需要进一步修改才得以支持Coded PHY,还是手机这端不兼容/不支持导致的?
相关链接:
https://github.com/NordicSemiconductor/Android-BLE-Library/issues/166
https://forums.developer.apple.com/forums/thread/665542
https://issuetracker.google.com/issues/227887174
Device:CC2640R2F LauchBoard
SDK Version:simplelink_cc2640r2_sdk_5_30_00_03
Vivian Gao:
您好
之前有客户问过类似的问题,您先看看这两个帖子里面的建议。
1)e2e.ti.com/…/5227116
2)e2e.ti.com/…/cc2640r2f-cc2640r2f
,
Jiawei Lai:
你好,我看完了。问题不太一样,我不需要从external MCU / UART 控制CC2640R2F。您再仔细读一下我的问题。
,
Vivian Gao:
您好
为了更好的解决您的问题,您到英文E2E论坛(e2e.ti.com/)发帖问一下资深工程师。