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.

RTOS/LAUNCHXL-CC1310: Radio TX function fails when called from Sw or Hw interrupt handler

Part Number: LAUNCHXL-CC1310
Other Parts Discussed in Thread: CC1310

Tool/software: TI-RTOS

Please help me figuring out a puzzle.

In this pretty basic program for LouchPad CC1310 I have a dedicated function for sending a radio message. Calling this function from the main task works fine. The messages go out every six seconds, as per the algorithm, and RF_runCmd returns 2 (RF_EventLastCmdDone) as it should.

My project, however, requires that I send out messages from event-based functions, such as hardware interrupt handlers and timer event handlers. And I cannot get either of those to work with the Tx function. The example below I have stripped off my application particularly for figuring out this problem. Please help me to get it to work.

Example description:

1. All inits, except for the radio's, are done in "main".

2. SendPacket is the radio function. All the radio stuff is done here - from init to shutting down.

3. txTaskFunction is the main task. It sends messages every 6 seconds. This works fine - messages are sent and received on the other end. In ROV (see screenshot 1) you can see a proof of error-free running in this "interrupt-free" mode. (Note that at the same time the Semaphore page shows an error as per screenshot 3. I don't know why this happens, as I am not using semaphores, and also I am not sure why this error is not reflected in BIOS scan results on screenshot 1.)

4. PinInterrupt is intended to process buttons events. Button 1 should shoot a message, Button 2 should start a 10 seconds timer, who's event handler Timer1Event would also shoot a message.

This is as for the description. Now how it doesn't work. When SendPacket is called from either PinInterrupt or Timer1Event, sending the packet fails. RF_runCmd returns 0 (zero) instead of 2. The program then gets stuck in some strange place in memory, before reaching the end of SendPacket. The ROV results of running it to that point are shown on screenshots 2 to 5 (number 3 is the same as in the "interrupt-free" mode).

Ideas? Thanks in advance!

/*
 * 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.
 */

/***** Includes *****/
#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/drivers/rf/RF.h>
#include <ti/drivers/PIN.h>
#include "Board.h"
#include "smartrf_settings/smartrf_settings.h"

/* Pin driver */
static PIN_State PinState;
static PIN_Handle pinHandle;
PIN_Config pinTable[] =
{
    Board_GLED | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
    Board_BTN1 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE | PIN_HYSTERESIS,
    Board_BTN2 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE | PIN_HYSTERESIS,
    PIN_TERMINATE
};

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

/* Main Task */
Task_Struct         txTask;
static uint8_t      txTaskStack[1024];
static Task_Params  txTaskParams;

/* Radio */
static RF_Object rfObject;
static RF_Handle rfHandle;
static RF_Params rfParams;
#define PAYLOAD_LENGTH 30
static uint8_t packet[PAYLOAD_LENGTH];
static RF_EventMask TxResult;

/* Timers */
Clock_Struct Timer1Struct;
Clock_Handle Timer1Handle;

/* Other */
bool Settling = 0;

/*  ======================  */
/*  ==== Send packet =====  */
/*  ======================  */
static void SendPacket(uint8_t data)
{
    Settling = 1; // Flag to ignore buttons

    /* Construct payload */
    packet[0] = 10; packet[1] = 15; packet[2] = data;

    /* Configure radio */
    RF_Params_init(&rfParams);
    RF_cmdPropTx.pktLen = PAYLOAD_LENGTH;
    RF_cmdPropTx.pPkt = packet;
    RF_cmdPropTx.startTrigger.triggerType = TRIG_ABSTIME;
    RF_cmdPropTx.startTrigger.pastTrig = 1;
    rfHandle = RF_open(&rfObject, &RF_prop, (RF_RadioSetup*)&RF_cmdPropRadioDivSetup, &rfParams);
    RF_postCmd(rfHandle, (RF_Op*)&RF_cmdFs, RF_PriorityNormal, NULL , 0); // Set the frequency

    /* Send packet */
    RF_cmdPropTx.startTime = RF_getCurrentTime()+4000000*0.1f; // Transmit after 0.1 s delay
    TxResult = RF_runCmd(rfHandle, (RF_Op*)&RF_cmdPropTx, RF_PriorityNormal, NULL, 0);
    if (!(TxResult & RF_EventLastCmdDone)) { /*System_abort("Radio transmission error\n");*/ }

    /* Power down the radio to save power */
    RF_close(rfHandle);
    RF_yield(rfHandle);

    /* Blink green LED */
    PIN_setOutputValue(pinHandle, Board_GLED, 1);
    Task_sleep(50*1000/Clock_tickPeriod);
    PIN_setOutputValue(pinHandle, Board_GLED, 0);

    /* Pause for settling */
    Task_sleep(1000*1000/Clock_tickPeriod);
    Settling = 0;
}

/*  ======================  */
/*  === Pin interrupt ====  */
/*  ======================  */
void PinInterrupt(PIN_Handle handle, PIN_Id pinId)
{
    if (Settling) { }                         // Ignore buttons
    else if (pinId == 13) { SendPacket(11); } // Send a packet right away
    else if (pinId == 14) {                   // Start the send delay timer
        Clock_stop(Timer1Handle);
        Clock_start(Timer1Handle);
    }
}

/*  ======================  */
/*  == Timer1 interrupt ==  */
/*  ======================  */
void Timer1Event(UArg arg0)
{
    SendPacket(99); // Send a packet 10 s after Button 2 is pressed
}

/*  ======================  */
/*  ====== Main Task =====  */
/*  ======================  */
static void txTaskFunction(UArg arg0, UArg arg1)
{
    while(1) {
        Task_sleep(5000*1000/Clock_tickPeriod);  // Send a packet approx. every 5 s
        SendPacket(00);
    }
}

/*  ======================  */
/*  ======== Init ========  */
/*  ======================  */
int main(void)
{
    /* Call board init functions. */
    Board_initGeneral();

    /* Open pins */
    pinHandle = PIN_open(&PinState, pinTable);
    if (!pinHandle) { System_abort("Error initializing board pins\n"); }
    if (PIN_registerIntCb(&PinState, PinInterrupt)) { System_abort("Error initializing board pins\n"); }

    /* Initialize timers */
    Clock_Params clkParams;
    Clock_Params_init(&clkParams);
    clkParams.period = 0;
    clkParams.startFlag = FALSE;
    Clock_construct(&Timer1Struct, (Clock_FuncPtr)Timer1Event, 10000*1000/Clock_tickPeriod, &clkParams);
    Timer1Handle = Clock_handle(&Timer1Struct);

    /* Initialize task */
    Task_Params_init(&txTaskParams);
    txTaskParams.priority = 2;
    txTaskParams.stack = &txTaskStack;
    txTaskParams.stackSize = sizeof(txTaskStack);
    txTaskParams.arg0 = (UInt)1000000;
    Task_construct(&txTask, txTaskFunction, &txTaskParams, NULL);

    /* Start BIOS */
    BIOS_start();
}

  • Hi Dmytro,

    I see you are calling Task_sleep() in SendPacket(). Clock functions and PIN callback functions are both executed in SWI (or HWI) interrupt context, hence it is illegal to call Task_sleep() (see BIOS API guide for details). Furthermore, if you look at the source code of the RF driver in <TI RTOS installation directory>\products\tidrivers_cc13xx_cc26xx_X_XX_XX_XX\packages\ti\drivers\rf, you will see that RF_runCmd() calls RF_pendCmd(), which calls functions such as Semaphore_pend() with a non-zero timeout. This tells me that the function is not designed to be called in interrupt context.

    I think one thing you can do is to call SendPacket() in a Task which pends on a Semaphore. This Semaphore can be posted by your Clock and PIN callback functions whenever they wish to send a packet. This way you can ensure that SendPacket() is only called within Task context.

    Best regards,
    Vincent

  • Hi, Vincent. Thank you very much for your answer. This is kind of what I was guessing too, except for the illegal part - I didn't mean to do anything illegal, I swear! :'(

    > I see you are calling Task_sleep() in SendPacket(). Clock functions and PIN callback functions are both executed in SWI (or HWI) interrupt context, hence it is illegal to call Task_sleep()

    I don't mind getting rid of those sleeps in SendPacket, although I would have hoped that the Smarty Miss RTOS would be able to resolve a squabble in between her own flunky idling procedures...

    ...And I have just tried it for the sake of sport. Same exact symptoms. Which takes us to the next part of your answer:

    > Furthermore, if you look at the source code of the RF driver in <TI RTOS installation directory>\products\tidrivers_cc13xx_cc26xx_X_XX_XX_XX\packages\ti\drivers\rf, you will see that RF_runCmd() calls RF_pendCmd(), which calls functions such as Semaphore_pend() with a non-zero timeout. This tells me that the function is not designed to be called in interrupt context.

    I traced it, and now can tell for certain that the CPU gets thrown into abyss only upon the return command of the interrupt function. I.e. it makes its way through the remainder of the SendPacket, then returns from it into the calling interrupt function, and then also works its way through the rest of it, up to the very end. It almost looks like the interrupt ret address in the stack is the only thing that  somehow gets corrupted (or if a wrong stack was used at that stage). Very strange...

    I am very glad that you pointed out the source code location. I was wondering why CCS wouldn't open it for me during the debugging. Is there a way to make this code available in CCS? Ideally I would like to be able to right-click an identifier and see something like "Open Source" just as I see "Open Declaration" (and I would be glad if it worked, too :), because the "Toggle Source/Header" doesn't work for some reason in this case). This is not the only module which is not available of those I'd like to be able to peek inside of. By the way, which would be the right ".c" file, associated with "RF.h" in this case? "C:\ti\tirtos_cc13xx_cc26xx_2_21_00_06\products\tidrivers_cc13xx_cc26xx_2_21_00_04\packages\ti\drivers\rf\RFCC26XX_singleMode.c"? I feel so lost in all these cross references...

    So, if I understood you correctly, whatever system module/driver uses semaphores inside of its functions, is not intended for use within the classic approach to interrupt calls. It sounds kind of strange to me that the drivers decide how we utilize the multitasking features... I probably would feel comfortable enough managing it by manipulating task priorities and idle periods only.

    Aside from the above few clarifications (which, I hope, you will comment on), I understand which direction I should move in - thanks again for your answer.

    By the way, I noticed that for some reason my screenshots didn't get posted within the original post.  I swear I pasted them. Let me try again.

    Best regards,

    Dmytro

  • Hi Dmytro,

    No worries. I was merely using the 2nd defitinition of 'illegal' from merriam-webster.com. No jail time is necessary. :)

    That said, software written on top of the BIOS kernel are expected to follow the rules put forth in the documentation. Otherwise, undefined results occur, such as what you are experiencing.

    > Is there a way to make this code available in CCS?

    The likely reason is that the code you are linking in was compiled with optimization turned on. Optimized code does not work well with source-level debugging, as assembly instructions for the source lines might have been reordered or even eliminated altogether. You would have to rebuild the drivers without optimization in order to properly step into them. For instance, here's a thread with someone trying to do the same: e2e.ti.com/.../488344. If you need more tips regarding how to use CCS, the CCS forum should be able to better assist you.

    > By the way, which would be the right ".c" file, associated with "RF.h" in this case?
    It depends on how your RF driver is configured in your .cfg file. The default is single mode unless it is explicitly set in the file.

    > It sounds kind of strange to me that the drivers decide how we utilize the multitasking features...

    It is actually not that strange. The driver designer usually has to make decisions about how the driver is to be used. In this case, the design is probably done to encourage lower interrupt latency by handling the events in a Task context.

    Best regards,

    Vincent