This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2640R2F: 请问BLE5 Simple peripheral 例程是否默认启用Coded PHY?

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