CC1311P3: Intermittent out-of-sync behavior observed during continuous reception stream using CC1311P3 RX_ADV mode

Part Number: CC1311P3

Tool/software:

We are working with the CC1311P3 on a custom board designed based on your reference design for the 433 MHz band. We are configuring the following parameters on the RX_ADV_CMD with unlimited RX using a partial buffer read mechanism.

rfc_CMD_PROP_RX_ADV_t RF_cmdPropRxAdv_cst =
{
.commandNo = 0x3804,
.status = 0x0000,
.pNextOp = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.startTime = 0x00000000,
.startTrigger.triggerType = 0x0,
.startTrigger.bEnaCmd = 0x0,//0x1,//0x0,
.startTrigger.triggerNo = 0x0,//0x3,//0x3,//0x2,//0x0,
.startTrigger.pastTrig = 0x1,//0x1,//0x0,
.condition.rule = 0x1,
.condition.nSkip = 0x0,
.pktConf.bFsOff = 0x0,
.pktConf.bRepeatOk = 0x1,
.pktConf.bRepeatNok = 0x1,
.pktConf.bUseCrc = 0x0,
.pktConf.bCrcIncSw = 0x0,
.pktConf.bCrcIncHdr = 0x0,
.pktConf.endType = 0x0,//0x0,
.pktConf.filterOp = 0x0,
.rxConf.bAutoFlushIgnored = 0x0,
.rxConf.bAutoFlushCrcErr = 0x0,
.rxConf.bIncludeHdr = 0x0,
.rxConf.bIncludeCrc = 0x0,
.rxConf.bAppendRssi = 0x0,
.rxConf.bAppendTimestamp = 0x0,
.rxConf.bAppendStatus = 0x0,
.syncWord0 = 0xFEFEFEFE,
.syncWord1 = 0xFEFEFEFE,
.maxPktLen = 0x0,
.hdrConf.numHdrBits = 0x0,
.hdrConf.lenPos = 0x0,
.hdrConf.numLenBits = 0x0,
.addrConf.addrType = 0x0,
.addrConf.addrSize = 0x0,
.addrConf.addrPos = 0x0,
.addrConf.numAddr = 0x0,
.lenOffset = 0x00,
.endTrigger.triggerType = 0x1,//0x1,//0x0,
.endTrigger.bEnaCmd = 0x0,//0x1,//0x0,
.endTrigger.triggerNo = 0x3,//0x0,//0x3,//0x1,//0x2,//0x0, 0x2 = 11 packet miss 0x0 = 31 packet miss
.endTrigger.pastTrig = 0x0,//0x0,//0x1,
.endTime = 0x00000000,
.pAddr = 0, // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
.pQueue = 0, // INSERT APPLICABLE POINTER: (dataQueue_t*)&xxx
.pOutput = 0 // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
};

RF_cmdPropRxAdv_cst.pNextOp = (uint8_t *)&RF_cmdPropRxAdv_cst;

// Run cmd trigger
rfPostHandle = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRxAdv_cst,
RF_PriorityNormal, &ReceivedOnRFcallback,
RF_EventRxEntryDone);

Observed behavior. 

  • We are reading 63 bytes of data continuously, triggered by the RX done interrupt.

  • The transmitter is sending 50-byte packets every 30 ms, periodically.

  • After a few seconds of successful reception, we start observing out-of-sync behavior.

  • In a random fashion, a few packets are correctly decoded, but then continuous noise is received even though transmitter sending data. 

Constraints: 

  • The receiver does not know the packet length in advance.

  • Hence, we are using RX_ADV mode with maxPktLen = 0 to allow partial reads.

  • And we are not aware of periodicity of data transmission by transmitter. 

Could you please advise on the correct usage of RX_ADV mode with partial buffer reading for our use case? Specifically:

  1. Are we missing any critical configuration for unlimited RX with unknown packet length?

  2. Is there any recommendation for avoiding out-of-sync conditions or maintaining packet alignment?

  3. Should we configure or handle synchronization loss detection manually when using maxPktLen = 0?

  4. Do we need to explicitly flush or reset the RX buffer periodically?


