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.

CC1310 TI-RTOS based RX/TX example

Other Parts Discussed in Thread: CC1310

I have a customer who is looking for an example where we have both tasks txTask_init(ledPinHandle) and rxTask_init(ledPinHandle) working together on a CC1310 LaunchPad.

He has imported the example  "RF Easylink RX"  from "TI Resource Explorer" and added to it the "RF Easylink TX" task and created a semaphore that should switch between those two tasks.

Both tasks work independently but not together. I am trying to have an example working where we enter RX task and wait there until we receive a message.

Once a message is received we then forward that message to TX task and transmit it over radio, then we enter the RX again repeat the for every packet.

 

He also have posted a question to TI E2E Community forum regarding this:

https://e2e.ti.com/support/wireless_connectivity/proprietary_sub_1_ghz_simpliciti/f/156/p/487766/1955110#1955110

 

 

I figured a new post would be best.  Any help is greatly appreciated.  Thanks!

  • Will,
    we need more details to offer advice. The source code would be useful.

    We typically use ROV to see the tasks that are running and the ones that are pending (within CCS menu: Tools/RTOS Object View (ROV)). For this application I would create a semaphore_post inside of the RX interrupt to release the TX task.

    Lenio
  • This thread:  shows how to combine Rx and Tx and could possibly be used as a starting point. 

  • Hello TER,

    Thank you for your reply,

    I was looking at this thread last week as well, and this could be the solution I am looking for. However, I really would like to achieve this with the EasyLink RX/TX API examples if possible and not the RF drivers as in this example.
    Lenio is kind enough to take a look at the EasyLink RX/TX API example and hopefully guide in the right direction.

    Thank you kindly,

    Admir

  • Admir,

    I notice the code has 4 counting semaphores with an intricate cascade of events to post and pend in sequence. I have not tried it yet, but I would suggest reducing those semaphores to one. The RX task would be priority 1 or 2 and it would be running all the time. Once something valid is received, the RX task will post to the semaphore, releasing the TX task. TX task has to be higher priority (3 or more) so it preempts RX, finishes the transmission, and pend to the semaphore again. This "TXsem" could just be a binary semaphore, easier to manage than a counting semaphore. In summary:
    - Use only one binary semaphore
    - switch the current priority of tasks to TX = 3 and RX = 2.
    - Create a test inside RX to validate a message or command. Once the message is checked, post to the semaphore
    - Transmit a message in TX and then pend to the semaphore.

    Do you think that would be reasonable?

    Lenio
  • Yes, this is very reasonable. I have implemented your recommendation. Please see source attached code (via your email).

    Steps I have taken:

    1. Imported "RF easyLink RX" from "TI Resource Explorer"
    2. Copied over the TX task from "RF EasyLink TX" example project.
    3. Commented out both original semaphores found in the RX and TX examples : rxDoneSem and txDoneSem. (that way RX and TX are always running)
    4. Created my own custom binary semaphore "semHandle" that is used control switching between "rfEasyLinkRxFnx" and "rfEasyLinkTxFnx"
    5. Assigned task priority: TX =3 and RX= 2.

    Observations when code is running on two CC1310 LaunchPads:

    1. The "rxTask_init" runs fine on its own when "txTask_init" is commented out: (the RX keeps receiving packets forever and blinks LED1 for each packet received)
    2. The "txTask_init" runs fine on its own when "rxTask_init" is commented out: (the TX keeps sending a random packets forever and blinks LED2 for each packet sent)
    3. When both tasks are enabled at the same time, RX task is running forever, but no packets are received.

    My Question: Is there a radio conflict between the "rxTask_init" and "rxTask_init" running together? Is there any radio clean up necessary before the switch occurs between these two tasks?

    Thank you again for your time and response.

    Admir
  • Hello Lenio,

    I think I have come across something interesting.
    After I commented out the:
    //EasyLink_init(EasyLink_Phy_50kbps2gfsk);
    //EasyLink_setFrequency(915000000);

    in the "rfEasyLinkTxFnx" , I was able to see RX and TX tasks finally toggle and behave as expected.
    I think since "EasyLink_init()" and therefore "Rf_open() "has been called twice (once in RX and once in TX) there was a conflict on the radio side.

    Admir
  • Hi Admir,

    I too found issues with the double initialization of EasyLink. Some portion of the memory must be getting corrupted, but I could not pinpoint the cause at the moment.

    However, I still had to make modifications in your code for it to work properly. All the changes are in the main file frEasyLinkRxTx.c, attached for your review.

    - I reinstated the txDoneSem, removing the comments from the existing code

    - Changed txDoneSem to binary

    - Added a semaphore_pend to the end of tx transmission, so the tx task blocks allowing rx to run.

    - inside of rx, added a semaphore_post to enable tx to run once. I made a simple counter as my criteria for posting to the semaphore, but you should change it to a condition that satisfies the needs for the application.

    rfEasyLinkRxTx - Copy.txt
    /*
     * Copyright (c) 2015-2016, 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.
     */
    
    /*
     *  ======== rfEasyLinkTx.c ========
     */
    /* XDCtools Header files */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/Error.h>
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Clock.h>
    
    /* TI-RTOS Header files */
    #include <ti/drivers/PIN.h>
    
    /* Board Header files */
    #include "Board.h"
    
    /* EasyLink API Header files */
    #include "easylink/EasyLink.h"
    
    /***** Defines *****/
    
    /* Undefine to remove address filter and async mode */
    #define RFEASYLINKRX_ASYNC
    #define RFEASYLINKRX_ADDR_FILTER
    
    #define RFEASYLINKEX_TASK_STACK_SIZE 1024
    #define RFEASYLINKEX_TASK_PRIORITY   2
    
    /* Pin driver handle */
    static PIN_Handle ledPinHandle;
    static PIN_State ledPinState;
    
    
    Semaphore_Struct semStruct;
    Semaphore_Handle semHandle;
    
    
    
    //TX TASK
    /* Undefine to not use async mode */
    #define RFEASYLINKTX_ASYNC
    
    #define RFEASYLINKTX_TASK_STACK_SIZE    1024
    #define RFEASYLINKTX_TASK_PRIORITY      3
    
    #define RFEASYLINKTX_BURST_SIZE         10
    #define RFEASYLINKTXPAYLOAD_LENGTH      30
    
    Task_Struct txTask;    /* not static so you can see in ROV */
    static Task_Params txTaskParams;
    static uint8_t txTaskStack[RFEASYLINKTX_TASK_STACK_SIZE];
    
    static uint16_t seqNumber;
    
    #ifdef RFEASYLINKTX_ASYNC
    static Semaphore_Handle txDoneSem;
    #endif //RFEASYLINKTX_ASYNC
    
    #ifdef RFEASYLINKTX_ASYNC
    void txDoneCb(EasyLink_Status status)
    {
        if (status == EasyLink_Status_Success)
        {
            /* Toggle LED1 to indicate TX */
            PIN_setOutputValue(ledPinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
            Semaphore_pend(semHandle, BIOS_WAIT_FOREVER); //block you se
        }
    
    //    else if(status == EasyLink_Status_Aborted)
    //    {
    //        /* Toggle LED2 to indicate command aborted */
    //        PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
    //    }
        else
        {
            /* Toggle LED1 and LED2 to indicate error */
            PIN_setOutputValue(ledPinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
            PIN_setOutputValue(ledPinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
        }
    
    //    Semaphore_post(txDoneSem);
    }
    #endif //RFEASYLINKTX_ASYNC
    
    static void rfEasyLinkTxFnx(UArg arg0, UArg arg1)
    {
        uint8_t txBurstSize = 0;
    
    #ifdef RFEASYLINKTX_ASYNC
        /* Create a semaphore for Async */
        Semaphore_Params params;
        Error_Block eb;
    
        /* Init params */
        params.mode = Semaphore_Mode_BINARY;
        Semaphore_Params_init(&params);
        Error_init(&eb);
    
        /* Create semaphore instance */
        txDoneSem = Semaphore_create(0, &params, &eb);
    #endif //TX_ASYNC
    
    //    EasyLink_init(EasyLink_Phy_50kbps2gfsk);
        /* Set Freq to 915MHz */
    //    EasyLink_setFrequency(915000000);
        /* Set output power to 12dBm */
        EasyLink_setRfPwr(12);
    
        while(1) {
            EasyLink_TxPacket txPacket =  { {0}, 0, 0, {0} };
    
            /* Create packet with incrementing sequence number and random payload */
            txPacket.payload[0] = (uint8_t)(seqNumber >> 8);
            txPacket.payload[1] = (uint8_t)(seqNumber++);
            uint8_t i;
            for (i = 2; i < RFEASYLINKTXPAYLOAD_LENGTH; i++)
            {
              txPacket.payload[i] = rand();
            }
    
            txPacket.len = RFEASYLINKTXPAYLOAD_LENGTH;
            txPacket.dstAddr[0] = 0xaa;
    
            /* Add a Tx delay for > 500ms, so that the abort kicks in and brakes the burst */
            if(txBurstSize++ >= RFEASYLINKTX_BURST_SIZE)
            {
              /* Set Tx absolute time to current time + 1s */
              txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(100);
              txBurstSize = 0;
            }
            /* Else set the next packet in burst to Tx in 100ms */
            else
            {
              /* Set Tx absolute time to current time + 100ms */
              txPacket.absTime = EasyLink_getAbsTime() + EasyLink_ms_To_RadioTime(100);
            }
    
    #ifdef RFEASYLINKTX_ASYNC
            EasyLink_transmitAsync(&txPacket, txDoneCb);
    
            /* Wait 300ms for Tx to complete */
    //        if(Semaphore_pend(txDoneSem, (300000 / Clock_tickPeriod)) == FALSE)
    //        {
    //            /* TX timed out, abort */
    //            if(EasyLink_abort() == EasyLink_Status_Success)
    //            {
    //                /*
    //                 * Abort will cause the txDoneCb to be called, and the txDoneSem ti
    //                 * Be released. So we must consume the txDoneSem
    //                 * */
                   Semaphore_pend(txDoneSem, BIOS_WAIT_FOREVER);
    //            }
    //        }
    #else
            EasyLink_Status result = EasyLink_transmit(&txPacket);
    
            if (result == EasyLink_Status_Success)
            {
                /* Toggle LED1 to indicate TX */
                PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
            }
            else
            {
                /* Toggle LED1 and LED2 to indicate error */
                PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
                PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
            }
    #endif //RFEASYLINKTX_ASYNC
        }
    }
    
    void txTask_init(PIN_Handle inPinHandle) {
        ledPinHandle = inPinHandle;
    
        Task_Params_init(&txTaskParams);
        txTaskParams.stackSize = RFEASYLINKTX_TASK_STACK_SIZE;
        txTaskParams.priority = RFEASYLINKTX_TASK_PRIORITY;
        txTaskParams.stack = &txTaskStack;
        txTaskParams.arg0 = (UInt)1000000;
    
        Task_construct(&txTask, rfEasyLinkTxFnx, &txTaskParams, NULL);
    }
    
    
    
    
    
    
    
    /*
     * Application LED pin configuration table:
     *   - All LEDs board LEDs are off.
     */
    PIN_Config pinTable[] = {
        Board_LED1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        Board_LED2 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        PIN_TERMINATE
    };
    
    /***** Variable declarations *****/
    static Task_Params rxTaskParams;
    Task_Struct rxTask;    /* not static so you can see in ROV */
    static uint8_t rxTaskStack[RFEASYLINKEX_TASK_STACK_SIZE];
    
    /* The RX Output struct contains statistics about the RX operation of the radio */
    PIN_Handle pinHandle;
    
    #ifdef RFEASYLINKRX_ASYNC
    //static Semaphore_Handle rxDoneSem;
    #endif
    
    /***** Function definitions *****/
    #ifdef RFEASYLINKRX_ASYNC
    void rxDoneCb(EasyLink_RxPacket * rxPacket, EasyLink_Status status)
    {
        if (status == EasyLink_Status_Success)
        {
            /* Toggle LED2 to indicate RX */
            PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
        }
    
    //    else if(status == EasyLink_Status_Aborted)
    //    {
    //        /* Toggle LED1 to indicate command aborted */
    //        PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
    //    }
        else
        {
            /* Toggle LED1 and LED2 to indicate error */
            PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
            PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
        }
    
    //    Semaphore_post(rxDoneSem);
    }
    #endif
    
    static void rfEasyLinkRxFnx(UArg arg0, UArg arg1)
    {
    static uint8_t countLoops;
    
    #ifndef RFEASYLINKRX_ASYNC
        EasyLink_RxPacket rxPacket = {0};
    #endif
    
    #ifdef RFEASYLINKRX_ASYNC
        /* Create a semaphore for Async*/
    //    Semaphore_Params params;
    //    Error_Block eb;
    
        /* Init params */
    //    Semaphore_Params_init(&params);
    //    Error_init(&eb);
    
        /* Create semaphore instance */
    //    rxDoneSem = Semaphore_create(0, &params, &eb);
    #endif //RFEASYLINKRX_ASYNC
    
        EasyLink_init(EasyLink_Phy_50kbps2gfsk);
        EasyLink_setFrequency(915000000);
    
    #ifdef RFEASYLINKRX_ADDR_FILTER
        uint8_t addrFilter = 0xaa;
        EasyLink_enableRxAddrFilter(&addrFilter, 1, 1);
    #endif //RFEASYLINKRX_ADDR_FILTER
    
        while(1) {
    #ifdef RFEASYLINKRX_ASYNC
            EasyLink_receiveAsync(rxDoneCb, 0);
            if(countLoops++ >10)
            {
            	countLoops = 0;
            	Semaphore_post(txDoneSem);
            }
    
    //        /* Wait 300ms for Rx */
    //        if(Semaphore_pend(rxDoneSem, (300000 / Clock_tickPeriod)) == FALSE)
    //        {
    //            /* RX timed out abort */
    //            if(EasyLink_abort() == EasyLink_Status_Success)
    //            {
    //               /* Wait for the abort */
    //               Semaphore_pend(rxDoneSem, BIOS_WAIT_FOREVER);
    //            }
    //        }
    #else
            rxPacket.absTime = 0;
            EasyLink_Status result = EasyLink_receive(&rxPacket);
    
            if (result == EasyLink_Status_Success)
            {
                /* Toggle LED2 to indicate RX */
                PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
                Semaphore_post(semHandle);
            }
            else
            {
                /* Toggle LED1 and LED2 to indicate error */
                PIN_setOutputValue(pinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
                PIN_setOutputValue(pinHandle, Board_LED2,!PIN_getOutputValue(Board_LED2));
            }
    #endif //RX_ASYNC
        }
    }
    
    void rxTask_init(PIN_Handle ledPinHandle) {
        pinHandle = ledPinHandle;
    
        Task_Params_init(&rxTaskParams);
        rxTaskParams.stackSize = RFEASYLINKEX_TASK_STACK_SIZE;
        rxTaskParams.priority = RFEASYLINKEX_TASK_PRIORITY;
        rxTaskParams.stack = &rxTaskStack;
        rxTaskParams.arg0 = (UInt)1000000;
    
        Task_construct(&rxTask, rfEasyLinkRxFnx, &rxTaskParams, NULL);
    }
    
    /*
     *  ======== main ========
     */
    int main(void)
    {
        //create RX/TX semaphore
    	 Semaphore_Params semParams;
    	 semParams.mode = ti_sysbios_knl_Semaphore_Mode_BINARY;
    
    
        /* Call board init functions. */
        Board_initGeneral();
    
        /* Open LED pins */
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if(!ledPinHandle) {
            System_abort("Error initializing board LED pins\n");
        }
    
        /* Clear LED pins */
        PIN_setOutputValue(ledPinHandle, Board_LED1, 0);
        PIN_setOutputValue(ledPinHandle, Board_LED2, 0);
    
        /* Construct a bimary Semaphore object to be use as a resource lock, inital count 0 */
        Semaphore_Params_init(&semParams); /* Init params */
        Semaphore_construct(&semStruct, 0, &semParams); //
    
        /* Obtain instance handle for RX/TX control */
        semHandle = Semaphore_handle(&semStruct);
    
        rxTask_init(ledPinHandle); //rxTask_init works fine on its own but not together with the txTask_init ??
    
        txTask_init(ledPinHandle); ////txTask_init works fine on its own but not together with the rxTask_init ??
    
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

    Lenio

  • Guys, may I chime in here. I think that you over-complicate the problem.

    Although TI-RTOS supports multiple tasks, I generally do not recommend using more than one task if it doesn't add certain value to the application structure and readability. When you access easylink from multiple tasks, you have to treat it like a shared resource. That means: no double initialization, protect mutual access. You have done that already with semaphores.

    Instead, I would suggest to use a single task and implement a state machine that switches states based on events. The tool PlantUML comes in handy for drawing quick state and activity diagram sketches.

    Let's look at an example. An application sends a periodic packet, but from time to time, it needs to send a spontaneous packet with another RF configuration and wait for a acknowledgment reply. The state chart is very simple here as there are only two states incorporated:

    Diagram source code rendered on plantuml.com:

    @startuml
    state PeriodicState {
        PeriodicState : entry / enterPeriodicRfConfig();
        PeriodicState : periodicTimerFired / sendPeriodicPacket();
        PeriodicState : exit / exitPeriodicRfConfig(); 
    }
    
    state IrqState {
        IrqState : entry / enterIrqRfConfig();
        IrqState : do / sendAndReceiveIrqPacket();
        IrqState : exit / exitIrqRfConfig(); 
    }
    
    [*] -> PeriodicState
    PeriodicState --> IrqState : buttonPushed 
    IrqState --> PeriodicState
    
    note "The periodic timer might\nfire in all states. Unhandled\nevents are queued." as N1
    @enduml

    In the application, the state machine is implemented in the following way:

    /* States in the application state machine */
    typedef enum {
        PeriodicState,
        IrqState
    } ApplicationState;
    
    /* Events and states */
    typedef enum {
       ButtonPushedEvent = Event_Id_00,
       PeriodicTimerFiredEvent = Event_Id_01,
    } ApplicationEvent;
    
    /***** Prototypes *****/
    void mainTaskFunction(UArg arg0, UArg arg1);
    
    void periodicClockFunction();
    
    static void enterPeriodicRfConfig();
    static void exitPeriodicRfConfig();
    static void sendPeriodicPacket();
    
    static void enterIrqRfConfig();
    static void exitIrqRfConfig();
    static void sendAndReceiveIrqPacket();
    
    /* Variables */
    ApplicationState currentState = PeriodicState;
    
    /*
    The application has only 1 task. This task encodes the state machine logic.
    State transitions are based on events only.
     */
    void mainTaskFunction(UArg arg0, UArg arg1)
    {
        uint32_t pendingEvents = 0;
        ApplicationState nextState = currentState;
    
        for (;;)
        {
            switch (currentState)
            {
            case PeriodicState:
                // State with 2 possible transitions (1 internal, 1 to another state)
                // Use an event loop.
                enterPeriodicRfConfig();
                while (nextState == currentState)
                {
                    pendingEvents = Event_pend(applicationEvent, 0, PeriodicTimerFiredEvent | ButtonPushedEvent, BIOS_WAIT_FOREVER);
                    if (pendingEvents & PeriodicTimerFiredEvent)
                    {
                        sendPeriodicPacket();
                        nextState = PeriodicState;
                    }
                    else if (ButtonPushedEvent)
                    {
                        nextState = IrqState;
                    }
                };
                exitPeriodicRfConfig();
                break;
            case IrqState:
                // Simple state with only one possible transition. No need for an event loop.
                enterIrqRfConfig();
                sendAndReceiveIrqPacket();
                nextState = PeriodicState;
                exitIrqRfConfig();
                break;
            }
    
            currentState = nextState;
        }
    }

    This code uses one task, the Event module from TI-RTOS to post events to the state machine logic and a periodic clock object. I let XDC generate all the boilerplate setup code from the .cfg file:

    /* ================ Application Specific Instances ================ */
    var event0Params = new Event.Params();
    event0Params.instance.name = "applicationEvent";
    Program.global.applicationEvent = Event.create(event0Params);
    var task0Params = new Task.Params();
    task0Params.instance.name = "mainTask";
    task0Params.stackSize = 2048;
    Program.global.mainTask = Task.create("&mainTaskFunction", task0Params);
    var clock0Params = new Clock.Params();
    clock0Params.instance.name = "periodicClock";
    clock0Params.period = 300000;
    clock0Params.startFlag = true;
    Program.global.periodicClock = Clock.create("&periodicClockFunction", 300000, clock0Params);

    This makes the application code much more readable. For instance, the clock handler in the above example would look like:

    // Called from periodic clock, defined in .cfg file
    void periodicClockFunction()
    {
        Event_post(applicationEvent, PeriodicTimerFiredEvent);
    }

    No other setup code is needed. The same applies to the above task function. The functions for the periodic state look like:

    void enterPeriodicRfConfig()
    {
        // Open RF driver instance with a certain radio setup command and front-end config
        // The setup command is cached in the driver for later power-up.
        rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, NULL);
    
        // Set the frequency. The RF core is now powered up and the CMD_FS is cached for later power up events.
        RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdFsPP, RF_PriorityNormal, NULL, 0);
        if (!(result & RF_EventLastCmdDone))
        {
            while (TRUE);
        }
    }
    
    void exitPeriodicRfConfig()
    {
        RF_close(rfHandle);
    }
    
    void sendPeriodicPacket()
    {
        /* Create packet with incrementing sequence number and random payload */
        packetPP[0] = (uint8_t)(seqNumberPP >> 8);
        packetPP[1] = (uint8_t)(seqNumberPP++);
        uint8_t i;
        for (i = 2; i < PAYLOAD_LENGTH_PP; i++)
        {
            packetPP[i] = rand();
        }
    
        /* Send packet */
        RF_EventMask result = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTxPP, RF_PriorityNormal, NULL, 0);
        if (!(result & RF_EventLastCmdDone))
        {
            while (TRUE);
        }
        /* Power the RF core manually down since there are no more commands pending. */
        RF_yield(rfHandle);
    
        /* Update display */
        Display_print1(uartDisplayHandle, 0, 0, "Pkts sent: %u", seqNumberPP);
        Display_print1(lcdDisplayHandle, 1, 0, "Pkts sent: %u", seqNumberPP);
    
        PIN_setOutputValue(ledPinHandle, Board_LED1,!PIN_getOutputValue(Board_LED1));
    }

    The other ones I leave up to you, but they follow the same pattern.

  • Hello Lenio,

    With you help I was able to get the Easylink RX/TX Tasks working together on a CC1310 Launchpad.

    I have attached the TX/RX example project with source code for others to use as well.

    Essentially, on power up the RX task runs and waits until it receives a packet. Once a packet has been received,  a semaphore(pend) blocks the RX task and allows the TX task to transmit a packet. Once TX completes, it issues a semaphore(post) to enable the RX Task again and the endless loop repeat itself.

    In order to jumpstart the RX/TX board example, I also have attached a fixed transmitter example project that needs to be programmed into a second CC1310 Launchpad board. The fixed transmitter board will transmit a packet once every 10ms second forever.

    Hi Richard,

    Thank you for the improved recommendation. I think having only one task and creating the state machine that runs based on different events instead of tasks makes a lot of sense. I will try to implement the event driven state machine at some point in the near future.

    2287.rfEasyLinkRxTx_CC1310_LAUNCHXL_TI_CC1310F128.zip

    3364.rfEasyLinkTx_CC1310_LAUNCHXL_GNU_CC1310F128.zip

    Thank you again for all of your time and support.

    Sincerely,
    Admir Maglajlic