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.

LP-CC1352P7: CC1352P7 - TX-RX Chain Command Not Receiving Data in Proprietary Mode (868 MHz)

Part Number: LP-CC1352P7
Other Parts Discussed in Thread: CC1352P7

Tool/software:

Environment

  • Device: CC1352P7
  • SDK: SimpleLink SDK 8.30
  • RF Mode: Proprietary Sub-1 GHz (868 MHz)
  • Configuration: 1 Mbps, 350 kHz Deviation, 2-GFSK, 2.2 MHz RX Bandwidth

We're having issues with TX-RX command chains. The TX-only and RX-only operations work perfectly, but when chaining TX->RX commands, the RX portion never receives data, even though we know the remote device is transmitting. Our protocol requires the RX window to open shortly(with small delay) after TX completes.

Test Scenario

  1. Device A : Sends a request packet, then immediately listens for response packet using TX-RX chain
  2. Device B : Receives packet correctly, immediately sends response packet
  3. Issue: Device A completes the chain (EVT_CHAIN_DONE) but never receives the connection request packet


static int radio_start_tx_rx_chain(void) {
    /* Reset command status */
    RF_cmdPropTx.status = 0;
    RF_cmdPropRx.status = 0;

    /* Configure TX */
    RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
    RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
    RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
    RF_cmdPropTx.startTrigger.pastTrig = 1;
    RF_cmdPropTx.pNextOp = (RF_Op*)&RF_cmdPropRx;
    RF_cmdPropTx.condition.rule = COND_ALWAYS;

    /* Configure RX */
    RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
    RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
    RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
    RF_cmdPropRx.startTrigger.pastTrig = 1;
    RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
    RF_cmdPropRx.endTime = 8000;  // 2ms window
    RF_cmdPropRx.pNextOp = NULL;
    RF_cmdPropRx.condition.rule = COND_NEVER;

    /* Reset RX queue */
    g_radio.rx_data_entry.status = DATA_ENTRY_PENDING;

    /* Post chain */
    g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                    (RF_Op*)&RF_cmdPropTx,
                                    RF_PriorityNormal,
                                    radio_rf_callback,
                                    RF_EventLastCmdDone);

    return (g_radio.cmd_handle >= 0) ? 0 : -1;
}



static int radio_start_rx(void) {
    /* Reset RX data entry */
    g_radio.rx_data_entry.status = DATA_ENTRY_PENDING;

    /* Use global RF_cmdPropRx, not cmd_rx_adv */
    RF_cmdPropRx.status = 0;
    RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
    RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;

    /* Configure what gets appended */
    RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
    RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
    RF_cmdPropRx.rxConf.bIncludeHdr = 1;      /* Include length byte */
    RF_cmdPropRx.rxConf.bIncludeCrc = 0;      /* Don't include CRC */
    RF_cmdPropRx.rxConf.bAppendRssi = 1;      /* Append RSSI */
    RF_cmdPropRx.rxConf.bAppendTimestamp = 1; /* Append timestamp */
    RF_cmdPropRx.rxConf.bAppendStatus = 1;    /* Append status */

    /* Keep RX on for multiple packets */
    RF_cmdPropRx.pktConf.bRepeatOk = 1;
    RF_cmdPropRx.pktConf.bRepeatNok = 1;

    if (g_radio.current_op.rx_timeout_rat > 0) {
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = g_radio.current_op.rx_timeout_rat;
    } else {
        RF_cmdPropRx.endTrigger.triggerType = TRIG_NEVER;
    }

    /* Post RX command directly (no chaining) */
    g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                    (RF_Op*)&RF_cmdPropRx,
                                    RF_PriorityNormal,
                                    radio_rf_callback,
                                    RF_EventRxEntryDone | RF_EventLastCmdDone);

    if (g_radio.cmd_handle < 0) {
        g_radio.state = RADIO_STATE_IDLE;
        return -1;
    }

    return 0;
}



static int radio_start_tx(void) {
    /* Prepare TX packet with length byte */
    g_radio.tx_packet[0] = g_radio.current_op.tx_length;
    memcpy(&g_radio.tx_packet[1], g_radio.current_op.tx_data,
           g_radio.current_op.tx_length);

    /* Reset global commands */
    RF_cmdPropTx.status = 0;
    RF_cmdPropRx.status = 0;

    RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
    RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
    RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
    RF_cmdPropTx.startTrigger.pastTrig = 1;
    RF_cmdPropTx.pNextOp = NULL;
    RF_cmdPropTx.condition.rule = COND_ALWAYS;

    /* Post chain */
    g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                    (RF_Op*)&RF_cmdPropTx,
                                    RF_PriorityNormal,
                                    radio_rf_callback,
                                    RF_EventLastCmdDone);

    return (g_radio.cmd_handle >= 0) ? 0 : -1;
}



Questions

  1. Is there a specific configuration required for TX-RX chains in proprietary mode that differs from the examples?
  2. Are there timing constraints between TX end and RX start that we should be aware of?
  3. Should we be using CMD_PROP_TX_ADV/CMD_PROP_RX_ADV instead of the basic commands?
  4. Is there a minimum or maximum gap required between TX and RX in a chain?
  5. Most importantly: Are there any reference examples of TX->RX command chains in proprietary mode? We've searched the SDK but only found TX-only chains and RX-only operations.

