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.

CCS/CC1350: fastest way to continuously send packets

Part Number: CC1350
Other Parts Discussed in Thread: SYSBIOS

Tool/software: Code Composer Studio

I have my boardconfigured to continuously send packets to different receivers, basically I have a for loop where RF_runCmd(tx) is called in each iteration.

The problem is that the transmission of each iteration takes about 10 ms, this would result in a datarate of about 5000 bps for me, which is a lot lower than what the cc1350 can support. My question is: if I need to continuously send packets, is there one set-up that is significantly faster than another (say to chain tx commands together, instead of using separate transmissions), since I suspect much of this time is spent on transmitter set-up?

To rephase it better - If I have a 5 byte packet that I would like to have the board continuously send, what would be the fastest way to set up the board?

  • If you want to transmit several packets after each other as fast as possible (and have the radio automatically send preamble, sync, crc. etc. ) you can use command chaining, and chain the TX command to itself. However, using this method, there will still be a small gap between the packets.

    If you want true back-to-back transmission, you can achieve this by using infnite length and then write the preamble, sync word, payload, and CRC manually to a data queue. An example on how this can be done can be found here:

    e2e.ti.com/.../2184712

    BR
    Siri
  • Siri, thanks for your response!

    If I use one of the methods I described in the original question, which is to have run_cmd(tx) in a while loop, is the 10ms window for each TX command expected? Or can I expect a much faster rate than this? In terms of speed, is it faster to chain commands together than to have multiple run_cmd()?

    In addition, in the example code that you pointed me to, does the board send 100 packets every 1.5s, or does it send 1 packet every 1.5s for 100 times? If it's the latter, would it be ok to move to packetCounter out of the callback function and into the while(1) loop?

    Thanks!

  • Command chaining would be faster that calling the command over and over again manually. What do you mean by an iteration taking 10 ms. How long time the complete loop will take will depend of the data rate you are using and the packet length. What you should look at it the time from exiting TX until entering TX again. With command chaining I would assume that this would take 200-300 us. I have done some measurements with RX-TX and TX-RX, and I would assume that TX-TX would be about the same:

    e2e.ti.com/.../562134

    The txSyncPacket is sent 100 taimes back-to-back, and this is repeated every 1.5 s.

    Siri
  • I am sorry that I wasn't being clear - my loop is basically just calling tx over and over again and I measured the data rate on the receiver to be about 6 kbps, which means each tx command took roughly 8ms to complete, I was wondering if this is something I would expect.

    In the easylink sample code, a 10 ms delay is added to each transmit, and removing this delay caused the packets to be lost/ dropped, and I was wondering if this also suggests that's how long it takes for the transmitter to send a packet.

  • I modified WakeOnRadioRx to incorporate the code you posted, but it hung on transmit. Here is the code:

    /***** Includes *****/
    #include <stdlib.h>
    #include "RFQueue.h"

    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/Assert.h>

    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Task.h>

    /* TI-RTOS Header files */
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/PIN.h>
    #include <ti/display/Display.h>

    #include <ti/devices/DeviceFamily.h>
    #include DeviceFamily_constructPath(driverlib/cpu.h)

    /* Board Header files */
    #include "Board.h"

    /* RF settings */
    #include "smartrf_settings/smartrf_settings.h"

    /***** Defines *****/
    /* Wake-on-Radio configuration */
    #define WOR_WAKEUPS_PER_SECOND 2

    /* TX number of random payload bytes */
    #define PAYLOAD_LENGTH 30

    /* WOR Example configuration defines */
    #define WOR_PREAMBLE_TIME_RAT_TICKS(x) \
    ((uint32_t)(4000000*(1.0f/(x))))

    /* TX task stack size and priority */
    #define TX_TASK_STACK_SIZE 1024
    #define TX_TASK_PRIORITY 2

    static uint32_t sTime;

    /***** Prototypes *****/
    static void txTaskFunction(UArg arg0, UArg arg1);
    static void initializeTxAdvCmdFromTxCmd(rfc_CMD_PROP_TX_ADV_t* RF_cmdPropTxAdv, rfc_CMD_PROP_TX_t* RF_cmdPropTx);


    /***** Variable declarations *****/
    /* TX task objects and task stack */
    static Task_Params txTaskParams;
    Task_Struct txTask; /* not static so you can see in ROV */
    static uint8_t txTaskStack[TX_TASK_STACK_SIZE];

    /* TX packet payload (length +1 to fit length byte) and sequence number */
    static uint8_t packet[PAYLOAD_LENGTH +1];
    static uint16_t seqNumber;

    /* RF driver objects and handles */
    static RF_Object rfObject;
    static RF_Handle rfHandle;

    /* Pin driver objects and handles */
    static PIN_Handle ledPinHandle;
    static PIN_Handle buttonPinHandle;
    static PIN_State ledPinState;
    static PIN_State buttonPinState;

    /* TX Semaphore */
    static Semaphore_Struct txSemaphore;
    static Semaphore_Handle txSemaphoreHandle;

    /* Advanced TX command for sending long preamble */
    static rfc_CMD_PROP_TX_ADV_t RF_cmdPropTxAdv;

    /*
    * Application LED pin configuration table:
    * - All LEDs board LEDs are off.
    */
    PIN_Config pinTable[] =
    {
    Board_PIN_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_PIN_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    #if defined Board_CC1352R1_LAUNCHXL
    Board_DIO30_RFSW | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    #endif
    PIN_TERMINATE
    };

    /*
    * Application button pin configuration table:
    * - Buttons interrupts are configured to trigger on falling edge.
    */
    PIN_Config buttonPinTable[] = {
    Board_PIN_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
    PIN_TERMINATE
    };


    /***** Function definitions *****/
    /* Pin interrupt Callback function board buttons configured in the pinTable. */
    void buttonCallbackFunction(PIN_Handle handle, PIN_Id pinId) {

    /* Simple debounce logic, only toggle if the button is still pushed (low) */
    CPUdelay((uint32_t)((48000000/3)*0.050f));
    if (!PIN_getInputValue(pinId)) {
    /* Post TX semaphore to TX task */
    Semaphore_post(txSemaphoreHandle);
    }
    }

    #define PACKET_INTERVAL (uint32_t)(4000000*1.5f) /* Set packet interval to 1.5 s */

    #define TX_BUFFERS_TO_SEND 100

    /* The buffer below contains a packet that is sent back-to-back TX_BUFFERS_TO_SEND every PACKET_INTERVAL. */
    static uint8_t txSyncPacket[] = {
    /**********************************************/
    0x55, 0x55, 0x55, 0x55, // Preamble
    0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
    0x91, 0xD3, 0x91, 0xD3, // Sync Word
    0x01, // Length byte
    0xAA, // 1 dummy byte payload
    /**********************************************/
    };

    static rfc_dataEntryPointer_t txEntry;
    static dataQueue_t txQueue;

    static uint8_t packetCounter = 0;

    /***** Prototypes *****/
    static void txTaskFunction(UArg arg0, UArg arg1);

    /***** Variable declarations *****/
    static Task_Params txTaskParams;
    Task_Struct txTask; /* not static so you can see in ROV */
    static uint8_t txTaskStack[TX_TASK_STACK_SIZE];

    static RF_Object rfObject;
    static RF_Handle rfHandle;

    volatile uint8_t packetSent = false;


    /***** Prototypes *****/
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    void TxTask_init(PIN_Handle inPinHandle);

    /*
    * ======== main ========
    */
    int main(void)
    {
    /* Call driver init functions. */
    Board_initGeneral();
    Display_init();

    /* Open LED pins */
    ledPinHandle = PIN_open(&ledPinState, pinTable);
    Assert_isTrue(ledPinHandle != NULL, NULL);

    /* Open Button pins */
    buttonPinHandle = PIN_open(&buttonPinState, buttonPinTable);
    Assert_isTrue(buttonPinHandle != NULL, NULL);

    /* Initialize task */
    TxTask_init(ledPinHandle);

    /* Start BIOS */
    BIOS_start();

    return (0);
    }

    /***** Function definitions *****/
    void TxTask_init(PIN_Handle inPinHandle)
    {
    pinHandle = inPinHandle;

    Task_Params_init(&txTaskParams);
    txTaskParams.stackSize = TX_TASK_STACK_SIZE;
    txTaskParams.priority = TX_TASK_PRIORITY;
    txTaskParams.stack = &txTaskStack;
    txTaskParams.arg0 = (UInt)1000000;

    Task_construct(&txTask, txTaskFunction, &txTaskParams, NULL);
    }

    static void txTaskFunction(UArg arg0, UArg arg1)
    {
    RF_Params rfParams;
    RF_Params_init(&rfParams);

    /* Configure TX Entry */
    txEntry.pNextEntry = (uint8_t*)&txEntry;
    txEntry.status = DATA_ENTRY_PENDING;
    txEntry.config.type = DATA_ENTRY_TYPE_PTR;
    txEntry.pData = (uint8_t*)txSyncPacket;
    txEntry.length = sizeof(txSyncPacket);

    /* Configure CMD_PROP_TX_ADV */
    RF_cmdPropTxAdv.pPkt = (uint8_t*)&txQueue;
    RF_cmdPropTxAdv.pktLen = 0;
    RF_cmdPropTxAdv.startTrigger.triggerType = TRIG_ABSTIME;
    RF_cmdPropTxAdv.startTrigger.pastTrig = 1;
    RF_cmdPropTxAdv.startTime = 0;

    txQueue.pCurrEntry = (uint8_t*)&txEntry;
    txQueue.pLastEntry = NULL;

    /* 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);

    /* Get current time */
    sTime = RF_getCurrentTime();
    while(true)
    {
    /* Set absolute TX time to utilize automatic power management */
    sTime += PACKET_INTERVAL;
    RF_cmdPropTxAdv.startTime = sTime;
    packetCounter = 0;
    txQueue.pCurrEntry = (uint8_t*)&txEntry;

    /* Send packet */
    RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxAdv, RF_PriorityNormal, &callback, RF_EventTxEntryDone);
    while(!packetSent);
    packetSent = false;
    }
    }

    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
    if (e & RF_EventTxEntryDone)
    {
    packetCounter++;

    if (packetCounter == (TX_BUFFERS_TO_SEND - 1))
    {
    txQueue.pLastEntry = txQueue.pCurrEntry;
    }
    }
    else if (e & RF_EventLastCmdDone)
    {
    packetSent = true;
    }
    }
  • It turns out that the previous problem is due to the fact that I didn't initialize the RF_cmdPropTxAdv properly....now I can get RF_runCmd() to execute, however, no packet is received by another board running the RFPacketRx example...

  • Siri, could you please take a look at the above documents when you have a chance?

    Thanks
  • I am sorry, but I am not quite sure I understand what exactly you are trying to achieve, as you are asking about a lot of different things. For example, you cannot use the WOR example on the RX side, and then transmit packets back-to back on the TX side, and expect that to work (The WOR example expects a long "normal" preamble, as used in the WOR TX example).

    If you want to run WOR, you should use the WOR example we provide, and use a long "normal" preamble.

    What are you trying to achieve with sending packets back-to back and use WOR?
    If you need an application that transmit packets back to back, you need to make sure you have a receiver that can receive and process the packets fast enough.
  • I am not trying to get WOR working, I am trying to modify WOR so the back to back TX works, since they both used the advanced TX command. I wasn't able to use your code directly, and I thought it might be some setting issues, which is why I started with WOR.

    My RX is always receiving.

  • Could you please reply? Thanks!

  • I took the rfPacket TX example from the latest SDK and replaced the rfPacketTX.c file with the following:

    /*
     * Copyright (c) 2017, Texas Instruments Incorporated
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     *
     * *  Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     *
     * *  Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * *  Neither the name of Texas Instruments Incorporated nor the names of
     *    its contributors may be used to endorse or promote products derived
     *    from this software without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /***** Includes *****/
    /* Standard C Libraries */
    #include <stdlib.h>
    #include <unistd.h>
    
    /* TI Drivers */
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /* Driverlib Header files */
    #include DeviceFamily_constructPath(driverlib/rf_prop_mailbox.h)
    
    /* Board Header files */
    #include "Board.h"
    #include "smartrf_settings/smartrf_settings.h"
    #include "RFQueue.h"
    
    /***** Defines *****/
    
    /* Do power measurement */
    //#define POWER_MEASUREMENT
    
    /* Packet TX Configuration */
    #define PAYLOAD_LENGTH      30
    #ifdef POWER_MEASUREMENT
    #define PACKET_INTERVAL     5       /* For power measurement set packet interval to 5 s */
    #else
    #define PACKET_INTERVAL     500000  /* Set packet interval to 500000us or 500 ms */
    #endif
    
    #define TX_BUFFERS_TO_SEND  100
    
    /***** Prototypes *****/
    
    /***** Variable declarations *****/
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    /* Pin driver handle */
    static PIN_Handle ledPinHandle;
    static PIN_State ledPinState;
    
    static uint8_t packet[] = {0x55, 0x55, 0x55, 0x55, // Preamble
                               0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
                               0x93, 0x0B, 0x51, 0xDE, // Sync Word
                               0x01, // Length byte
                               0xAA, // Dummy payload
                               0x05, 0xF2, // CRc
    };
    
    
    static rfc_dataEntryPointer_t txEntry;
    static dataQueue_t txQueue;
    static uint8_t packetCounter = 0;
    static uint8_t packetSent = false;
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    /*
     * Application LED pin configuration table:
     *   - All LEDs board LEDs are off.
     */
    PIN_Config pinTable[] =
    {
        Board_PIN_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    #if defined Board_CC1352R1_LAUNCHXL
        Board_DIO30_RFSW | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    #endif
    #ifdef POWER_MEASUREMENT
    #if defined(Board_CC1350_LAUNCHXL)
        Board_DIO30_SWPWR | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    #endif
    #endif
        PIN_TERMINATE
    };
    
    /***** Function definitions *****/
    
    void *mainThread(void *arg0)
    {
        RF_Params rfParams;
        RF_Params_init(&rfParams);
    
        /* Open LED pins */
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
    
    #ifdef POWER_MEASUREMENT
    #if defined(Board_CC1350_LAUNCHXL)
        /* Route out PA active pin to Board_DIO30_SWPWR */
        PINCC26XX_setMux(ledPinHandle, Board_DIO30_SWPWR, PINCC26XX_MUX_RFC_GPO1);
    #endif
    #endif
    
        /* Configure TX Entry */
        txEntry.pNextEntry = (uint8_t*)&txEntry;
        txEntry.status = DATA_ENTRY_PENDING;
        txEntry.config.type = DATA_ENTRY_TYPE_PTR;
        txEntry.pData = (uint8_t*)packet;
        txEntry.length = sizeof(packet);
    
        /* Configure CMD_PROP_TX_ADV */
        RF_cmdPropTxAdv.pPkt = (uint8_t*)&txQueue;
        RF_cmdPropTxAdv.pktLen = 0;
        RF_cmdPropTxAdv.startTrigger.triggerType = TRIG_NOW;
    
        txQueue.pCurrEntry = (uint8_t*)&txEntry;
        txQueue.pLastEntry = NULL;
    
        /* 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)
        {
            packetCounter = 0;
            txQueue.pCurrEntry = (uint8_t*)&txEntry;
    
            /* Send packet */
            RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxAdv,
                                                       RF_PriorityNormal, &callback, RF_EventTxEntryDone);
    
            while(!packetSent);
            packetSent = false;
    
    #ifndef POWER_MEASUREMENT
            PIN_setOutputValue(ledPinHandle, Board_PIN_LED1,!PIN_getOutputValue(Board_PIN_LED1));
    #endif        
            /* Power down the radio */
            RF_yield(rfHandle);
    
    #ifdef POWER_MEASUREMENT
            /* Sleep for PACKET_INTERVAL s */
            sleep(PACKET_INTERVAL);
    #else
            /* Sleep for PACKET_INTERVAL us */
            usleep(PACKET_INTERVAL);
    #endif
    
        }
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventTxEntryDone)
        {
            packetCounter++;
    
            if (packetCounter == (TX_BUFFERS_TO_SEND - 1))
            {
                txQueue.pLastEntry = txQueue.pCurrEntry;
            }
        }
        else if (e & RF_EventLastCmdDone)
        {
            packetSent = true;
        }
    }
    

    I addition I added the RFQueue.c/h files from the rfPacketRx example.

    My advanced TX command was as follows:

    rfc_CMD_PROP_TX_ADV_t RF_cmdPropTxAdv =
    {
        .commandNo = 0x3803,
        .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 = 0x0,
        .pktConf.bCrcIncSw = 0x0,
        .pktConf.bCrcIncHdr = 0x0,
        .numHdrBits = 0x00,
        .pktLen = 0x0000,
        .startConf.bExtTxTrig = 0x0,
        .startConf.inputMode = 0x0,
        .startConf.source = 0x0,
        .preTrigger.triggerType = 0x0,
        .preTrigger.bEnaCmd = 0x0,
        .preTrigger.triggerNo = 0x0,
        .preTrigger.pastTrig = 0x0,
        .preTime = 0x00000000,
        .syncWord = 0x55,
        .pPkt = 0 // INSERT APPLICABLE POINTER: (uint8_t*)&xxx
    };
    

    I also set the number of sync word bits to 8:

    .formatConf.nSwBits = 0x8,

    I could receive all 100 packets with SmartRF Studio.

  • Hi Siri,

    Thank you very much for the response! I set up the latest SDK with the code example that you provided and used another launchpad as the receiver, it seems to be able to receive the data! However, if I change the dummy load from 0xAA to something else, or change the length of the load, the receiver is unable to receive any packets again...Is there a filter being set somewhere? My receiver is just running the sample PacketRx code.

    Is .formatConf.nSwBits = 0x8, only need to be set on the TX side under radio settings?
  • You should not change the number of sync word bits on the RX side. The receiver is still locking for the 4 byte long sync word that you write to the TX FIFO.

    I changed the TX packet to be 16 bytes instead of 1:

    static uint8_t packet[] = {0x55, 0x55, 0x55, 0x55, // Preamble
                               0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
                               0x93, 0x0B, 0x51, 0xDE, // Sync Word
                               16, // Length byte
                               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, // Dummy payload
                               0x90, 0x0E, // CRC
    };

    I received all packet with SmartRF Studio.

    Siri