How to Handle Interrupts in FreeRTOS?


How to Handle Interrupts in FreeRTOS?

Interrupt handling is a crucial aspect of programming in FreeRTOS, as it allows for efficient handling of external events and interrupts. These interrupts can come from a variety of sources such as timers, sensors, or other peripherals. In this blog post, we will discuss how to handle interrupts in FreeRTOS using the Arduino IDE and the FreeRTOS interrupt management functions.

What is Interrupt?

First, it is important to understand the concept of an interrupt. Interrupts are signals sent to the microcontroller to indicate that an event has occurred. These events can be triggered by external devices, such as sensors or buttons, or by internal events, such as a timer reaching a certain value. When an interrupt occurs, the microcontroller stops its current task and handles the interrupt before returning to the previous task.

An interrupt in FreeRTOS is a hardware event that triggers the processor to temporarily suspend the current task and execute a predefined interrupt service routine (ISR) to handle the interrupt event. Interrupts are commonly used to handle events that occur asynchronously, such as receiving data on a serial port, detecting a timer event, or responding to a hardware interrupt from a peripheral device.

In FreeRTOS, interrupts are managed using a priority-based interrupt handling mechanism.The FreeRTOS kernel assigns a priority level to each interrupt, and when an interrupt occurs,the kernel will temporarily suspend the current task and execute the ISR associated with the highest priority interrupt. After the ISR completes, the kernel will then resume the previously interrupted task, ensuring that no high-priority events are missed.

In FreeRTOS, interrupts are handled using the FreeRTOS interrupt management functions. These functions include:

vPortEnterCritical(): This function disables interrupts and is typically used at the start of a critical section of code.

vPortExitCritical(): This function re-enables interrupts and is typically used at the end of a critical section of code.

xPortDisableInterrupts(): This function disables interrupts and returns the previous interrupt state.

xPortEnableInterrupts(): This function re-enables interrupts and sets the interrupt state to the value passed in.

To use these functions in the Arduino IDE with ESP32 Board, we can use the interrupt management functions in our code. For example, let's say we have a button connected to pin 2 of our Arduino board and we want to handle the button press as an interrupt. We can use the following code to handle the interrupt:


#if CONFIG_FREERTOS_UNICORE

#define ARDUINO_RUNNING_CORE 0

#else

#define ARDUINO_RUNNING_CORE 1

#endif


portMUX_TYPE mySpinlock;

#define LED_BUILTIN 2


//interrupt service routine (ISR) for button press

void buttonPressISR() {

    //disable interrupts

    vPortEnterCritical(&mySpinlock);

    //perform button press actions

    digitalWrite(LED_BUILTIN, HIGH); 

    //enable interrupts

    vPortExitCritical(&mySpinlock);

}


//setup function

void setup() {

     pinMode(LED_BUILTIN, OUTPUT);

     spinlock_initialize(&mySpinlock);

     digitalWrite(LED_BUILTIN, LOW); 

    //attach interrupt to button press

      attachInterrupt(digitalPinToInterrupt(15), buttonPressISR, RISING);

}

void loop()

{

 }

In this example, we have defined an interrupt service routine (ISR) for the button press. The ISR is called every time the button press is detected. Inside the ISR, we first disable interrupts using the vPortEnterCritical() function. This is to ensure that the microcontroller does not handle any other interrupts while the button press is being handled. We then perform the actions for the button press, such as setting LED.on Finally, we re-enable interrupts using the vPortExitCritical() function.

Example to set up a software timer interrupt

Here is an example of how to set up a software timer interrupt using FreeRTOS for Arduino using the Arduino IDE.


#if CONFIG_FREERTOS_UNICORE

#define ARDUINO_RUNNING_CORE 0

#else

#define ARDUINO_RUNNING_CORE 1

#endif


xTimerHandle timer;

#define LED_BUILTIN 2

//interrupt service routine (ISR) for button press

void vTimerCallback(xTimerHandle pxTimer) {

    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));

}


//setup function

void setup() {

     pinMode(LED_BUILTIN, OUTPUT);

     digitalWrite(LED_BUILTIN, LOW); 

    //attach interrupt to button press

    // initialize software timer interrupt

    timer = xTimerCreate("Timer", pdMS_TO_TICKS(1000), pdTRUE, (void *)0, vTimerCallback);

    xTimerStart(timer, 0);

}