Any guidance on proper TX-RX chain configuration for proprietary mode would be greatly appreciated. We specifically need the RX to start immediately after TX completes to implement our protocol correctly.



  • Hi Sharan,

    When switching from Tx to Rx there is a certain delay for the radio, I believe a couple of hundred uS. What's your requirement here? 

    Cheers,

    Marie

  • You should output the LNA and Pa signals on some pins for debugging (both on your transmitter and receiver) to make sure that your receiver is in RX while you are transmitting:

    Routing RF Core Signals to Physical Pins — SimpleLinkTm CC13XX/CC26XX SDK Proprietary RF User's Guide 7.30.00 documentation

    It might also be a good idea to avoid having a timeout on the chained RX while debugging, to make sure that your time in RX is not too short.

    below is a modified version of the rfPacketRX example that uses a TX/RX command chain.

    The example was tested towards SmartRF Studio, so I did not test with a timeout in RX, as I had to change window in Studio from RX to TX when testing this.

    /***** 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>
    
    /***** Defines *****/
    
    /* Packet RX Configuration */
    #define DATA_ENTRY_HEADER_SIZE  8  /* Constant header size of a Generic Data Entry */
    #define MAX_LENGTH              255 /* Max length byte the radio will accept */
    #define NUM_DATA_ENTRIES        2  /* NOTE: Only two data entries supported at the moment */
    #define NUM_APPENDED_BYTES      2  /* The Data Entries data field will contain:
                                        * 1 Header byte (RF_cmdPropRx.rxConf.bIncludeHdr = 0x1)
                                        * Max 30 payload bytes
                                        * 1 status byte (RF_cmdPropRx.rxConf.bAppendStatus = 0x1) */
    #define PAYLOAD_LENGTH          5
    
    
    /***** Prototypes *****/
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    /***** Variable declarations *****/
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    /* Buffer which contains all Data Entries for receiving data.
     * Pragmas are needed to make sure this buffer is 4 byte aligned (requirement from the RF Core) */
    #if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment = 4
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__GNUC__)
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)]
                                                      __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 rfc_dataEntryGeneral_t* currentDataEntry;
    static uint8_t packetLength;
    static uint8_t* packetDataPointer;
    static uint16_t seqNumber;
    
    
    static uint8_t rxPacket[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
    static uint8_t txPacket[PAYLOAD_LENGTH];
    
    /***** Function definitions *****/
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        GPIO_setConfigAndMux(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO0);
        GPIO_setConfigAndMux(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO1);
    
        if( RFQueue_defineQueue(&dataQueue,
                                rxDataEntryBuffer,
                                sizeof(rxDataEntryBuffer),
                                NUM_DATA_ENTRIES,
                                MAX_LENGTH + NUM_APPENDED_BYTES))
        {
            /* Failed to allocate space for all data entries */
            while(1);
        }
    
        /* Modify CMD_PROP_RX command for application needs */
        /* Set the Data Entity queue for received data */
        RF_cmdPropRx.pQueue = &dataQueue;
        /* Discard ignored packets from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        /* Discard packets with CRC error from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
        RF_cmdPropRx.maxPktLen = MAX_LENGTH;
    
        //RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        //RF_cmdPropRx.endTime = 8000;  // 2ms window
    
        RF_cmdPropTx.pktLen = PAYLOAD_LENGTH;
        RF_cmdPropTx.pPkt = txPacket;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
    
        RF_cmdPropTx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx;
        RF_cmdPropTx.condition.rule = COND_ALWAYS;
    
        /* 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(1)
        {
            txPacket[0] = (uint8_t)(seqNumber >> 8);
            txPacket[1] = (uint8_t)(seqNumber++);
            uint8_t i;
            for (i = 2; i < PAYLOAD_LENGTH; i++)
            {
                txPacket[i] = rand();
            }
            RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, &callback, RF_EventRxEntryDone);
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            //GPIO_toggle(CONFIG_GPIO_RLED);
    
            /* Get current unhandled data entry */
            currentDataEntry = RFQueue_getDataEntry();
    
            /* Handle the packet data, located at &currentDataEntry->data:
             * - Length is the first byte with the current configuration
             * - Data starts from the second byte */
            packetLength      = *(uint8_t*)(&currentDataEntry->data);
            packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);
    
            /* Copy the payload + the status byte to the packet variable */
            memcpy(rxPacket, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
        }
    }

    Siri

  • Hi Marie,

    I tested with a ~2 ms delay for now, but we’re flexible to adjust it based on the radio configuration. My main goal is to get the TX-RX chain command working.

    Could you please review and confirm whether the function radio_start_tx_rx_chain() looks correct?

    For reference, when I split the TX and RX into separate commands, it works as expected. It would also be very helpful if you could point me to any example implementations of a chain command.


     */
    static int radio_start_tx_rx_chain(void) {
        /* Run FS first */
        RF_cmdFs.frequency = radio_channel_to_frequency(g_radio.current_op.channel);
        RF_cmdFs.fractFreq = 0;
    
        RF_EventMask fsResult = RF_runCmd(g_radio.rf_handle,
                                         (RF_Op*)&RF_cmdFs,
                                         RF_PriorityNormal,
                                         NULL, 0);
    
        if (!(fsResult & RF_EventLastCmdDone)) {
            return -1;
        }
    
        /* Step 1: Execute TX synchronously with runCmd */
        RF_cmdPropTx.status = 0;
        RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.pktConf.bFsOff = 0;  /* Keep FS on for RX */
        RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.pNextOp = NULL;  /* No chaining */
        RF_cmdPropTx.condition.rule = COND_NEVER;
    
        /* Synchronous TX - blocks until complete */
        RF_EventMask txResult = RF_runCmd(g_radio.rf_handle,
                                          (RF_Op*)&RF_cmdPropTx,
                                          RF_PriorityNormal,
                                          NULL, 0);
    
        log_printf("TX complete: status=0x%04X, events=0x%08X",
                   RF_cmdPropTx.status, txResult);
    
        /* Check TX success */
        if (!(txResult & RF_EventLastCmdDone) ||
            RF_cmdPropTx.status != PROP_DONE_OK) {
            log_printf("TX failed in chain");
            g_radio.state = RADIO_STATE_IDLE;
            return -1;
        }
    
        /* Call TX callback */
        if (g_radio.current_op.on_tx_done) {
            g_radio.current_op.on_tx_done(RADIO_RESULT_OK);
        }
        g_radio.stats.tx_count++;
    
        /* Step 2: Start RX asynchronously with postCmd */
        RF_cmdPropRx.status = 0;
        RF_cmdPropRx.pktConf.bFsOff = 1;  /* Can turn off FS after RX */
        RF_cmdPropRx.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 0;
        RF_cmdPropRx.pktConf.bUseCrc = 1;
        RF_cmdPropRx.pktConf.bVarLen = 1;
    
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx.rxConf.bIncludeHdr = 1;
        RF_cmdPropRx.rxConf.bIncludeCrc = 0;
        RF_cmdPropRx.rxConf.bAppendRssi = 1;
        RF_cmdPropRx.rxConf.bAppendTimestamp = 0;
        RF_cmdPropRx.rxConf.bAppendStatus = 1;
    
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
    
        /* RX starts immediately */
        RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx.startTrigger.pastTrig = 1;
        RF_cmdPropRx.startTime = 0;
    
        /* RX window */
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = g_radio.current_op.rx_timeout_rat ?: 8000;
    
        RF_cmdPropRx.pNextOp = NULL;
        RF_cmdPropRx.condition.rule = COND_NEVER;
    
        /* Reset RX queue */
        g_radio.rx_data_entry.status = DATA_ENTRY_PENDING;
    
        log_printf("Starting RX after TX, timeout=%d RAT", RF_cmdPropRx.endTime);
    
        /* Post RX command asynchronously */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropRx,
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventLastCmdDone);
    
        if (g_radio.cmd_handle < 0) {
            log_printf("RX post failed");
            g_radio.state = RADIO_STATE_IDLE;
            return -1;
        }
    
        /* State is now waiting for RX */
        g_radio.state = RADIO_STATE_RX;  /* Treat as RX-only for callback */
    
        return 0;
    }

  • Please look at the code example I just posted and compare your code with it.

    It has been tested and works as expected with the 1 Mbps PHY from SmartRF Studio.

    Siri

  • Hi Siri, Thanks. I'll check it.

    Regards,
    Sharan

  • Hi Siri,

    Thanks again for the example, it really helped. With a few timing/delay tweaks, I was able to get the TX-RX chain command working. I've pasted the working function below.

    Now, I'm trying to make the reverse role(RX-TX) work. I followed the TX-RX syntax and adapted it for RX-TX, but it doesn't seems to work. I've included the function below for your reference. 

    Could you please review the command configuration? I'll adjust the timings as needed for our test setup.

    Now, I want to make the reverse role work. I followed the syntax of the TX-RX and modified for reverse but it doesn't work. Pasting the function below for you reference, please check the command configuration, timings I'll tweak according to our test setup.

    Appreciate your help with this!

    static int radio_start_rx_tx_chain(void) {
        /* Reset commands */
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx.status = 0;
    
        /* Setup RX first (slave receives first) */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx.startTrigger.pastTrig = 1;
    
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = 40000;
    
        /* RX configuration */
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx.rxConf.bIncludeHdr = 1;
        RF_cmdPropRx.rxConf.bAppendRssi = 1;
        RF_cmdPropRx.rxConf.bAppendStatus = 1;
    
        /* Chain to TX after RX completes */
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;  /* Always go to TX even if RX times out */
    
        /* Setup TX to follow RX */
        RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.startTime = 7000;
    
        /* TX is end of chain */
        RF_cmdPropTx.pNextOp = NULL;
        RF_cmdPropTx.condition.rule = COND_NEVER;
    
        /* Reset RX queue */
        g_radio.rx_data_entry.status = DATA_ENTRY_PENDING;
    
        /* Post the chain starting with RX */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropRx,  /* Start with RX! */
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }



    static int radio_start_rx_tx_chain(void) {
        /* Reset commands */
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx.status = 0;
    
        /* Setup RX first */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx.startTrigger.pastTrig = 1;
        
        /* RX window from request */
        if (g_radio.current_op.rx_timeout_rat > 0) {
            RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
            RF_cmdPropRx.endTime = g_radio.current_op.rx_timeout_rat;
        } else {
            RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
            RF_cmdPropRx.endTime = 40000;
        }
        
        /* RX configuration */
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx.rxConf.bIncludeHdr = 1;
        RF_cmdPropRx.rxConf.bAppendRssi = 1;
        RF_cmdPropRx.rxConf.bAppendStatus = 1;
        
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx;
        RF_cmdPropRx.condition.rule = COND_STOP_ON_TRUE;  /* Stop after receiving packet */
    
        /* Setup TX to follow */
        RF_cmdPropTx.commandNo = CMD_PROP_TX;
        RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.startTime = 7000;
        RF_cmdPropTx.pNextOp = NULL;  /* End of chain */
        RF_cmdPropTx.condition.rule = COND_NEVER;  /* Always execute if reached */
    
        /* Reset RX queue */
        g_radio.rx_data_entry.status = DATA_ENTRY_PENDING;
    
        log_printf("radio_start_rx_tx_chain");
    
        /* Post the chain starting with RX */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropRx,  /* Start with RX! */
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }

  • I took the example I shared with you and simply swapped the RX and TX command.

    Then I added a 10 ms timeout on the RX command, and had TX start 1.75 ms after RX.

    The code is below and worked as expected:

    /***** 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>
    
    /***** Defines *****/
    
    /* Packet RX Configuration */
    #define DATA_ENTRY_HEADER_SIZE  8  /* Constant header size of a Generic Data Entry */
    #define MAX_LENGTH              255 /* Max length byte the radio will accept */
    #define NUM_DATA_ENTRIES        2  /* NOTE: Only two data entries supported at the moment */
    #define NUM_APPENDED_BYTES      2  /* The Data Entries data field will contain:
                                        * 1 Header byte (RF_cmdPropRx.rxConf.bIncludeHdr = 0x1)
                                        * Max 30 payload bytes
                                        * 1 status byte (RF_cmdPropRx.rxConf.bAppendStatus = 0x1) */
    #define PAYLOAD_LENGTH          5
    
    
    /***** Prototypes *****/
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    /***** Variable declarations *****/
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    /* Buffer which contains all Data Entries for receiving data.
     * Pragmas are needed to make sure this buffer is 4 byte aligned (requirement from the RF Core) */
    #if defined(__TI_COMPILER_VERSION__)
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__IAR_SYSTEMS_ICC__)
    #pragma data_alignment = 4
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    #elif defined(__GNUC__)
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)]
                                                      __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 rfc_dataEntryGeneral_t* currentDataEntry;
    static uint8_t packetLength;
    static uint8_t* packetDataPointer;
    static uint16_t seqNumber;
    
    
    static uint8_t rxPacket[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
    static uint8_t txPacket[PAYLOAD_LENGTH];
    
    /***** Function definitions *****/
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        GPIO_setConfigAndMux(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO0);
        GPIO_setConfigAndMux(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO1);
    
        if( RFQueue_defineQueue(&dataQueue,
                                rxDataEntryBuffer,
                                sizeof(rxDataEntryBuffer),
                                NUM_DATA_ENTRIES,
                                MAX_LENGTH + NUM_APPENDED_BYTES))
        {
            /* Failed to allocate space for all data entries */
            while(1);
        }
    
        /* Modify CMD_PROP_RX command for application needs */
        /* Set the Data Entity queue for received data */
        RF_cmdPropRx.pQueue = &dataQueue;
        /* Discard ignored packets from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushIgnored = 1;
        /* Discard packets with CRC error from Rx queue */
        RF_cmdPropRx.rxConf.bAutoFlushCrcErr = 1;
        /* Implement packet length filtering to avoid PROP_ERROR_RXBUF */
        RF_cmdPropRx.maxPktLen = MAX_LENGTH;
    
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START; 
        RF_cmdPropRx.endTime = (uint32_t)(4000000*0.01f); // 10 ms
    
        RF_cmdPropTx.pktLen = PAYLOAD_LENGTH;
        RF_cmdPropTx.pPkt = txPacket;
    
        RF_cmdPropTx.startTrigger.triggerType = TRIG_REL_PREVEND;       
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.startTime = (uint32_t)(4000000*0.00175f); // 1 .75 
    
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;
    
        /* 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(1)
        {
            txPacket[0] = (uint8_t)(seqNumber >> 8);
            txPacket[1] = (uint8_t)(seqNumber++);
            uint8_t i;
            for (i = 2; i < PAYLOAD_LENGTH; i++)
            {
                txPacket[i] = rand();
            }
            RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx, RF_PriorityNormal, &callback, RF_EventRxEntryDone);
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            /* Get current unhandled data entry */
            currentDataEntry = RFQueue_getDataEntry();
    
            /* Handle the packet data, located at &currentDataEntry->data:
             * - Length is the first byte with the current configuration
             * - Data starts from the second byte */
            packetLength      = *(uint8_t*)(&currentDataEntry->data);
            packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);
    
            /* Copy the payload + the status byte to the packet variable */
            memcpy(rxPacket, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
        }
    }
    

    Running the code above and monitoring the PA and LNA signals (+ the PA signal of another transmitter), it looks like this:

    The RX times out after 10 ms, and transmit a packet 1.75 ms after that. It TX a packet both in the case of timeout and in the case of receiving a packet.

    Chaning the .condition.rule of the RX command to:

    RF_cmdPropRx.condition.rule = COND_STOP_ON_TRUE;

    The radio will only go to TX after an RX timeout. In the case where a packet is received, the chain will stop:

    Siri

  • Hi Siri,

    Thank you for your support. I was able to get the chain commands working between two devices (TX→RX and RX→TX). As you can see, the Master (upper) and Slave (lower) are now perfectly synchronized with the connection intervals and timings.



    My next requirement is to run multiple chain commands within each connection interval. I’ve extended the current setup to send two pairs of TX→RX and RX→TX transactions in a single chained command. However, only the first pair works. I don’t see the second transaction happening, or even being attempted.

    static int radio_start_tx_rx_chain(void) {
        /* Reset commands */
        RF_cmdPropTx.status = 0;
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx2.status = 0;
        RF_cmdPropRx2.status = 0;
    
        /* First TX */
        RF_cmdPropTx.commandNo = CMD_PROP_TX;
        RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropTx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx;
        RF_cmdPropTx.condition.rule = COND_ALWAYS;
    
        /* First RX - configure to not stop on packet */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx.pktConf.bRepeatOk = 1;
        RF_cmdPropRx.pktConf.bRepeatNok = 1;
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;
    
        /* Second TX - use relative timing */
        RF_cmdPropTx2.commandNo = CMD_PROP_TX;
        RF_cmdPropTx2.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx2.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx2.condition.rule = COND_ALWAYS;
    
        /* Second RX */
        RF_cmdPropRx2.commandNo = CMD_PROP_RX;
        RF_cmdPropRx2.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx2.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx2.pNextOp = NULL;
        RF_cmdPropRx2.condition.rule = COND_NEVER;
    
        /* Post the chain */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropTx,
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }



    static int radio_start_rx_tx_chain(void) {
        /* Reset commands */
        RF_cmdPropTx.status = 0;
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx2.status = 0;
        RF_cmdPropRx2.status = 0;
    
        /* Create second command set */
        memcpy(&RF_cmdPropRx2, &RF_cmdPropRx, sizeof(RF_cmdPropRx));
        memcpy(&RF_cmdPropTx2, &RF_cmdPropTx, sizeof(RF_cmdPropTx));
    
        /* First RX */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx.startTrigger.pastTrig = 1;
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx.pktConf.bRepeatOk = 1;
        RF_cmdPropRx.pktConf.bRepeatNok = 1;
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;
    
        /* First TX response */
        memcpy(g_radio.tx_packet, g_radio.current_op.tx_data, g_radio.current_op.tx_length);
        RF_cmdPropTx.commandNo = CMD_PROP_TX;
        RF_cmdPropTx.pPkt = g_radio.tx_packet;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx.condition.rule = COND_ALWAYS;
    
        /* Second RX */
        RF_cmdPropRx2.commandNo = CMD_PROP_RX;
        RF_cmdPropRx2.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx2.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTrigger.pastTrig = 1;
        RF_cmdPropRx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx2.pktConf.bRepeatOk = 1;
        RF_cmdPropRx2.pktConf.bRepeatNok = 1;
        RF_cmdPropRx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx2.condition.rule = COND_ALWAYS;
    
        /* Second TX response */
        RF_cmdPropTx2.commandNo = CMD_PROP_TX;
        RF_cmdPropTx2.pPkt = g_radio.tx_packet;
        RF_cmdPropTx2.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTrigger.pastTrig = 1;
        RF_cmdPropTx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx2.pNextOp = NULL;
        RF_cmdPropTx2.condition.rule = COND_NEVER;
    
        /* Post chain starting with RX */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropRx,
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventTxDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }



    /* Use RFQueue for multiple data entries */
    #define NUM_DATA_ENTRIES    4
    #define MAX_PACKET_LENGTH   255
    #define NUM_APPENDED_BYTES  2


    Could you please check if I’m missing something? Any references or example implementations would greatly speed up my exercise.

    Thanks,
    Sharan

  • Before I try to implement this, you should change your code to not use repeat mode. 

    Having repeat mode set to 1, means that you will restart the RX command once a packet has been received, so your RX will never finish (due to a packet received) and you will not run the next TX.

    If the RX times out, it should go to TX2.

    Please fix this and try again, and then let me know the result.

    Siri

  • I tested with bRepeatOk = 0 and bRepeatNok = 0, but the second pair in the chain still didn’t go through.

  • Below is example code for TX->RX->TX->RX and RX->TX->RX->TX

    #include <stdlib.h>
    #include <unistd.h>
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/GPIO.h>
    #include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
    #include "ti_drivers_config.h"
    #include "RFQueue.h"
    #include <ti_radio_config.h>
    
    #define DATA_ENTRY_HEADER_SIZE  8
    #define MAX_LENGTH              255
    #define NUM_DATA_ENTRIES        2
    #define NUM_APPENDED_BYTES      2
    #define PAYLOAD_LENGTH1         5
    #define PAYLOAD_LENGTH2         50
    
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)]
                                                      __attribute__((aligned(4)));
    static dataQueue_t dataQueue;
    static rfc_dataEntryGeneral_t* currentDataEntry;
    static uint8_t packetLength;
    static uint8_t* packetDataPointer;
    static uint16_t seqNumber;
    
    rfc_CMD_PROP_TX_t RF_cmdPropTx2;
    rfc_CMD_PROP_RX_t RF_cmdPropRx2;
    
    static uint8_t rxPacket[MAX_LENGTH + NUM_APPENDED_BYTES - 1];
    static uint8_t txPacket1[PAYLOAD_LENGTH1];
    static uint8_t txPacket2[PAYLOAD_LENGTH2];
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        GPIO_setConfigAndMux(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO0);
        GPIO_setConfigAndMux(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO1);
    
        if( RFQueue_defineQueue(&dataQueue,
                                rxDataEntryBuffer,
                                sizeof(rxDataEntryBuffer),
                                NUM_DATA_ENTRIES,
                                MAX_LENGTH + NUM_APPENDED_BYTES))
        {
            while(1);
        }
    
        RF_cmdPropRx1.pQueue = &dataQueue;
        RF_cmdPropRx1.rxConf.bAutoFlushIgnored = 1;
        RF_cmdPropRx1.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx1.maxPktLen = MAX_LENGTH;
    
        memcpy(&RF_cmdPropRx2, &RF_cmdPropRx1, sizeof(RF_cmdPropRx1));
        memcpy(&RF_cmdPropTx2, &RF_cmdPropTx1, sizeof(RF_cmdPropTx1));
    
        RF_cmdPropTx1.pktLen = PAYLOAD_LENGTH1;
        RF_cmdPropTx1.pPkt = txPacket1;
        RF_cmdPropTx1.startTrigger.triggerType = TRIG_NOW;      // TX 1 starts immediately
        RF_cmdPropTx1.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx1;
        RF_cmdPropTx1.condition.rule = COND_ALWAYS;
    
        RF_cmdPropRx1.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx1.startTime = (uint32_t)(4000000*0.0025f);  // RX1 starts 2.5 ms after TX1
        RF_cmdPropRx1.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx1.endTime = (uint32_t)(4000000*0.002f);     // Stay 2 ms in RX1 before timeout
        RF_cmdPropRx1.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx1.condition.rule = COND_ALWAYS;
    
        RF_cmdPropTx2.pktLen = PAYLOAD_LENGTH2;
        RF_cmdPropTx2.pPkt = txPacket2;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTime = (uint32_t)(4000000*0.005f);   // TX2 starts 5 ms after RX1
        RF_cmdPropTx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx2.condition.rule = COND_ALWAYS;
    
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTime = (uint32_t)(4000000*0.0075f);  // RX2 starts 7.5 ms after TX2
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime = (uint32_t)(4000000*0.004f);     // Stay 4 ms in RX2
    
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
    
        while(1)
        {
            txPacket1[0] = (uint8_t)(seqNumber >> 8);
            txPacket1[1] = (uint8_t)(seqNumber++);
    
            uint8_t i;
    
            for (i = 2; i < PAYLOAD_LENGTH1; i++)
            {
                txPacket1[i] = rand();
            }
    
            txPacket2[0] = (uint8_t)(seqNumber >> 8);
            txPacket2[1] = (uint8_t)(seqNumber++);
    
            for (i = 2; i < PAYLOAD_LENGTH1; i++)
            {
                txPacket2[i] = rand();
            }
    
            RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx1, RF_PriorityNormal, &callback, RF_EventRxEntryDone);
    
    
            RF_yield(rfHandle);
    
            usleep(100000);
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            currentDataEntry = RFQueue_getDataEntry();
    
            packetLength      = *(uint8_t*)(&currentDataEntry->data);
            packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);
    
            memcpy(rxPacket, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
        }
    }

    #include <stdlib.h>
    #include <unistd.h>
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/GPIO.h>
    #include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
    #include "ti_drivers_config.h"
    #include "RFQueue.h"
    #include <ti_radio_config.h>
    
    #define DATA_ENTRY_HEADER_SIZE  8
    #define MAX_LENGTH              255
    #define NUM_DATA_ENTRIES        2
    #define NUM_APPENDED_BYTES      2
    #define PAYLOAD_LENGTH1         5
    #define PAYLOAD_LENGTH2         50
    
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    static uint8_t rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)]
                                                      __attribute__((aligned(4)));
    
    static dataQueue_t dataQueue;
    static rfc_dataEntryGeneral_t* currentDataEntry;
    static uint8_t packetLength;
    static uint8_t* packetDataPointer;
    static uint16_t seqNumber;
    
    rfc_CMD_PROP_TX_t RF_cmdPropTx2;
    rfc_CMD_PROP_RX_t RF_cmdPropRx2;
    
    static uint8_t rxPacket[MAX_LENGTH + NUM_APPENDED_BYTES - 1];
    static uint8_t txPacket1[PAYLOAD_LENGTH1];
    static uint8_t txPacket2[PAYLOAD_LENGTH2];
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        GPIO_setConfigAndMux(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO0);
        GPIO_setConfigAndMux(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO1);
    
        if( RFQueue_defineQueue(&dataQueue,
                                rxDataEntryBuffer,
                                sizeof(rxDataEntryBuffer),
                                NUM_DATA_ENTRIES,
                                MAX_LENGTH + NUM_APPENDED_BYTES))
        {
            while(1);
        }
    
        RF_cmdPropRx1.pQueue = &dataQueue;
        RF_cmdPropRx1.rxConf.bAutoFlushIgnored = 1;
        RF_cmdPropRx1.rxConf.bAutoFlushCrcErr = 1;
        RF_cmdPropRx1.maxPktLen = MAX_LENGTH;
    
        memcpy(&RF_cmdPropRx2, &RF_cmdPropRx1, sizeof(RF_cmdPropRx1));
        memcpy(&RF_cmdPropTx2, &RF_cmdPropTx1, sizeof(RF_cmdPropTx1));
    
        RF_cmdPropRx1.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx1.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx1.endTime = (uint32_t)(4000000*0.002f);     // Stay 2 ms in RX1 before timeout
        RF_cmdPropRx1.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx1;
        RF_cmdPropRx1.condition.rule = COND_ALWAYS;
    
        RF_cmdPropTx1.pktLen = PAYLOAD_LENGTH1;
        RF_cmdPropTx1.pPkt = txPacket1;
        RF_cmdPropTx1.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx1.startTime = (uint32_t)(4000000*0.0025f);  // TX1 starts 2.5 ms after RX1
        RF_cmdPropTx1.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx1.condition.rule = COND_ALWAYS;
    
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTime = (uint32_t)(4000000*0.005f);   // RX1 starts 5 ms after TX1
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime = (uint32_t)(4000000*0.004f);     // Stay 4 ms in RX2 before timeout
        RF_cmdPropRx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx2.condition.rule = COND_ALWAYS;
    
        RF_cmdPropTx2.pktLen = PAYLOAD_LENGTH2;
        RF_cmdPropTx2.pPkt = txPacket2;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTime = (uint32_t)(4000000*0.0075f);  // TX2 starts 7.5 ms after RX2
    
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    
        RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL, 0);
    
        while(1)
        {
            txPacket1[0] = (uint8_t)(seqNumber >> 8);
            txPacket1[1] = (uint8_t)(seqNumber++);
    
            uint8_t i;
    
            for (i = 2; i < PAYLOAD_LENGTH1; i++)
            {
                txPacket1[i] = rand();
            }
    
            txPacket2[0] = (uint8_t)(seqNumber >> 8);
            txPacket2[1] = (uint8_t)(seqNumber++);
    
            for (i = 2; i < PAYLOAD_LENGTH1; i++)
            {
                txPacket2[i] = rand();
            }
    
            RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx1, RF_PriorityNormal, &callback, RF_EventRxEntryDone);
    
            RF_yield(rfHandle);
    
            usleep(100000);
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
            currentDataEntry = RFQueue_getDataEntry();
    
            packetLength      = *(uint8_t*)(&currentDataEntry->data);
            packetDataPointer = (uint8_t*)(&currentDataEntry->data + 1);
    
            memcpy(rxPacket, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
        }
    }

    Siri

  • Hi,

    Thanks for the previous example. Mapping the radio activity to IOs was a great idea, it’s giving me some hints about what’s happening. I’m seeing two pulses for every connection interval. Does this confirm that two chained operations are being executed? I still need to fine-tune the timing alignments, and I’m trying to interpret the pulses more clearly. Any pointers would be really helpful since I’m still new to TI’s radio chips :)


    First picture: Master device

    • Channel0: CONFIG_GPIO_RLED

    • Channel1: CONFIG_GPIO_GLED





    Second picture: Master and Slave devices

    • Channel0 (Master): CONFIG_GPIO_RLED

    • Channel1 (Slave): CONFIG_GPIO_RLED




  • I did some further testing, and it looks like the second TX-RX isn’t working. From the scope traces, the second TX-RX pair (set up the same way as the first) is aligned in time, but the RX appears to be timing out.

    I’ve attached the Saleae logs for your reference.

    • D0 – Master GLED (7)

    • D1 – Master RLED (6)

    • D2 – Slave GLED (7)

    • D3 – Slave RLED (6)


        GPIO_setConfigAndMux(CONFIG_GPIO_RLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO0);
        GPIO_setConfigAndMux(CONFIG_GPIO_GLED, GPIO_CFG_OUT_STD, IOC_PORT_RFC_GPO1);






    capture.sal.zip



     
    static int radio_start_conn_master_chain(void) {
        /* Reset commands */
        RF_cmdPropTx.status = 0;
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx2.status = 0;
        RF_cmdPropRx2.status = 0;
    
        /* First TX */
        RF_cmdPropTx.commandNo = CMD_PROP_TX;
        RF_cmdPropTx.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropTx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx;
        RF_cmdPropTx.condition.rule = COND_ALWAYS;
    
        /* First RX - configure to not stop on packet */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx.startTime =  RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 0;
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;
    
        /* Second TX - use relative timing */
        RF_cmdPropTx2.commandNo = CMD_PROP_TX;
        RF_cmdPropTx2.pPkt = g_radio.current_op.tx_data;
        RF_cmdPropTx2.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx2.condition.rule = COND_ALWAYS;
    
        /* Second RX */
        RF_cmdPropRx2.commandNo = CMD_PROP_RX;
        RF_cmdPropRx2.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx2.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime =  RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx2.pNextOp = NULL;
        RF_cmdPropRx2.condition.rule = COND_NEVER;
    
        /* Post the chain */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropTx,
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }
    
    static int radio_start_rx_tx_chain(void) {
        /* Reset commands */
        RF_cmdPropTx.status = 0;
        RF_cmdPropRx.status = 0;
        RF_cmdPropTx2.status = 0;
        RF_cmdPropRx2.status = 0;
    
        /* Create second command set */
        memcpy(&RF_cmdPropRx2, &RF_cmdPropRx, sizeof(RF_cmdPropRx));
        memcpy(&RF_cmdPropTx2, &RF_cmdPropTx, sizeof(RF_cmdPropTx));
    
        /* First RX */
        RF_cmdPropRx.commandNo = CMD_PROP_RX;
        RF_cmdPropRx.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx.startTrigger.triggerType = TRIG_NOW;
        RF_cmdPropRx.startTrigger.pastTrig = 1;
        RF_cmdPropRx.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx.endTime = RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 0;
        RF_cmdPropRx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx;
        RF_cmdPropRx.condition.rule = COND_ALWAYS;
    
        /* First TX response */
        memcpy(g_radio.tx_packet, g_radio.current_op.tx_data, g_radio.current_op.tx_length);
        RF_cmdPropTx.commandNo = CMD_PROP_TX;
        RF_cmdPropTx.pPkt = g_radio.tx_packet;
        RF_cmdPropTx.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx.startTrigger.pastTrig = 1;
        RF_cmdPropTx.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx.pNextOp = (rfc_radioOp_t*)&RF_cmdPropRx2;
        RF_cmdPropTx.condition.rule = COND_ALWAYS;
    
        /* Second RX */
        RF_cmdPropRx2.commandNo = CMD_PROP_RX;
        RF_cmdPropRx2.pQueue = &g_radio.rx_data_queue;
        RF_cmdPropRx2.maxPktLen = RADIO_MAX_PACKET_LEN;
        RF_cmdPropRx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropRx2.startTrigger.pastTrig = 1;
        RF_cmdPropRx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropRx2.endTrigger.triggerType = TRIG_REL_START;
        RF_cmdPropRx2.endTime = 4000;//RF_convertMsToRatTicks(g_radio.current_op.rx_timeout_ms);
        RF_cmdPropRx2.pktConf.bRepeatOk = 0;
        RF_cmdPropRx2.pktConf.bRepeatNok = 0;
        RF_cmdPropRx2.pNextOp = (rfc_radioOp_t*)&RF_cmdPropTx2;
        RF_cmdPropRx2.condition.rule = COND_ALWAYS;
    
        /* Second TX response */
        RF_cmdPropTx2.commandNo = CMD_PROP_TX;
        RF_cmdPropTx2.pPkt = g_radio.tx_packet;
        RF_cmdPropTx2.pktLen = g_radio.current_op.tx_length;
        RF_cmdPropTx2.startTrigger.triggerType = TRIG_REL_PREVEND;
        RF_cmdPropTx2.startTrigger.pastTrig = 1;
        RF_cmdPropTx2.startTime = RF_convertUsToRatTicks(g_radio.current_op.turnaround_time_us);
        RF_cmdPropTx2.pNextOp = NULL;
        RF_cmdPropTx2.condition.rule = COND_NEVER;
    
        /* Post chain starting with RX */
        g_radio.cmd_handle = RF_postCmd(g_radio.rf_handle,
                                        (RF_Op*)&RF_cmdPropRx,
                                        RF_PriorityNormal,
                                        radio_rf_callback,
                                        RF_EventRxEntryDone | RF_EventTxDone | RF_EventLastCmdDone);
    
        return (g_radio.cmd_handle >= 0) ? 0 : -1;
    }

  • Not sure what you mean.

    The two test codes I provided works as expected. The purpose of them was to show how the chaining was done and to have the chain run all commands.

    I did not indent to provide an example where you could run the TX-RX-TX-RX chain on one device and the RX-TX-RX-TX on another and then have them to be aligned.

    Is this what you are trying to do? If so, I will have to leave it to you to configure the timing correctly based on the data rate, packet length etc. that you are using for your application.

    Siri