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/LAUNCHXL-CC1352P: Rx power down with RF_yield doesn't yield power savings

Part Number: LAUNCHXL-CC1352P
Other Parts Discussed in Thread: ENERGYTRACE, SYSBIOS, SYSCONFIG

Tool/software: Code Composer Studio

Hi,

I am modifying the rfPacketRx example you have provided.  I am receiving packets with a regular period of space in between each, whose period is not very strict.  My code is working, but my power as measured by EnergyTrace is high (6mA average).  It is even higher (8mA average) when the board is receiving no signal. 

At the end of each received packet, I run RF_yield, then use a timer to run RF_runCmd again before the next packet will come.  I can vary this timer from long to short, and my power usage doesn't change.  When I run EnergyTrace++, it shows that my RF stays in rxwait 100% of the time.  

If I run the rfWakeOnRadioRx example, my power usage is 1mA average, and EnergyTrace++ shows that the RF stays off most of the time.  How do I get the radio in that state?  I thought RF_yield was supposed to do it, but as I said I see no power savings.  

I also implemented some timers to watch for a long period of no input and run RF_yield, and I can verify on the logic analyzer that I'm getting into that state, but my power does not change.

Thank you

  • Keith,

    You would need to provide more information as for what you do in software. Simply turning of the radio would not mean you go into power saving modes depending on how the rest of the application is setup. Could you maybe share your code modifications for a review?

  • Thank you for offering to look at my code.  I've stripped out the portions that actually use the data once it is received.  Please tell me anything you think I could change, even if unrelated to my question.  Thanks!

    #include <stdlib.h>
    
    /* TI Drivers */
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/SPI.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>
    typedef unsigned char u_char;
    typedef unsigned int u_int;
    
    /***** Defines *****/
    
    /* Packet RX Configuration */
    #define DATA_ENTRY_HEADER_SIZE 8  /* Constant header size of a Generic Data Entry */
    #define MAX_LENGTH             100 /* Max length byte the radio will accept */
    #define NUM_DATA_ENTRIES       60
    #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) */
    
    
    
    /***** Prototypes *****/
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    /***** Variable declarations *****/
    
    /* Pin driver handle */
    static PIN_Handle ledPinHandle;
    static PIN_Handle pinHandle;
    
    /* 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) */
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    
    
    
    static uint8_t packet[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
    
    
    
    PIN_Config pinTable[] =
    {
        CONFIG_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    	PIN_TERMINATE
    };
    PIN_Config gpioTable[] =
    {
        CONFIG_PIN_1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MED,
        PIN_TERMINATE
    };
    
    
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    RF_Params rfParams;
    
    bool radio_on = 1;
    
    // Doorbell
    #include <ti/devices/cc13x2_cc26x2/inc/hw_rfc_dbell.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /***** Function definitions *****/
    
    #include <ti/sysbios/knl/Clock.h>
    #include <xdc/runtime/Error.h>
    Clock_Handle between_frames_clk;
    Clock_Handle timeout_clk;
    Clock_Handle idle_clk;
    Clock_Handle wait_for_rx_clk;
    bool receiver_idle = 0;
    
    // time between packets to disable Rx
    Void between_frames_Clock()
    {
        Clock_stop(between_frames_clk);
        radio_on = 1;
        GPIO_write(DIO21,1);
    //    GPIO_write(DIO21,GPIO_read(DIO21)^1);
        return;
    }
    Void timeout_Clock() 
    {
        receiver_idle = 1;
        PIN_setOutputValue(ledPinHandle, CONFIG_PIN_RLED,0);
        Clock_stop(timeout_clk);
        Clock_start(idle_clk);
        radio_on = 1;
    //    GPIO_write(DIO5,GPIO_read(DIO5)^1);
        return;
    }
    Void idle_Clock()
    {
        Clock_stop(idle_clk);
        Clock_start(wait_for_rx_clk);
        radio_on = 1;
        GPIO_write(DIO23,GPIO_read(DIO23)^1);
        return;
    }
    Void wait_for_rx_Clock()
    {
        Clock_stop(wait_for_rx_clk);
        if (receiver_idle == 0) {
            radio_on = 1;
        } else {
            radio_on = 0;
            RF_yield(rfHandle);
            Clock_start(idle_clk);
    //        GPIO_write(DIO24,GPIO_read(DIO24)^1);
        }
        return;
    }
    
    void *mainThread(void *arg0)
    {
        RF_Params_init(&rfParams);
    
        /* Receive dataQueue for RF Core to fill in data */
        static dataQueue_t dataQueue;
    
        /* Open LED pins */
        static PIN_State ledPinState;
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
        /* Open GPIO pins */
        static PIN_State pinState;
        pinHandle = PIN_open(&pinState, gpioTable);
        if (pinHandle == NULL)
        {
            while(1);
        }
    
    
        HWREG(RFC_DBELL_BASE + RFC_DBELL_O_SYSGPOCTL) =
                RFC_DBELL_SYSGPOCTL_GPOCTL0_RATGPO1 | RFC_DBELL_SYSGPOCTL_GPOCTL1_MCEGPO1;
        PINCC26XX_setMux(pinHandle, CONFIG_PIN_1, PINCC26XX_MUX_RFC_GPO1);
    
    
        Clock_Params between_frames_Params;
        Clock_Params_init(&between_frames_Params);
        between_frames_Params.period = 0;        // 0 is the default, but added for completeness
        between_frames_Params.startFlag = FALSE;
        between_frames_clk = Clock_create(between_frames_Clock, 2000, &between_frames_Params, Error_IGNORE);
    
        Clock_Params timeout_Params;
        Clock_Params_init(&timeout_Params);
        timeout_Params.period = 0;        // 0 is the default, but added for completeness
        timeout_Params.startFlag = TRUE;
        timeout_clk = Clock_create(timeout_Clock, 200000, &timeout_Params, Error_IGNORE);
    
        Clock_Params idle_Params;
        Clock_Params_init(&idle_Params);
        idle_Params.period = 0;        // 0 is the default, but added for completeness
        idle_Params.startFlag = FALSE;
        idle_clk = Clock_create(idle_Clock, 150000, &idle_Params, Error_IGNORE);
    
        Clock_Params wait_for_rx_Params;
        Clock_Params_init(&wait_for_rx_Params);
        wait_for_rx_Params.period = 0;        // 0 is the default, but added for completeness
        wait_for_rx_Params.startFlag = FALSE;
        wait_for_rx_clk = Clock_create(wait_for_rx_Clock, 5000, &wait_for_rx_Params, Error_IGNORE);
    
    
        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.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 1;
    
        /* 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) {
     
        if (radio_on) {
          RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);
    
          // This portion of the program uses packet[] 
        }
    
    
    }
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
    //        GPIO_write(DIO5,0);
            static uint8_t packetLength;
            static uint8_t* packetDataPointer;
            static rfc_dataEntryGeneral_t* currentDataEntry;
    
            /* Toggle pin to indicate RX */
            PIN_setOutputValue(ledPinHandle, CONFIG_PIN_RLED,
                               !PIN_getOutputValue(CONFIG_PIN_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(packet, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
    
            GPIO_write(DIO21,0);
            radio_on = 0;
            RF_yield(rfHandle);
            Clock_stop(timeout_clk);
            Clock_start(timeout_clk);
            receiver_idle = 0;
    
            Clock_start(between_frames_clk);
        }
    }
    

  • Hi,

    By the look of your code, you would never go into standby as you leave your main task always running inside the while(1) loop. Even in the case where "radio_on" is zero, you simply busy spin around the if statement. For the device to go into actual standby, there need to be a place for it "rest". This could be for example waiting for an event/semaphore to be posted, this would block the task and allow the idleTask to run and put you into standby.

  • Thank you for your response.  This is my first experience with using an RTOS, and I was under the impression it would look for things like spinning while loops and go to sleep?  EnergyTrace++ shows that I spend a lot of time with the processor in deep sleep, so it's finding places it can do it.  If I were programming without an RTOS, I'd put a call to put it to sleep then set the timer interrupt to wake it up, but I was hoping the RTOS would take care of it for me.

    That said, I'm more concerned right now with the radio itself going to sleep.  Does that not happen unless the chip is asleep?  

    Thanks!

  • Hi Keith,

    The radio will go turn of/got to sleep as much as possible. Most of the power saving is however done by the power driver from the idle loop. There is really no way for the kernel to find generic idle loops and insert power saving, you would need to make sure the tasks yield to the kernel when they don't need to be active. 

    In your case, you could for example try to add a simple "Task_sleep(N_ticks)" in the while loop to allow the device to enter low-power modes for a period of time. A better alternative in your case would be to use semaphores to block and wake the task. There is a "mutex" example found under the "sysbios" examples that you can look at for this.

  • Lest you think I've let this go stale....I haven't, I'm just still stuck.  

    I'm now attempting to implement tasks, using the mutex.c example.  I basically put my whole program in task 1 and an idle function in task 2.  For whatever reason, now, instead of starting the program in mainThread(), it starts the program in main() inside of main_tirtos.c  This bypasses all of my setup and the program goes nuts.  What have I done wrong here?

    Thanks

  • I was barking up the wrong tree on this one, sorry.  The problem was my Task_construct(&task2Struct, (Task_FuncPtr)task2Fxn, &taskParams, NULL); call.  I copied that code directly from mutex.c, so I still don't know why it failed, but it isn't holding me up.

    I pulled this code out of main and put it in task1, then added the delay:

    Void task1Fxn(UArg arg0, UArg arg1)
    {
      while(1) {
        Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
    
        if (radio_on) {
          RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);
    
          // This portion of the program uses packet[] 
        }
        Semaphore_post(semHandle);
        Task_sleep(2000);
      }
    }

    Everything is functional. but I still see no power savings.  I expected to see significant savings when nothing was happening, as radio_on stays at 0 most of the time, so the program should just spin in Task_sleep(2000) over and over.  Instead I see the same numbers as before.  I was under the impression that the program would go to an idle state when not executing a task, so I thought it would save power.

    Thanks

  • Hi Keith,

    There is a few things you should consider further reworking in small code snippet you shared me. 

    • Consider the Task_sleep() unit - The input argument is in "System ticks" which is given by Clock_tickPeriod (10 on our device) where the period is in us. This means that 2000 equals 20000us, or 20ms. This is not a particular long sleep which is not very power efficient.
    • How you handle the semaphores - Now you post and pend on the semaphore inside the same loop, this serves 0 purpose, you gain nothing by doing this.

    Continuing on the semaphore path, putting down some pseudo code, I would use it something like this:

    Void another_function_such_as_a_clock_callback()
    {
      // Turn on the radio
      Semaphore_post(semHandle);
    }
    
    Void task1Fxn(UArg arg0, UArg arg1)
    {
      while(1) {
        Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
    
        RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);
    
      }
    }

    Notice how there is no sleeps or anything int he task loop, it just sleeps in the semaphore pend until some other part of the application (like a clock timer, you seem to have plenty) post the semaphore to enable the radio.

    Finally, what did you do with the "mainThread" task when moving the RX loop into it's own task? How do you terminate/block it?

  • Thank you for your response.

    20ms is actually intentional--I'm trying to sleep between packets of data.  It should make it sleep about 50% of the time.  The main savings I'm trying to achieve is when the device is not receiving a signal.  There, the timers are designed to provide polling, with the whole chip staying in idle if no data is received during a brief window.  

    It looks like I need to replace my radio_on signal with a semaphore.  That makes sense, I suppose.  In the past I've only used semaphores in multi-threaded programs so I wasn't even sure exactly why I needed them here.  

    My (apparently faulty) understanding of tasks is that there is a default task, idle, that will run if nothing else is running.  So my assumption was that the sleep function would cause the idle task to take over, until the timeout is up and my task grabs the semaphore again.  Please help me understand what is really happening and needs to happen.

    My mainThread function ends in BIOS_start().  I'm not sure I need that, since the main() function in main_tirtos.c would run it after I return, but it's there.

    I attempted to copy most/all of the code out of mutex.c to set this up, but when I attempt to use task2Fxn, CCS doesn't like the call to Task_construct.  If I run the program for a minute then hit pause, it takes me to an error function.  I know which line is doing this because of breakpoints I tried.  Do you know what I should do to debug this?  Whenever I end up in an error function I don't know what to do.  

    Here is my code--the last line hangs:

        /* Construct writer/reader Task threads */
        Task_Params_init(&taskParams);
        taskParams.stackSize = TASKSTACKSIZE;
        taskParams.stack = &task1Stack;
        taskParams.priority = 1;
        Task_construct(&task1Struct, (Task_FuncPtr)task1Fxn, &taskParams, NULL);
    
        taskParams.stack = &task2Stack;
        taskParams.priority = 2;
        Task_construct(&task2Struct, (Task_FuncPtr)task2Fxn, &taskParams, NULL);
    

    It hangs in Error.c in this for loop:

    Void Error_policyMin(Error_Block *eb, Types_ModuleId mod, CString file,
        Int line, Error_Id id, IArg arg1, IArg arg2)
    {
        /* REQ_TAG(SYSBIOS-852) */
        if (eb == NULL || (UInt)Error_policy == (UInt)Error_TERMINATE) {
            for(;;) {
            }
        }
    

    Thanks!

  • Hi Keith, 

    Maybe you could share your new main (and task1/2) code for me to look at? I can't say why it would dislike your second call without seeing more of the context around it. 

    Your fundamental idea of how the idle task works is right, the thing to consider is what state the device is in. Just because the idle loop runs does not mean it will go into power saving mode. This depends on if there is power constraints set in the device or not and/or the time window it has to get into standby. If the "sleep" is to short it would stay active as you would not have time to get into and out of standby fast enough (however, 20 ms should be fine in this regard.

    As for the semaphores, I guess you could argue that this is actually a multi threaded use-case as you have (as it seems) two of your own tasks and a idle task. Another common component to use in these setups is the "Event" module which basically is a semaphore with argument. This means the task can wait for an "Event" (which might be more intuitive) from another part of the application and then act on it. It also means you can easily incorporate more than one feature in the same task if you want.

    Just for sanity check, what kind of power numbers do you expect to see in your use-case and what do you see?

  • Thanks for your response.  I'll try not to overwhelm, but here's the entire program, with the main program code removed.  Warts and all.  

    I'm hoping to see ~1mA when it's not receiving any signal.  Right now the 7mA average I'm seeing when it's working is fine, but I would like to cut that in half if I can.  

    Thanks!

    /***** Includes *****/
    /* Standard C Libraries */
    #include <stdlib.h>
    
    /* XDC module Headers */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    
    /* TI Drivers */
    #include <ti/drivers/rf/RF.h>
    #include <ti/drivers/PIN.h>
    #include <ti/drivers/SPI.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>
    typedef unsigned char u_char;
    typedef unsigned int u_int;
    
    /***** Defines *****/
    
    /* Packet RX Configuration */
    #define DATA_ENTRY_HEADER_SIZE 8  /* Constant header size of a Generic Data Entry */
    #define MAX_LENGTH             100 /* Max length byte the radio will accept */
    #define NUM_DATA_ENTRIES       60
    #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) */
    
    
    
    /***** Prototypes *****/
    static void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e);
    
    /* BIOS module Headers */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    
    #include <ti/drivers/Board.h>
    
    #define TASKSTACKSIZE   512
    
    Void task1Fxn(UArg arg0, UArg arg1);
    Void task2Fxn(UArg arg0, UArg arg1);
    
    Int resource = 0;
    Int finishCount = 0;
    UInt32 sleepTickCount;
    
    Task_Struct task1Struct, task2Struct;
    Char task1Stack[TASKSTACKSIZE], task2Stack[TASKSTACKSIZE];
    Semaphore_Struct semStruct;
    Semaphore_Handle semHandle;
    
    
    SPI_Handle      masterSpi;
    SPI_Params      spiParams;
    SPI_Transaction transaction;
    bool            transferOK;
    int32_t         status;
    
    
    /***** Variable declarations *****/
    
    /* Pin driver handle */
    static PIN_Handle ledPinHandle;
    static PIN_Handle pinHandle;
    
    /* 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) */
    #pragma DATA_ALIGN (rxDataEntryBuffer, 4);
    static uint8_t
    rxDataEntryBuffer[RF_QUEUE_DATA_ENTRY_BUFFER_SIZE(NUM_DATA_ENTRIES,
                                                      MAX_LENGTH,
                                                      NUM_APPENDED_BYTES)];
    
    
    
    static uint8_t packet[MAX_LENGTH + NUM_APPENDED_BYTES - 1]; /* The length byte is stored in a separate variable */
    
    
    
    
    /*
     * Application LED pin configuration table:
     *   - All LEDs board LEDs are off.
     */
    PIN_Config pinTable[] =
    {
        CONFIG_PIN_RLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
        PIN_TERMINATE
    };
    PIN_Config gpioTable[] =
    {
        CONFIG_PIN_1 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MED,
        PIN_TERMINATE
    };
    
    
    static RF_Object rfObject;
    static RF_Handle rfHandle;
    
    RF_Params rfParams;
    
    bool radio_on = 1;
    
    // Doorbell
    #include <ti/devices/cc13x2_cc26x2/inc/hw_rfc_dbell.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /***** Function definitions *****/
    
    #include <ti/sysbios/knl/Clock.h>
    #include <xdc/runtime/Error.h>
    Clock_Handle between_frames_clk;
    Clock_Handle timeout_clk;
    Clock_Handle idle_clk;
    Clock_Handle wait_for_rx_clk;
    bool receiver_idle = 0;
    Void between_frames_Clock()
    {
        Clock_stop(between_frames_clk);
        radio_on = 1;
        GPIO_write(DIO21,1);
    //    GPIO_write(DIO21,GPIO_read(DIO21)^1);
        return;
    }
    Void timeout_Clock()
    {
        receiver_idle = 1;
        PIN_setOutputValue(ledPinHandle, CONFIG_PIN_RLED,0);
        Clock_stop(timeout_clk);
        Clock_start(idle_clk);
        radio_on = 1;
    //    GPIO_write(DIO5,GPIO_read(DIO5)^1);
        return;
    }
    Void idle_Clock()
    {
        Clock_stop(idle_clk);
        Clock_start(wait_for_rx_clk);
        radio_on = 1;
        GPIO_write(DIO23,GPIO_read(DIO23)^1);
        return;
    }
    Void wait_for_rx_Clock()
    {
        Clock_stop(wait_for_rx_clk);
        if (receiver_idle == 0) {
            radio_on = 1;
        } else {
            radio_on = 0;
            RF_yield(rfHandle);
            Clock_start(idle_clk);
    //        GPIO_write(DIO24,GPIO_read(DIO24)^1);
        }
        return;
    }
    
    void *mainThread(void *arg0)
    {
        RF_Params_init(&rfParams);
    //    rfParams.nInactivityTimeout = 50;
    
        SPI_init();
    
        /* Construct BIOS objects */
        Task_Params taskParams;
        Semaphore_Params semParams;
    
        /* Call driver init functions */
        Board_init();
    
        /* Construct writer/reader Task threads */
        Task_Params_init(&taskParams);
        taskParams.stackSize = TASKSTACKSIZE;
        taskParams.stack = &task1Stack;
        taskParams.priority = 1;
        Task_construct(&task1Struct, (Task_FuncPtr)task1Fxn, &taskParams, NULL);
    
        taskParams.stack = &task2Stack;
        taskParams.priority = 2;
        Task_construct(&task2Struct, (Task_FuncPtr)task2Fxn, &taskParams, NULL);
    
        /* Construct a Semaphore object to be use as a resource lock, inital count 1 */
        Semaphore_Params_init(&semParams);
        Semaphore_construct(&semStruct, 1, &semParams);
    
        /* Obtain instance handle */
        semHandle = Semaphore_handle(&semStruct);
    
        /* We want to sleep for 10000 microseconds */
        sleepTickCount = 10000 / Clock_tickPeriod;
    
    
    
        /* Receive dataQueue for RF Core to fill in data */
        static dataQueue_t dataQueue;
    
        /* Open LED pins */
        static PIN_State ledPinState;
        ledPinHandle = PIN_open(&ledPinState, pinTable);
        if (ledPinHandle == NULL)
        {
            while(1);
        }
        /* Open GPIO pins */
        static PIN_State pinState;
        pinHandle = PIN_open(&pinState, gpioTable);
        if (pinHandle == NULL)
        {
            while(1);
        }
    
    
        HWREG(RFC_DBELL_BASE + RFC_DBELL_O_SYSGPOCTL) =
                RFC_DBELL_SYSGPOCTL_GPOCTL0_RATGPO1 | RFC_DBELL_SYSGPOCTL_GPOCTL1_MCEGPO1;
        PINCC26XX_setMux(pinHandle, CONFIG_PIN_1, PINCC26XX_MUX_RFC_GPO1);
    
    
        Clock_Params between_frames_Params;
        Clock_Params_init(&between_frames_Params);
        between_frames_Params.period = 0;        // 0 is the default, but added for completeness
        between_frames_Params.startFlag = FALSE;
        between_frames_clk = Clock_create(between_frames_Clock, 2000, &between_frames_Params, Error_IGNORE);
    
        Clock_Params timeout_Params;
        Clock_Params_init(&timeout_Params);
        timeout_Params.period = 0;        // 0 is the default, but added for completeness
        timeout_Params.startFlag = TRUE;
        timeout_clk = Clock_create(timeout_Clock, 200000, &timeout_Params, Error_IGNORE);
    
        Clock_Params idle_Params;
        Clock_Params_init(&idle_Params);
        idle_Params.period = 0;        // 0 is the default, but added for completeness
        idle_Params.startFlag = FALSE;
        idle_clk = Clock_create(idle_Clock, 150000, &idle_Params, Error_IGNORE);
    
        Clock_Params wait_for_rx_Params;
        Clock_Params_init(&wait_for_rx_Params);
        wait_for_rx_Params.period = 0;        // 0 is the default, but added for completeness
        wait_for_rx_Params.startFlag = FALSE;
        wait_for_rx_clk = Clock_create(wait_for_rx_Clock, 5000, &wait_for_rx_Params, Error_IGNORE);
    
    
        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.pktConf.bRepeatOk = 0;
        RF_cmdPropRx.pktConf.bRepeatNok = 1;
        RF_cmdPropRx.endTime = 2;
    
        /* 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);
    
    
    
    
        BIOS_start();    /* Does not return */
        return(0);
    
    }
    Void task1Fxn(UArg arg0, UArg arg1)
    {
      int i;
      int j;
    
      while(1) {
        /* Get access to resource */
        Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
    
        /* Enter RX mode and stay forever in RX */
    //        GPIO_write(DIO5,1);
        GPIO_write(DIO22,1);
        if (radio_on) {
          RF_EventMask terminationReason = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                                                   RF_PriorityNormal, &callback,
                                                   RF_EventRxEntryDone);
    
    
    <my code>
    
        Semaphore_post(semHandle);
        GPIO_write(DIO22,0);
    //    Task_sleep(2000);
      }
    }
    
    Void task2Fxn(UArg arg0, UArg arg1)
    {
        for (;;) {
            GPIO_write(DIO22,0);
    
            /* Get access to resource */
            Semaphore_pend(semHandle, BIOS_WAIT_FOREVER);
            Task_sleep(2000);
    
            Semaphore_post(semHandle);
    
        }
    }
    
    
    
    
    void callback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
    {
        if (e & RF_EventRxEntryDone)
        {
    //        GPIO_write(DIO5,0);
            static uint8_t packetLength;
            static uint8_t* packetDataPointer;
            static rfc_dataEntryGeneral_t* currentDataEntry;
    
            /* Toggle pin to indicate RX */
            PIN_setOutputValue(ledPinHandle, CONFIG_PIN_RLED,
                               !PIN_getOutputValue(CONFIG_PIN_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(packet, packetDataPointer, (packetLength + 1));
    
            RFQueue_nextEntry();
    //        GPIO_write(DIO21,GPIO_read(DIO21)^1);
    
            GPIO_write(DIO21,0);
            radio_on = 0;
            RF_yield(rfHandle);
            Clock_stop(timeout_clk);
            Clock_start(timeout_clk);
            receiver_idle = 0;
    
            Clock_start(between_frames_clk);
        }
    }
    

  • Hi Keith,

    I'm confused by your mainThread, it seems it "ends" with you calling BIOS start. Typically this is called from main which sets up mainThread that then run, have you changed this topology? If not, you should not be calling the API again from this thread (you should also consider removing this thread all in all as it would cost you RAM to keep it) as this is not the proper way to "end" a task.

    Please feel free to add in your main() here as well to clarify exactly what is going on.

  • Hi,

    My main() routine in main_tirtos.c is unmodified from the rxPacketRx example I pulled in from the resource explorer.  It does a few things then calls mainThread.  I didn't actually pay attention to the fact that my mainThread function wasn't main until recently.  It essentially functions as such.  I wasn't envisioning it as a task--it just has all of my setup.  Are you saying I need to move all of that setup to the main_tirtos.c file?  That's not what was done in the example.

    I have removed the BIOS_start from mainThread, as it does get called in main().  I guess it matters where it gets called from?

    I am still completely stuck on this Error_policyMin error.  What I don't understand is the Task_construct call I copied from the examples has NULL for the eb, but this function seems to be erroring out on having NULL for an eb.  If you can help me with this, I can at least go back to making progress and experimenting.  

    Thanks!

  • Hi Keith,

    Yes, it sort of matters where you call BIOS_start() from, this is the point where the TI-RTOS is started and task scheduling starts. What this means for you is that the task you construct from your mainThread (task) is put into play right away.

    As you setup "task 2" to a priority higher than what we normally use in the example (2 instead of 1), I would expect this to run straight away when the construct is done. Now, when task 2 runs, you try to use a semaphore which (most likely) are not yet constructed, as you do this after constructing all your tasks. This could thrown you into the error policy spin.

    My suggestion to you is that you try to refactor your program as such:

    * Create "init" functions for each task, in this function, include things such as:

    - Constructing the task

    - Constructing other TI-RTOS objects, such as semaphores, clocks etc (note that construct can be done prior to BIOS_start())

    - Call driver "init" functions. Do not call any "open" calls, these should only be called after BIOS_start()

    * From main(), instead of constructing your "mainThread()", instead call the two "init" functions which will prepare your two tasks.

    * Move any remaining code, such as PIN and RF driver related things into the task that owns them.

    Furthermore, try to avoid mixing APIs. I see you use the PIN driver for GPIO which is fine (I actually prefer this driver over the GPIO one), but you should then use the PIN driver API for reading/writing the IO and not the "GPIO_write()" which is actually a lower-level API.

  • THANK YOU!

    I changed the priority to 2 at some point when I was trying some things out.  I never imagined it would be what was causing my fails.  Simply changing that back to 1 keeps my code from erroring out.  

    I'll work on implementing the other things you've suggested.  

    You say move PIN and RF driver things to tasks--okay so this suggests I was viewing tasks incorrectly.  I guess I was thinking of them like interrupt handlers, where they run over and over and you need to keep them short.  I guess these only really run once, since each task needs a while(1), so it's okay to put setup things in them?  

    As for API's--that was something that I got really confused about.  Even knowing there are two API's helps explain.  I originally used GPIO_write, but then I wanted to add doorbell code and it seemed to require PIN stuff.  I was/am completely confused about having to have a PIN_Config table, when I already set everything up in sysconfig.  I'm unsatisfied with my doorbell solution, as I had to dig through the sysconfig generated files to find what it decided to name the pin I need.  You suggest using the PIN driver--is there a way to use it with sysconfig?  I would prefer to set things up there rather than have these pin tables.  

    Thanks

  • Hi Keith,

    You are correct, tasks are typically "one time" thing, they are basically threads to use another word for it. Either you keep it alive yourself (by having a while(1)) or it will run to completion and stop, you could potentially re-start it (never tried this) but I would say that is not a practical approach here. Keeping them alive with a super loop also means you can do setup prior to the loop as the local variables stay valid.

    There is unfortunately no way to use the PIN driver in sysconfig today, you would need to put the code down yourself. The "GPIO" driver that is in there uses the PIN driver under the hood but it do not give you access to the internal handles etc.

    You could combine using the GPIO and PIN driver (using the later for doorbell routing only) but I personally typically prefer the PIN driver as it gives a bit more freedom and control over what you are doing (and the flash footprint will be lower as long as the GPIO wrapper are not getting linked in).

  • I've been attempting various changes with no luck and today I realized I'm getting ahead of myself.  I think I know my problem, just not how to fix it.  When I turn off the transmitter and step CCS manually down to this command on the receiver, it sits and waits forever:

          RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                    RF_PriorityNormal, &callback,
                    RF_EventRxEntryDone);
    

    I have attempted both of these:

      uint32_t timeoutUs = 100000;
      RF_control(rfHandle, RF_CTRL_SET_INACTIVITY_TIMEOUT, &timeoutUs);
    

    and

        rfParams.nInactivityTimeout = 100000;
    

    Neither seem to work.  I'm looking for a 100ms timeout.  I also tried much smaller numbers to test, with no difference in result.

    If I turn the transmitter back on (another launchpad), the step completes and CCS takes me to the next line.  Even 15 seconds later this happens.  So I think that's why none of my power saving options are working--they are never actually doing anything.  The timers still operate because they are interrupts but RF_yield does nothing since the receiver is still open, and I never end up in the idle state because the receiver is still open.  This also explains why my power use is lower when I'm actually receiving a signal vs when everything is off.  

    I'm hoping this is something simple.  Do you know what I need to do?

    Thanks!

  • Hi Keith,

    Tied does not imply an ongoing command is canceled, just that it should not continue to the next command. Typically this runs on a timer which is the "inactivity timeout" that you have played with above. 

    To stop the operation, you need to call "RF_cancelCmd()" on the RF handle you received from the "runCmd()" call, this will actually stop the operation and let you return. 

  • Thank you.  I can't get this to work properly.  I changed the clock subroutine to this:

    Void wait_for_rx_Clock()
    {
        Clock_stop(wait_for_rx_clk);
        if (receiver_idle == 0) {
            radio_on = 1;
        } else {
            radio_on = 0;
            RF_cancelCmd(rfHandle,rfCmdHandle,1);
    //        RF_yield(rfHandle);
            Clock_start(idle_clk);
        }
        return;
    }
    

    And changed the call to this:

          rfCmdHandle = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                    RF_PriorityNormal, &callback,
                    RF_EventRxEntryDone);
    

    When I run and put a breakpoint on the RF_cancelCmd, it is running periodically.  However, it doesn't take me out of the RF_runCmd.

    The documentation shows RF_cancelCmd only applying to RF_postCmd, so I attempted to change RF_runCmd to that, and indeed that made the RF_cancelCmd work, but it makes my script stop working.  Switching RF_runCmd to RF_postCmd caused me to miss a lot of packets.  Is there a way to make RF_runCmd time out?  I'd rather not convert everything over to using RF_postCmd, since everything is functional now, just taking too much power when it sits and waits to receive forever.  

    Thanks

  • Hi Keith,

    cancel applies to both post and run. Run is just a Post + Pend. You can try make an abrupt abortion -> RF_cancelCmd(rfHandle,rfCmdHandle,0); and see if this helps you. 

    Generally, it is hard to follow just what you are doing as I guess the code has been updated a lot based on the feedback. Being more clear on what the current state is would be helpful. 

  • > Run is just a Post + Pend

    THIS lead me to a fix!  Thank you!  

    RF_runCmd doesn't return an RF_cmdHandle, so I can't run RF_cancelCmd on it, but when I ran just RF_postCmd, it didn't wait for the data before it kept moving.  I replaced my RF_runCmd with both the RF_postCmd and the RF_pendCmd, then then cancel works!  My power went from 9mA average to 3.5mA average.  I'll keep playing with the idle timers to get that lower, but I'm now past where I've been stuck for 2 months.  

    Here's what I ended up with:

            rfCmdHandle = RF_postCmd(rfHandle, (RF_Op*)&RF_cmdPropRx,
                      RF_PriorityNormal, &callback,
                      RF_EventRxEntryDone);
            RF_EventMask terminationReason = RF_pendCmd(rfHandle,rfCmdHandle,RF_EventCmdDone);
    
            if (terminationReason == RF_EventLastCmdDone) {
    

    I will likely get stuck again, but I think I'm past the original problem I described here.  Thank you for the help.