void loop()

{

 

}


FreeRTOS has special APIs to be used in the interrupt service routines such as xSemaphore Give From ISR xQueue Send From ISR xQueue Send To BackFrom ISR etc You can refer the documentation for FreeRTOS given on FreeRTOS site Now Let us see the code to implement interrupt handling in freeRTOS

What is deferred interupt in freeRTOS?

In FreeRTOS, a deferred interrupt is a mechanism for handling interrupts that allows time-critical interrupt service routines (ISRs) to defer non-time-critical processing to a later time, after the interrupt has been handled. This is achieved by using a special deferred interrupt processing mechanism, which is built into the FreeRTOS kernel.

The basic idea behind deferred interrupt processing is to minimize the amount of time that the processor spends executing interrupt service routines, which can be critical for real-time systems. Instead of performing all of the processing required by an interrupt within the ISR, deferred interrupt processing enables some of this processing to be deferred to other task.


Interrupt handling in FreeRTOS Code 

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif


hw_timer_t *My_timer = NULL;


SemaphoreHandle_t binaraySemaphore;

static portMUX_TYPE my_spinlock = portMUX_INITIALIZER_UNLOCKED;

void IRAM_ATTR onTimer(){

  BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  
  portENTER_CRITICAL_ISR(&my_spinlock);
  
  xSemaphoreGiveFromISR(binaraySemaphore,&xHigherPriorityTaskWoken);
   
  portEXIT_CRITICAL_ISR(&my_spinlock);
  
  if(xHigherPriorityTaskWoken) portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

 }


void Task1(void * pv)
{

  while(1)
  {
      if (xSemaphoreTake(binaraySemaphore, portMAX_DELAY) == pdTRUE)
      {
        
        Serial.println("Task 1");
        
        
      }
     
  }
  
}

void Task2(void * pv)
{

  while(1)
  {
      
      Serial.println("Task 2 begin");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
      Serial.println("Task 2 ends");
      
      vTaskDelay(200 / portTICK_PERIOD_MS);   
  
  }
  
}


void Task3(void * pv)
{

  while(1)
  {

      Serial.println("Task 3 begin");
      vTaskDelay(1000 / portTICK_PERIOD_MS);
      Serial.println("Task 3 ends");
      
      vTaskDelay(200 / portTICK_PERIOD_MS);   
  
  }
  
}


void setup() {
  
    Serial.begin(115200);
    
    binaraySemaphore = xSemaphoreCreateBinary();
    portMUX_INITIALIZE(&my_spinlock);

    
    xTaskCreatePinnedToCore(Task1,"Task1",1024,NULL,1,NULL,ARDUINO_RUNNING_CORE);
    xTaskCreatePinnedToCore(Task2,"Task2",1024,NULL,1,NULL,ARDUINO_RUNNING_CORE);
    //xTaskCreatePinnedToCore(Task3,"Task3",1024,NULL,3,NULL,ARDUINO_RUNNING_CORE);

    
    My_timer = timerBegin(0, 80, true);
    timerAttachInterrupt(My_timer, &onTimer, true);
    timerAlarmWrite(My_timer, 1000000, true);
    timerAlarmEnable(My_timer); //Just Enable
   
}

void loop() {
}



In this code, the timer overflow interrupt is used. In the ISR the Binary semaphore is given.
The Task 1 waits for the availability of the binary semaphore. When the binary semaphore is received
by the Task 1 then it executes the job and again goes into the waiting mode. The task 2 is a task which is runs continuously.
When the ISR occurs in the middle of the execution of the Task 2 then the ISR releases the binary semaphore. After the execution, the program control is transferred to the task one which is waiting for the semaphore instead of Task 2 which should have got the control.So this is because the priority of the Task one becomes higher because of the API xSemaphoreGiveFromISR .

It's worth noting that the FreeRTOS interrupt management functions are designed to be used in conjunction with the FreeRTOS scheduler. This means that if the scheduler is not running, the interrupt management functions will not work as expected. Therefore, it's important to ensure that the scheduler is running before using the interrupt management functions in your code.For Arduino IDE there no neeed write statement for starting scheduler.It is taken care by the Arduino only.



Post a Comment

Previous Post Next Post