Thanks,

Theiv B

  • I am afraid I do not fully understand your setup.

    You say you do not know the length of your packets, but at the same time you are saying that you are transmitting 50-byte long packet every 30 ms?

    What event is the “RX done interrupt”, and why do you read 63 bytes (you stated that you are sending 50 bytes?)

    Please share an example of how the packet you are transmitting looks like (preamble, sync, placement of length byte, max possible length byte etc.) and I can see if I can make an example that correctly receives this packet.

    Why are you using the advanced RX command and not the regular one?

    Also, please explain what you mean by “out-of-sync behavior”. You cannot transmit packets at a given interval (every 30 ms) and then receive it as one long packet. Is that what you are trying to do?

    Siri

  • Hi  ,

    My transmitter sends data in the following format:

    (1-bit preamble + 4-byte sync word) + variable-length data + end-of-data indicator (postamble).
    The transmission occurs at regular or random intervals.

    Note: The data payload size can vary between 1 byte and 2500 bytes.

    On the receiver side, we are configuring the CC1311P3 in continuous RX mode using the RX_ADV command with a partial buffer read mechanism.

    Once a sync word is detected, we observe the RF_EventRxEntryDone interrupt being triggered continuously at regular intervals, which allows us to read the received data.

    Issue Observed:

    • When the transmitter starts for the first time, we are able to decode the incoming data correctly.

    • However, if the transmitter pauses (e.g., for 1 second) and then resumes transmission, the CC1311P3 is unable to receive the new data.

    • Instead of valid packets, we observe continuous noise on the receiver side.

    It seems the receiver is not properly re-synchronizing after the transmission gap.

    Could you please suggest how we can overcome this issue and ensure that the receiver can reliably re-synchronize and receive data, even after gaps in transmission?

    We would appreciate any guidance on:

    1. Proper RX_ADV configuration for re-synchronization in such intermittent transmissions.

    2. Whether any manual RX buffer flushing or reinitialization is needed after transmission gaps.

    3. Any recommended strategy to handle variable-length packets with unknown start time or interval.

    Thanks,

    Theiv B

  • Hi Theiv

    There are still a lot I do not understand with your setup, and it is almost impossible for me to tell you what is going wrong when I do not know what you are doing in your code.

    I will therefore give some general guidance and show you something similar to what I think you are doing, and then you might be able to use this as a starting point.

    A 1 bit preamble and a sync word that is 0xFEFEFEFE is VERY bad, and I am surprised that you even receive anything at all.

    You should start your testing using the recommended 4 bytes preamble and 4 bytes sync (needs to have good autocorrelation properties, so start using the default one) and then you can change this when you have confirmed that your SW is OK.

    For my testing I used the default 50 kbps PHY.

    I also used the normal RX command (CMD_PROP_RX) as I do not understand why you would need the advanced one.

    From Studio, I transmit a packet that looks like this:

    4 bytes preamble, 4 bytes sync, 121 byte payload + END_OF_PACKET (0xAA)

    I have selected the max length to be 250, but you can change this to 2500 if you like to store the complete packet.

    I have two partial read entries that have room for 15 bytes each (+ header) and have chosen to process the incoming data every time a partial read entry is full.

    That means that for the above packet, I will receive 9 RF_EventRxEntryDone interrupts, and receive a total of 9 x 15 = 135 bytes before I cancel RX

    I receive all packets sent from Studio, as expected.

    Please try the example and make it work before you start changing stuff to comply to your spec.

    That way it is easier for me to help you if you get any problems (it is hard to tell what is going on without having code up and running that I can debug myself).

    You can use the rfPacketRX example from the SDK and then change the rfPacketRX.c file as follows:

    // Includes
    // Standard C Libraries
    #include <stdlib.h>
    
    // TI Drivers
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/GPIO.h>
    
    // Driverlib Header files
    #include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
    
    // Board Header files
    #include "ti_drivers_config.h"
    
    // Application Header files
    #include "RFQueue.h"
    #include <ti_radio_config.h>
    
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Semaphore.h>
    
    // Packet RX Configuration
    //  ------------------------------------------------------------------------------------------------
    //  | DATA_ENTRY_HEADER (12 bytes) | D0 | D1 | D2 | .. | .. | D(MAX_LENGTH-2) | END_OF_PACKET 0xAA |
    //  ------------------------------------------------------------------------------------------------
    
    #define DATA_ENTRY_HEADER_SIZE  12                   // Constant header size of a Partial Data Entry
    #define MAX_LENGTH              250                  // Max bytes in the packet (including the END_OF_PACKET bytes)
    #define BUFFER_SIZE             15
    
    // Prototypes
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    // Variable declarations
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    static RF_CmdHandle rxHandle;
    
    // Receive dataQueue for RF Core to fill in data
    static dataQueue_t dataQueue;
    
    #if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN (rxDataEntryBuf0, 4);
    static uint8_t rxDataEntryBuf0[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE];
    #pragma DATA_ALIGN (rxDataEntryBuf1, 4);
    static uint8_t rxDataEntryBuf1[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE];
    
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment = 4
    static uint8_t rxDataEntryBuf0[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE];
    #pragma data_alignment = 4
    static uint8_t rxDataEntryBuf1[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE];
    
    #elif defined(__GNUC__)
    static uint8_t rxDataEntryBuf0[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE] __attribute__((aligned(4)));
    static uint8_t rxDataEntryBuf1[DATA_ENTRY_HEADER_SIZE + BUFFER_SIZE] __attribute__((aligned(4)));
    
    #else
    #error This compiler is not supported.
    #endif
    
    // Receive dataQueue for RF Core to fill in data
    static dataQueue_t dataQueue;
    static uint16_t payloadLength;
    
    rfc_dataEntryPartial_t* partialReadEntry0 = (rfc_dataEntryPartial_t*)&rxDataEntryBuf0;
    rfc_dataEntryPartial_t* partialReadEntry1 = (rfc_dataEntryPartial_t*)&rxDataEntryBuf1;
    rfc_dataEntryPartial_t* currentReadEntry = (rfc_dataEntryPartial_t*)&rxDataEntryBuf0;
    
    rfc_propRxOutput_t rxStatistics;
    // static uint8_t lengthWritten = false;
    static uint8_t cancelRx = false;
    RF_Stat status = 0xFF;
    
    // RX Semaphore
    static Semaphore_Struct rxSemaphore;
    static Semaphore_Handle rxSemaphoreHandle;
    
    uint8_t completePacket[MAX_LENGTH + BUFFER_SIZE]; // You risk that the 0xAA is the first byte in the data entry and that BUFFER_SIZE - 1
                                                      // more bytes must be received and processes before RX is being cancelled
    
    // Debug
    uint8_t eventCmdAborted = 0;
    uint8_t eventRxBufFull = 0;
    uint8_t eventRxEntryDone = 0;
    uint8_t eventMdmSoft = 0;
    uint8_t packetIndex = 0;
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        GPIO_setConfig(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
        GPIO_write(CONFIG_GPIO_RLED, CONFIG_GPIO_LED_OFF);
    
        /* Initialize RX semaphore */
        Semaphore_construct(&rxSemaphore, 0, NULL);
        rxSemaphoreHandle = Semaphore_handle(&rxSemaphore);
    
        partialReadEntry0->length = BUFFER_SIZE + 4; // + 4 is to make room for the pktStatus (2 bytes) and nextIndex (2 bytes)
        partialReadEntry0->config.irqIntv = 10;
        partialReadEntry0->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry0->status = DATA_ENTRY_PENDING;
    
        partialReadEntry1->length = BUFFER_SIZE + 4; // + 4 is to make room for the pktStatus (2 bytes) and nextIndex (2 bytes)
        partialReadEntry1->config.irqIntv = 10;
        partialReadEntry1->config.type = DATA_ENTRY_TYPE_PARTIAL;
        partialReadEntry1->status = DATA_ENTRY_PENDING;
    
        partialReadEntry0->pNextEntry = (uint8_t*)partialReadEntry1;
        partialReadEntry1->pNextEntry = (uint8_t*)partialReadEntry0;
    
        dataQueue.pCurrEntry = (uint8_t*)partialReadEntry0;
        dataQueue.pLastEntry = NULL;
    
        // Modify CMD_PROP_RX command for application needs
        RF_cmdPropRx.pQueue = &dataQueue;
        RF_cmdPropRx.pOutput = (uint8_t*)&rxStatistics;
        RF_cmdPropRx.rxConf.bIncludeHdr = 1; // Must be 1 to receive the first byte after sync in the data entry
        RF_cmdPropRx.maxPktLen = 0;          // Unlimited length
    
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 0;
    
        RF_cmdPropRx.rxConf.bIncludeCrc = 0x0;
        RF_cmdPropRx.rxConf.bAppendRssi = 0x0;
        RF_cmdPropRx.rxConf.bAppendStatus = 0x0;
        RF_cmdPropRx.rxConf.bAppendTimestamp = 0x0;
    
        // Request access to the radio
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    
        // Set the frequency
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
    
        while(true)
        {
            cancelRx = false;
            status = 0xFF;
            eventCmdAborted = 0;
            eventRxBufFull = 0;
            eventRxEntryDone = 0;
            eventMdmSoft = 0;
            packetIndex = 0;
    
            rxHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, &callback, RF_EventCmdAborted   |
                                                                                                 RF_EventRxBufFull    |
                                                                                                 RF_EventRxEntryDone  |
                                                                                                 RF_EventMdmSoft);
    
            Semaphore_pend(rxSemaphoreHandle, BIOS_WAIT_FOREVER); // Wait for cancelRx = true
    
            if (cancelRx)
            {
                status = RF_cancelCmd(rfHandle, rxHandle, 0);
                Semaphore_pend(rxSemaphoreHandle, BIOS_WAIT_FOREVER);
            }
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventMdmSoft)
        {
            eventMdmSoft++;
        }
    
        if (e & RF_EventRxEntryDone)
        {
            eventRxEntryDone++;
            memcpy(&completePacket[packetIndex], (uint8_t*)(&currentReadEntry->rxData), BUFFER_SIZE);
            currentReadEntry->status = DATA_ENTRY_PENDING;
            currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;
            dataQueue.pCurrEntry = (uint8_t*)currentReadEntry;
            uint8_t i;
            for(i = packetIndex; i < packetIndex + BUFFER_SIZE; i++)
            {
                if(completePacket[i] == 0xAA)
                {
                    cancelRx = true;
                    Semaphore_post(rxSemaphoreHandle); // This takes us to "if (cancelRx)
                }
            }
            packetIndex += BUFFER_SIZE;
        }
    
        if (e & RF_EventCmdAborted) // happens due to RF_cancel()
        {
            eventCmdAborted++;
            currentReadEntry->status = DATA_ENTRY_PENDING;
            currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;
            dataQueue.pCurrEntry = (uint8_t*)currentReadEntry;
    
            Semaphore_post(rxSemaphoreHandle);
        }
    
        if (e & RF_EventRxBufFull) // This will happen BEFORE the RF_EventLastCmdDone
        {
            currentReadEntry->status = DATA_ENTRY_PENDING;
            currentReadEntry = (rfc_dataEntryPartial_t*)currentReadEntry->pNextEntry;
            dataQueue.pCurrEntry = (uint8_t*)currentReadEntry;
    
            eventRxBufFull++;
        }
    }
    

    Siri