Hi, everybody!
I'm trying to implement WakeOnRadio TX/RX to Easylink of existing project, but can't get TX packet correctly received even in SmartRF Studio
As requested I'm using rfWakeOnRadioTx/rfWakeOnRadioRx example to get all this thing working and SmartRF studio and receiving part of our
device to debug.
Biggest difference from TI examples, mentioned above, that we are using 5kpbs SimpleLink Long Range modulation. Also I wouldn't like to copy structures, using initializeTxAdvCmdFromTxCmd due to memory considerations, so, I'm trying to fill rfc_CMD_PROP_TX_ADV_t from scratch:
// CMD_PROP_TX_ADV // Proprietary Mode Transmit Command rfc_CMD_PROP_TX_ADV_t RF_cmdPropTxAdv = { .commandNo = CMD_PROP_TX_ADV, .status = 0x0000, .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx .startTime = 0x00000000, .startTrigger.triggerType = 0x0, .startTrigger.bEnaCmd = 0x0, .startTrigger.triggerNo = 0x0, .startTrigger.pastTrig = 0x0, .condition.rule = 0x1, .condition.nSkip = 0x0, .pktConf.bFsOff = 0x0, .pktConf.bUseCrc = 0x1, // .pktConf.bVarLen = 0x1, // no such field in CMD_PROP_TX_ADV .pktLen = 0x14, // SET APPLICATION PAYLOAD LENGTH .syncWord = 0x930B51DE, .pPkt = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx };
than - fill requested fields according TI example:
//store application callback txCb = cb; txBuffer[0]=txPacket->len + addrSize + 1; memcpy(txBuffer+1, txPacket->dstAddr, addrSize); memcpy(txBuffer + addrSize + 1, txPacket->payload, txPacket->len); //packet length to Tx includes address EasyLink_cmdPropTxAdv.pktLen = txPacket->len + addrSize + 1; EasyLink_cmdPropTxAdv.pPkt = txBuffer; EasyLink_cmdPropTxAdv.preTime = WOR_PREAMBLE_TIME_RAT_TICKS(WOR_WAKEUPS_PER_SECOND); EasyLink_cmdPropTxAdv.preTrigger.triggerType = TRIG_REL_START; EasyLink_cmdPropTxAdv.pktConf.bFsOff = 0x0; EasyLink_cmdPropTxAdv.pktConf.bUseCrc = 0x1; EasyLink_cmdPropTxAdv.syncWord = 0x930B51DE;
Then - transmit
/* Send packet */
asyncCmdHndl = RF_postCmd(rfHandle, (RF_Op*)&EasyLink_cmdPropTxAdv,
RF_PriorityNormal, txDoneCallback, EASYLINK_RF_EVENT_MASK);
if (EasyLink_CmdHandle_isValid(asyncCmdHndl))
{
status = EasyLink_Status_Success;
}
//busyMutex will be released by the callback
return status;
Other settings are attached:
//********************************************************************************* // Generated by SmartRF Studio version 2.6.1 (build #20) // Tested for SimpleLink SDK version: CC13x0 SDK 1.30.xx.xx // Device: CC1350 Rev. 2.1 (Rev. B) // //********************************************************************************* //********************************************************************************* // Parameter summary // Address: off // Address0: 0xAA // Address1: 0xBB // Frequency: 868.00000 MHz // Data Format: Serial mode disable // Deviation: 5.000 kHz // Packet Length Config: Variable // Max Packet Length: 255 // Packet Length: 20 // RX Filter BW: 49 kHz // Symbol Rate: 19.99969 kBaud // Sync Word Length: 32 Bits // TX Power: 14 dBm (requires define CCFG_FORCE_VDDR_HH = 1 in ccfg.c, see CC13xx/CC26xx Technical Reference Manual) // Whitening: No whitening #define DEVICE_FAMILY_PATH DeviceFamily_constructPath #include <ti/devices/DeviceFamily.h> #include DEVICE_FAMILY_PATH(driverlib/rf_mailbox.h) #include DEVICE_FAMILY_PATH(driverlib/rf_common_cmd.h) #include DEVICE_FAMILY_PATH(driverlib/rf_prop_cmd.h) #include <ti/drivers/rf/RF.h> #include DEVICE_FAMILY_PATH(rf_patches/rf_patch_cpe_sl_longrange.h) #include DEVICE_FAMILY_PATH(rf_patches/rf_patch_rfe_sl_longrange.h) #include DEVICE_FAMILY_PATH(rf_patches/rf_patch_mce_sl_longrange.h) #include "smartrf_settings.h" // TI-RTOS RF Mode Object RF_Mode RF_prop = { .rfMode = RF_MODE_PROPRIETARY_SUB_1, .cpePatchFxn = &rf_patch_cpe_sl_longrange, .mcePatchFxn = &rf_patch_mce_sl_longrange, .rfePatchFxn = &rf_patch_rfe_sl_longrange, }; // Overrides for CMD_PROP_RADIO_DIV_SETUP static uint32_t pOverrides[] = { // override_use_patch_simplelink_long_range.xml // PHY: Use MCE RAM patch, RFE RAM patch MCE_RFE_OVERRIDE(1,0,0,1,0,0), // override_synth_prop_863_930_div5_lbw60k.xml // Synth: Set recommended RTRIM to 7 HW_REG_OVERRIDE(0x4038,0x0037), // Synth: Set Fref to 4 MHz (uint32_t)0x000684A3, // Synth: Configure fine calibration setting HW_REG_OVERRIDE(0x4020,0x7F00), // Synth: Configure fine calibration setting HW_REG_OVERRIDE(0x4064,0x0040), // Synth: Configure fine calibration setting (uint32_t)0xB1070503, // Synth: Configure fine calibration setting (uint32_t)0x05330523, // Synth: Set loop bandwidth after lock to 60 kHz (uint32_t)0x40410583, // Synth: Set loop bandwidth after lock to 60 kHz (uint32_t)0x32CC0603, // Synth: Set loop bandwidth after lock to 60 kHz (uint32_t)0x00010623, // Synth: Configure VCO LDO (in ADI1, set VCOLDOCFG=0x9F to use voltage input reference) ADI_REG_OVERRIDE(1,4,0x9F), // Synth: Configure synth LDO (in ADI1, set SLDOCTL0.COMP_CAP=1) ADI_HALFREG_OVERRIDE(1,7,0x4,0x4), // Synth: Use 24 MHz XOSC as synth clock, enable extra PLL filtering (uint32_t)0x02010403, // Synth: Configure extra PLL filtering (uint32_t)0x00108463, // Synth: Increase synth programming timeout (0x04B0 RAT ticks = 300 us) (uint32_t)0x04B00243, // override_synth_disable_bias_div5.xml // Synth: Set divider bias to disabled HW32_ARRAY_OVERRIDE(0x405C,1), // Synth: Set divider bias to disabled (specific for loDivider=5) (uint32_t)0x18000200, // override_phy_rx_aaf_bw_0xd.xml // Rx: Set anti-aliasing filter bandwidth to 0xD (in ADI0, set IFAMPCTL3[7:4]=0xD) ADI_HALFREG_OVERRIDE(0,61,0xF,0xD), // override_phy_gfsk_rx.xml // Rx: Set LNA bias current trim offset to 3 (uint32_t)0x00038883, // Rx: Freeze RSSI on sync found event HW_REG_OVERRIDE(0x6084,0x35F1), // override_phy_gfsk_pa_ramp_agc_reflevel_0x14.xml // Tx: Configure PA ramping setting (0x41). Rx: Set AGC reference level to 0x14. HW_REG_OVERRIDE(0x6088,0x4114), // Tx: Configure PA ramping setting HW_REG_OVERRIDE(0x608C,0x8213), // override_phy_long_range_dsss2.xml // PHY: Configure DSSS SF=2 HW_REG_OVERRIDE(0x505C,0x0100), // override_phy_rx_rssi_offset_5db.xml // Rx: Set RSSI offset to adjust reported RSSI by +5 dB (uint32_t)0x00FB88A3, // TX power override // Tx: Set PA trim to max (in ADI0, set PACTL0=0xF8) ADI_REG_OVERRIDE(0,12,0xF8), (uint32_t)0xFFFFFFFF, }; // CMD_PROP_RADIO_DIV_SETUP // Proprietary Mode Radio Setup Command for All Frequency Bands rfc_CMD_PROP_RADIO_DIV_SETUP_t RF_cmdPropRadioDivSetup = { .commandNo = 0x3807, .status = 0x0000, .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx .startTime = 0x00000000, .startTrigger.triggerType = 0x0, .startTrigger.bEnaCmd = 0x0, .startTrigger.triggerNo = 0x0, .startTrigger.pastTrig = 0x0, .condition.rule = 0x1, .condition.nSkip = 0x0, .modulation.modType = 0x1, .modulation.deviation = 0x14, .symbolRate.preScale = 0xF, .symbolRate.rateWord = 0x3333, .rxBw = 0x21, .preamConf.nPreamBytes = 0x2, .preamConf.preamMode = 0x0, .formatConf.nSwBits = 0x20, .formatConf.bBitReversal = 0x0, .formatConf.bMsbFirst = 0x0, .formatConf.fecMode = 0x8, .formatConf.whitenMode = 0x0, .config.frontEndMode = 0x0, .config.biasMode = 0x1, .config.analogCfgMode = 0x0, .config.bNoFsPowerUp = 0x0, .txPower = 0xAB3F, .pRegOverride = pOverrides, .centerFreq = 0x0364, .intFreq = 0x8000, .loDivider = 0x05, }; // CMD_FS // Frequency Synthesizer Programming Command rfc_CMD_FS_t RF_cmdFs = { .commandNo = 0x0803, .status = 0x0000, .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx .startTime = 0x00000000, .startTrigger.triggerType = 0x0, .startTrigger.bEnaCmd = 0x0, .startTrigger.triggerNo = 0x0, .startTrigger.pastTrig = 0x0, .condition.rule = 0x1, .condition.nSkip = 0x0, .frequency = 0x0364, .fractFreq = 0x0000, .synthConf.bTxMode = 0x0, .synthConf.refFreq = 0x0, .__dummy0 = 0x00, .__dummy1 = 0x00, .__dummy2 = 0x00, .__dummy3 = 0x0000, }; // CMD_PROP_TX // Proprietary Mode Transmit Command rfc_CMD_PROP_TX_t RF_cmdPropTx = { .commandNo = 0x3801, .status = 0x0000, .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx .startTime = 0x00000000, .startTrigger.triggerType = 0x0, .startTrigger.bEnaCmd = 0x0, .startTrigger.triggerNo = 0x0, .startTrigger.pastTrig = 0x0, .condition.rule = 0x1, .condition.nSkip = 0x0, .pktConf.bFsOff = 0x0, .pktConf.bUseCrc = 0x1, .pktConf.bVarLen = 0x1, .pktLen = 0x14, // SET APPLICATION PAYLOAD LENGTH .syncWord = 0x930B51DE, .pPkt = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx }; // CMD_PROP_RX // Proprietary Mode Receive Command rfc_CMD_PROP_RX_t RF_cmdPropRx = { .commandNo = 0x3802, .status = 0x0000, .pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx .startTime = 0x00000000, .startTrigger.triggerType = 0x0, .startTrigger.bEnaCmd = 0x0, .startTrigger.triggerNo = 0x0, .startTrigger.pastTrig = 0x0, .condition.rule = 0x1, .condition.nSkip = 0x0, .pktConf.bFsOff = 0x0, .pktConf.bRepeatOk = 0x0, .pktConf.bRepeatNok = 0x0, .pktConf.bUseCrc = 0x1, .pktConf.bVarLen = 0x1, .pktConf.bChkAddress = 0x0, .pktConf.endType = 0x0, .pktConf.filterOp = 0x0, .rxConf.bAutoFlushIgnored = 0x0, .rxConf.bAutoFlushCrcErr = 0x0, .rxConf.bIncludeHdr = 0x1, .rxConf.bIncludeCrc = 0x0, .rxConf.bAppendRssi = 0x0, .rxConf.bAppendTimestamp = 0x0, .rxConf.bAppendStatus = 0x1, .syncWord = 0x930B51DE, .maxPktLen = 0xFF, // MAKE SURE DATA ENTRY IS LARGE ENOUGH .address0 = 0xAA, .address1 = 0xBB, .endTrigger.triggerType = 0x1, .endTrigger.bEnaCmd = 0x0, .endTrigger.triggerNo = 0x0, .endTrigger.pastTrig = 0x0, .endTime = 0x00000000, .pQueue = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx .pOutput = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx };
Packet transmit ok, but Smart RF Studio registering Received Not OK packet (default settings for 5kpbs SimpleLink Long Range modulation.
From receiver point of view:
1. Initialize rfc_CMD_PROP_RX_ADV_SNIFF_t struct (if we use rfc_CMD_PROP_TX_ADV_t - obvious to use rfc_CMD_PROP_RX_ADV_SNIFF_t command - as I understand TI datasheets, non-ADV and ADV TX/RX commands have different packet structure why your examples using ADV TX command in TX side and normal RX command on RX side)
2. Config sniffer parameters using configureSniffCmd from RX example of TI:
/* Configures Sniff-mode part of the RX_SNIFF command based on mode, datarate and wakeup interval */ void configureSniffCmd(rfc_CMD_PROP_RX_ADV_SNIFF_t* rxSniffCmd, enum CarrierSenseMode mode, uint32_t datarate, uint8_t wakeupPerSecond, uint8_t syncWordSymbols) { /* Enable or disable RSSI */ if ((mode == CarrierSenseMode_RSSI) || (mode == CarrierSenseMode_RSSIandPQT)) { rxSniffCmd->csConf.bEnaRssi = 1; } else { rxSniffCmd->csConf.bEnaRssi = 0; } /* Enable or disable PQT */ if ((mode == CarrierSenseMode_PQT) || (mode == CarrierSenseMode_RSSIandPQT)) { rxSniffCmd->csConf.bEnaCorr = 1; rxSniffCmd->csEndTrigger.triggerType = TRIG_REL_START; } else { rxSniffCmd->csConf.bEnaCorr = 0; rxSniffCmd->csEndTrigger.triggerType = TRIG_NEVER; } /* General Carrier Sense configuration */ rxSniffCmd->csConf.operation = 1; /* Report Idle if RSSI reports Idle to quickly exit if not above RSSI threshold */ rxSniffCmd->csConf.busyOp = 0; /* End carrier sense on channel Busy (the receiver will continue when carrier sense ends, but it will then not end if channel goes Idle) */ rxSniffCmd->csConf.idleOp = 1; /* End on channel Idle */ rxSniffCmd->csConf.timeoutRes = 1; /* If the channel is invalid, it will return PROP_DONE_IDLE_TIMEOUT */ /* RSSI configuration */ rxSniffCmd->numRssiIdle = 1; /* One idle RSSI samples signals that the channel is idle */ rxSniffCmd->numRssiBusy = 1; /* One busy RSSI samples signals that the channel is busy */ rxSniffCmd->rssiThr = (int8_t)WOR_RSSI_THRESHOLD; /* Set the RSSI threshold in dBm */ /* PQT configuration */ rxSniffCmd->corrConfig.numCorrBusy = 1; /* One busy PQT samples signals that the channel is busy */ rxSniffCmd->corrConfig.numCorrInv = 1; /* One busy PQT samples signals that the channel is busy */ /* Calculate basic timing parameters */ uint32_t symbolLengthUs = 4*(1000000UL/datarate); uint32_t preambleSymbols = (1000000UL/wakeupPerSecond)/(symbolLengthUs*4); // uint8_t syncWordSymbols = RF_cmdPropRadioDivSetup.formatConf.nSwBits; /* Represents the time in which we need to receive corrConfig.numCorr* correlation peaks to detect preamble. * When continously checking the preamble quality, this period has to be wide enough to also contain the sync * word, with a margin. If it is not, then there is a chance the SNIFF command will abort while receiving the * sync word, as it no longer detects a preamble. */ uint32_t correlationPeriodUs = (syncWordSymbols + CORR_PERIOD_SYM_MARGIN)*symbolLengthUs; // OK!!! /* Represents the time where we will force a check if preamble is present (only done once). * The main idea is that his should be shorter than "correlationPeriodUs" so that if we get RSSI valid, but * there is not a valid preamble on the air, we will leave RX as quickly as possible. */ uint32_t csEndTimeUs = (CS_END_TIME_MIN_TIME_SYM*symbolLengthUs + CS_END_TIME_MIN_TIME_STATIC_US); /* Represents the maximum time from the startTrigger to when we expect a sync word to be received. */ uint32_t rxEndTimeUs = (preambleSymbols + syncWordSymbols + RX_END_TIME_SYM_MARGIN)*symbolLengthUs; /* Set sniff mode timing configuration in sniff command in RAT ticks */ rxSniffCmd->corrPeriod = (uint16_t)(correlationPeriodUs * US_TO_RAT_TICKS); rxSniffCmd->csEndTime = (uint32_t)(csEndTimeUs * US_TO_RAT_TICKS); rxSniffCmd->startTrigger.triggerType = TRIG_ABSTIME; rxSniffCmd->startTrigger.pastTrig = 1; rxSniffCmd->endTrigger.triggerType = TRIG_REL_START; rxSniffCmd->endTime = (uint32_t)(rxEndTimeUs * US_TO_RAT_TICKS); }
Here is another problem with datarate parameter. calculateSymbolRate function from RX example give us 20000 bps
datarate but in fact it is 5000 only, because 20000 it is symbol rate, but not datarate...
There are also a lot of "magic" constants like:
#define WOR_WAKE_UP_MARGIN_S 0.05f
#define WOR_WAKE_UP_INTERVAL_RAT_TICKS(x) ((uint32_t)(4000000*(1.0f/(x) - (WOR_WAKE_UP_MARGIN_S))))
/* Calculate sniff mode parameters */
#define US_TO_RAT_TICKS 4
#define CORR_PERIOD_SYM_MARGIN 16
#define RX_END_TIME_SYM_MARGIN 40 //8
#define CS_END_TIME_MIN_TIME_SYM 30
#define CS_END_TIME_MIN_TIME_STATIC_US 150
That also might be changed if datarate and symbol rate are different.
After changing all this we getting such spaghetty-code (sorry for this - it is under debug) it is broken part of Easylink_receiveAsync function:
rxCb = cb; pDataEntry = (rfc_dataEntryGeneral_t*) rxBuffer; //data entry rx buffer includes hdr (len-1Byte), addr (max 8Bytes) and data pDataEntry->length = 1 + EASYLINK_MAX_ADDR_SIZE + EASYLINK_MAX_DATA_LENGTH; pDataEntry->status = 0; dataQueue.pCurrEntry = (uint8_t*) pDataEntry; dataQueue.pLastEntry = NULL; EasyLink_cmdPropRxAdv.pQueue = &dataQueue; /* Set the Data Entity queue for received data */ EasyLink_cmdPropRxAdv.pOutput = (uint8_t*)&rxStatistics; /* if (absTime != 0) { EasyLink_cmdPropRxAdv.startTrigger.triggerType = TRIG_ABSTIME; EasyLink_cmdPropRxAdv.startTrigger.pastTrig = 1; EasyLink_cmdPropRxAdv.startTime = absTime; } else { EasyLink_cmdPropRxAdv.startTrigger.triggerType = TRIG_NOW; EasyLink_cmdPropRxAdv.startTrigger.pastTrig = 1; EasyLink_cmdPropRxAdv.startTime = 0; } if (asyncRxTimeOut != 0) { EasyLink_cmdPropRxAdv.endTrigger.triggerType = TRIG_ABSTIME; EasyLink_cmdPropRxAdv.endTime = RF_getCurrentTime() + asyncRxTimeOut; } else { EasyLink_cmdPropRxAdv.endTrigger.triggerType = TRIG_NEVER; EasyLink_cmdPropRxAdv.endTime = 0; } */ //Clear the Rx statistics structure memset(&rxStatistics, 0, sizeof(rfc_propRxOutput_t)); // !!!!!! Configure sniffer !!!!! /* Calculate datarate from prescaler and rate word */ // uint32_t datarate = calculateSymbolRate(EasyLink_cmdPropRadioSetup.divSetup.symbolRate.preScale, // EasyLink_cmdPropRadioSetup.divSetup.symbolRate.rateWord); uint32_t datarate=5000; EasyLink_cmdPropRxAdv.rxConf.bAutoFlushIgnored = 1; EasyLink_cmdPropRxAdv.rxConf.bAutoFlushCrcErr = 1; EasyLink_cmdPropRxAdv.pktConf.bCrcIncHdr = 0x1, EasyLink_cmdPropRxAdv.pktConf.endType = 0x0, EasyLink_cmdPropRxAdv.pktConf.filterOp = 0x1, /* Configure Sniff-mode part of the RX_SNIFF command */ configureSniffCmd(&EasyLink_cmdPropRxAdv, WOR_MODE, datarate, WOR_WAKEUPS_PER_SECOND, EasyLink_cmdPropRadioSetup.divSetup.formatConf.nSwBits); EasyLink_cmdPropRxAdv.startTime = RF_getCurrentTime(); // EasyLink_cmdPropRxAdv.startTime += WOR_WAKE_UP_INTERVAL_RAT_TICKS(WOR_WAKEUPS_PER_SECOND); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! asyncCmdHndl = RF_postCmd(rfHandle, (RF_Op*)&EasyLink_cmdPropRxAdv, RF_PriorityNormal, rxDoneCallback, EASYLINK_RF_EVENT_MASK); if (EasyLink_CmdHandle_isValid(asyncCmdHndl)) { status = EasyLink_Status_Success; } //busyMutex will be released in callback return status;
Please note on bolded commented line.
In such stage we could receive something. .status field in callback function give us error PROP_DONE_RXERR after complete receive.
I have noted, that receive is completed before transmission completed in transmit side (controlling by LED), that means, that preamble
and syncWord receive OK, but some problems with packet structure. But if I uncomment commented line above, I will get only
PROP_DONE_IDLETIMEOUT error in status field each half a second... Than means, that preabmle is detected, but no syncWord found...
I'm completely *ucked with this... May be this is some stupid mistake here in some bit-wise parameters, but I broken my eyes finding
problems in WoR...
Thank you in advance
Looking forward for your reply