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.

CC2640R2F: GPIO interrupt and RTOS context switching

Part Number: CC2640R2F
Other Parts Discussed in Thread: CC2640, CC2642R

Hi team

I am using CC2640R2F, I have a bug that I need to fix, I think it may have something to do with my GPIO interrupt being interrupt with context switching in TI-RTOS.

So my question is: 

  • Can TI-RTOS context switching interrupt my GPIO interrupt?
  • If yes, is there a way to set my GPIO interrupt priority higher that the TI-RTOS context switching so my GPIO won't be interrupted?

Thanks team!

  • Hi Zhuhua,

    What version of the SimpleLink CC2640R2 SDK are you using, and what example project?

    TI RTOS will not switch context during the GPIO interrupt itself (the hardware interrupt), but software running consequently can be preempted by TI RTOS. To avoid this you can insert a critical section. You can read about how to do this in the TIRTOS Kernel User's Guide, 8.2.4 Enabling and Disabling Interrupts: 

     

  • Hi Marie

    Thanks for the reply, here are some information about our system:

    • SDK v3.30.0.20
    • TI compiler: v20.2.2.LTS
    • CCS: v10.0.0.00010

    It's my own project, and I am testing the final code. Part of the code is using a GPIO interrupt to decode a proprietary protocol. There are signal to mark the START and STOP of a packet, but I will just focus on one bit for now.

    Here is what one bit look like:

    |__|¯¯|   bit 1, positive = 320uS, negative = 320uS

    |___|¯|   bit 0, positive = 160uS, negative = 480uS

    here is how I measuring it in my code:

    /* measure negative pulse, as the pulse are both longer on bit 1 and 0 */
    
    voic cb_bus_read(uint_fast_t index){
    
      if(flag_isFallingEdge){
    
        /* get start ticks */
        ticks_start = Clock_getTicks();
        /* change to interrupt on rising edge */
        GPIO_setConfig(PIN_BUS_READ, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_RISING);
        /* update flag */
        flag_isFallingEdge = 0;
    
      }else{
    
        /* get stop ticks */
        ticks_stop = Clock_getTicks();
        /* change to interrupt on failing edge */
        GPIO_setConfig(PIN_BUS_READ, GPIO_CFG_IN_PD | GPIO_CFG_IN_INT_FALLING);
        /* update flag */
        flag_isFallingEdge = 1;
    
        /* decode bits */
        ticks_diff = ticks_start - ticks_stop;
        // decode code removed, it will just decode one byte then put it into a ring buffer
        // for main application
    
      }
    
    }

    My test show there is about 0.5% error rate in lab environment. I want to find out what cause it as I am expecting 0% error rate, I have done similar stuff with a PIC18 and I can achieve a 0% error rate.

    So my follow up question are:

    • How many clock does a context switch need?
    • Can a task context switching be interrupt by a GPIO interrupt?
    • Is Clock_getTicks() the right function to use? I understand that it only have 10us resolution
    • Consider the timing requirement of the above bit 1 & 0, can I differentiate them within TI-RTOS?

    Thanks for your time and looking forward for your replay

  • Hi Zhuhua,

    • How many clock does a context switch need?
      • While the CS itself could be considered having a static delay, the reason behind it could play into the actual delay which makes it hard to give you any number on this. In your case, you could always just disable interrupts during the ISR to avoid higher priority interrupts to delay you. 
    • Can a task context switching be interrupt by a GPIO interrupt?
      • This depends on where in the CS you are, naturally, parts of it is protected by critical sections.
    • Is Clock_getTicks() the right function to use? I understand that it only have 10us resolution
      • You can use this API sure, the Clock module is however driven by the RTC so the single tick resolution is ~31us. 
    • Consider the timing requirement of the above bit 1 & 0, can I differentiate them within TI-RTOS?
      • You could possibly do this but my guess is that you would end up introducing so many interrupts which needs critical sections that you would impact the overall performance of the application

    I would say that this is not the best approach of implementing this on this device. I would advice you to look into the "Sensor Controller" (SC) that exists on the device. This is a small MCU that can operate independently of the ARM core. This means you can use it for things like this where you can dedicate the (SC) to your protocol and have it pass the received data over to the ARM core, leaving the ARM core free to do other things. 

  • Just follow up on solving my problem, is this the right way to disable and enable the interrupt?

    void interrupt_cb(void){
        uint32_t key;
        key = Hwi_disable();
    
        // user code here
    
        Hwi_restore(key);
    }



    Also a couple of relative questions:

    • How do I read and set the interrupt priority of systick (of the task context switching)
    • How do I read and set the interrupt priority of PWM

    Also, I am exploring the idea of using the Sensor controller, but I can't seem to get it to work, could be something silly I have done, can you take a look at my code ? (Full code is also attached).

    The problem is I can't see the scTaskAlertCallback() run.

    4150.test.zip

    /*
     *  ======== hello.c ========
     */
    
    /* XDC Module Headers */
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    
    /* BIOS Module Headers */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    
    #include <ti/drivers/Board.h>
    
    #include "scif.h"
    
    /* task stuff */
    Task_Params     taskParams;
    uint8_t         taskStack[512];
    Task_Struct     taskStruct;
    
    void task(UArg arg0, UArg arg1);
    void scCtrlReadyCallback(void);
    void scTaskAlertCallback(void);
    
    /*
     *  ======== main ========
     */
    int main()
    {
        /* Call driver init functions */
        Board_init();
    
        System_printf("hello world\n");
        System_flush();
    
        Task_Params_init(&taskParams);
        taskParams.priority     = 1;
        taskParams.stack        = taskStack;
        taskParams.stackSize    = 512;
    
        Task_construct(&taskStruct, task, &taskParams, NULL);
    
        // Initialize the SCIF operating system abstraction layer
        scifOsalInit();
        scifOsalRegisterCtrlReadyCallback(scCtrlReadyCallback);
        scifOsalRegisterTaskAlertCallback(scTaskAlertCallback);
    
        // Initialize the SCIF driver
        scifInit(&scifDriverSetup);
    
        // Enable RTC ticks, with N Hz tick interval
        scifStartRtcTicksNow(0x00010000 / 1000);
    
        // Start the "Driver" Sensor Controller task
        scifStartTasksNbl(1 << SCIF_DRIVER_TASK_ID);
    
        BIOS_start();
        return(0);
    }
    
    void task(UArg arg0, UArg arg1){
        System_printf("Starting test task...\n");
        System_flush();
    
        uint16_t counter = 0;
    
        for(;;){
            Task_sleep(100 * 1000); // one second
            System_printf("counter = 0x%04x\n", counter++);
            System_flush();
        }
    }
    
    // SCIF driver callback: Task control interface ready (non-blocking task control operation completed)
    void scCtrlReadyCallback(void) {
        // Clear the ALERT interrupt source
        scifClearAlertIntSource();
    
        System_printf("In READY callback\n");
        System_flush();
    
        // Acknowledge the ALERT event
        scifAckAlertEvents();
    }
    
    // SCIF driver callback: Sensor Controller task code has generated an alert interrupt
    void scTaskAlertCallback(void) {
        // Clear the ALERT interrupt source
        scifClearAlertIntSource();
    
        System_printf("In ALERT callback\n");
        System_flush();
    
        // Acknowledge the ALERT event
        scifAckAlertEvents();
    }
    

  • Hi Z,

    Yes, that would be the way to make a critical section.

    "How do I read and set the interrupt priority of systick (of the task context switching)":


    You don't. There is no concept of "SysTick" as the Ti-RTOS is working in dynamic mode. CS will happen based on API interactions and events. For example, if a task use a "post" API, the scheduler will be invoked. Basically, dynamic mode means the scheduler only runs when there is a reason to, it does not periodically check for the state. 

    "How do I read and set the interrupt priority of PWM"I

    All drivers typically support setting interrupt priority as part of the "hwAttrs" in the board file. The basic PWM driver do not generate interrupts at all.

    As for the sensor controller code, I can't really give you any feedback, all the code you sent is limited to the M3 side. Without knowing how the SC code looks like and what it should be doing, it is impossible to say why it does not work as you intend it to. maybe you can shed some light on which SC project you are trying out?

  • M-W said:

    As for the sensor controller code, I can't really give you any feedback, all the code you sent is limited to the M3 side. Without knowing how the SC code looks like and what it should be doing, it is impossible to say why it does not work as you intend it to. maybe you can shed some light on which SC project you are trying out?

    My full SC project (M3 side and SC code) is attached in my last post, And I have attached the full project code and the SC only project for you to take a look. In my full project code, I can only see the alert ready call back. But I don't see the alert call back.

    In my SC only project, I tested it on the sensor controller studio, with the task testing, fwGenAlertInterrupt() should have been call, but I don't know how to know if it actual been call.

    // In Initialization Code
    fwScheduleTask(0);
    
    // In Execution code;
    state.counter += 1;
    
    ifnot (state.counter & 0x000F){
        output.counter += 1;
        fwGenAlertInterrupt();
    }
    
    fwScheduleTask(0);
    
    // Nothing in Termination code

    Also, thanks for your help, I am able to increase the priority level of the GPIO interrupt, and my packet error rate is reduced down to 0.02%. However, it looks like the GPIO interrupt call back is NOT run within the hwi thread but instead run on the swi thread, thats explain the error rate.

    From ROV, I can see the GPIO hwi is calling a func PIN_hwi(), can I replace this function with my own hwi call back so my interrupt code is run in the hwi thread?

    Here is what I have try, but my code ended up freeze:

    void my_pin_isr(UArg uarg){
      \\ nothing for now
    }
    
    Hwi_Params hwiParams;
    Hwi_Params_init(&hwiParams);
    hwiParams.enableInt = TRUE;
    cc3_Hwi = Hwi_create(16, my_pin_isr, &hwiParams, NULL); // vector 16 is GPIO edge interrupt

    Looking forward to your feedback.

    5826.full.zip

    sc_only.zip

  • Hi Z,

    Yes, the GPIO (and any other driver callback) is run from a SWI context. The GPIO driver in this case is a wrapper on the device specific PIN driver. it is in there for portability between devices as not all the Simplelink devices support the "PIN" driver. 

    i would say there is no way for you to change this Hwi (there is always a way but in this case it is not recommended).  Doing so would likely break any driver depending on the PIN driver and is a good way to introduce issues into the system. You can try to bump the SWI priority as well, but that is about it in terms of using theses drivers.

    As for the SC code, if you refer to the help section, you will find that fwScheduleTask(0); == "Do not schedule". in other words, the code you try to run will not run as you expect it as it never schedules it self. The minimum value to set here is 1 (which is the default in the example you based the code on). 

    I'm very confident in what you are trying to do is best done on the sensor controller.

  • Hi M-W

    First of all, thanks for answer all my questions. It's been great help and I learn a lot about TI-RTOS and the CC2640.

    So I have try a few thing to use the SC to decode my signal:

    Here how my signal look like:

    |__|¯¯|   bit 1, positive = 320uS, negative = 320uS

    |___|¯|   bit 0, positive = 160uS, negative = 480uS

    First I try to use the GPIO Event trigger, as of the document:

    If both Sensor Controller and System CPU are in standby mode when the GPIO event trigger occurs, the wake-up is delayed by approximately 400 us. This delay includes: 

    Is there a way for me to force the SC on full speed so I don't have the 400us delay? I have disable the power policy in my TI-RTOS already with this:

    Power_disablePolicy();

    But I am still missing interrupts.

    The second method I tried was using the good good polling in an infinity loop, but the code doesn't seem to do anything

    // in Initialization code
    fwScheduleTask(1);
    
    // in Execution code, testing a super loop
    // cfg.run is set to 1
    while(cfg.run == 1){
        // 50ms delay
        fwDelayUs(50000, FW_DELAY_RANGE_100_MS);
        // generate a interrupt for testing
        fwGenAlertInterrupt();
    }
    
    
    // in Termination code
    // nothing

  • Hi Z,

    As you noted, on CC2640R2 you would need to keep the Sensor Controller active to be sure to meet the timings. I guess one could get away with the 400 us delay depending on the negative or positive side of the pulse is sent first but the uncertainty of the system state and actual delay makes this hard.

    What is wrong with the loop is that you use the "non-Quick" version of the alert interrupt. If you look closer at the help section, you will find that "fwGenAlertInterrupt()" only triggers after the execution block is done (in other words, the end of  your code). This means that it will not trigger until you escape the loop. To make interrupts while running, you need to use the "fwGenQuickAlertInterrupt()" API.

    As for forcing SC to be on, the second method is a good option. If you had been basing the design of the newer CC2642R there would have been a "2 MHz" mode which would give you more flexibility as it provides better wake up times (among other things).