Handle external interrupt with FreeRTOS

Hi FreeRTOS community,

Could anyone explain to me how to handle an external interrupt using FreeRTOS? I have an interruption in the SPI RX of my Raspberry Pico board, and I don’t know how to configure FreeRTOS to handle this interrupt.

Thank you.

Would you please elaborate what you mean by “FreeRTOS handling interrupt”?

1 Like

Sure, I have enabled the interruption in the SPI RX FIFO. This interruption should trigger when the SPI RX FIFO is not empty, but the Interrupt Service Routine (ISR) function didn’t execute. I don’t know why. I have already tested the same configuration without using FreeRTOS, and everything is working fine. I don’t understand why, when I add FreeRTOS, none of my interrupt functions are executed.

So, my question is how to configure FreeRTOS to run those functions when the interrupt in one of my GPIO or SPI RX FIFO is fired?

You need to enter the address of the isr into the ivt at the right place. Other than that, FreeRTOS does not take on any infra structure for you. It does provide mechanisms for your isr to interact with the os and tasks, but you do not need to use that.

Depending on your mcu, you may need to enable the irq on several levels.

1 Like

Does the FreeRTOS scheduler start or not? Is the tick interrupt firing? Is any of your FreeRTOS task running?

1 Like

Note. FreeRTOS will disable interrupts every time you call most FreeRTOS functions before the scheduler is started, so if you SPI is using interrupt, you should wait until you have started the FreeRTOS scheduler, or you need to manually turn the interrupt on, and not call any FreeRTOS functions while you are doing the I/O, and make sure that none of your other interrupts fire and try to use FreeRTOS services.

1 Like

@aggarg Thank you for answering me. Yes, the scheduler is started. I already have a blink task running and another task sending data over UART.

@RAc Would you kindly provide additional guidance on implementing this? I am currently utilizing the RP2040 platform. Additionally, I would appreciate it if you could share a code example for reference. Thank you

Sorry, I have never used FreeRTOS On the Raspberry. Your best bet is to study existing projects for that platform on github and other repos. With a little luck, someone has already put something together that matches your setup closely enough to clone it. Given how popular the Raspberry is, your chances to find something aren’t that bad at all. Good luck!

1 Like

“Note. FreeRTOS will disable interrupts”
Yes, exactly. This is what I noticed when working with interruptions in hardware timers; all the interruptions are disabled when the scheduler is started. So, if I understand you correctly, I need to stop all FreeRTOS tasks when the interruption is fired and restart them at the end of my ISR (Interrupt Service Routine) function. Is this what you mean? Thank you.

No. But do you enable interrupts in one of your tasks after the scheduler was started and your tasks are running ? Did you also verify that interrupts are globally enabled after the scheduler was started ?
If you successfully run your bare metal application with handling the desired interrupts in ISRs there should be no difference doing so when using FreeRTOS.

No, BEFORE the scheduler is started, FreeRTOS will disable interrupts.

Now, starting the scheduler will affect the one timer that FreeRTOS is using for its tick.

I don’t know the Raspberry port, but I do know that a few ports (perhaps incorrectly) reinitialize the interrupt system as part of bringing up the scheduler. I don’t know if that is part of the problem.

You don’t need to “stop” the tasks, as that is exactly what an interrupt is, the processor stops what it was doing, saves its place so it can return, and then goes into an ISR. When the ISR finishes, the processor will pick up where it started (unless you force a rescheduling that alters this sequence to go to resume a different task instead(.

As @richard-damon has already clarified, DO NOT do that.

If 2 of your tasks are running, then the PendSV and SysTick interrupts are firing. Are you sure that you have enabled the SPI interrupt? Can you try moving the interrupt setup code to a task?

@aggarg
this is my code


#include "pico/stdlib.h"
#include "FreeRTOS.h"
#include "task.h"


uint8_t data;
bool data_isReady;



void TIMO_SPI_RX_INTERRUPT(){
	
	printf("in FUN spi_rx interrupt \n");
	if(!data_isReady){
	data = (uint8_t)spi_get_hw(Timo_spi_port)->dr;
	data_isReady = true;
	}
	

}

void spi_init(){

    spi_init(spi0,2000*1000);
    gpio_set_function(spi_RX,GPIO_FUNC_SPI);
    gpio_set_function(spi_TX,GPIO_FUNC_SPI);
    gpio_set_function(spi_sck,GPIO_FUNC_SPI);
    gpio_set_dir(spi_irq,GPIO_IN);
    gpio_init(spi_csn);
    gpio_set_dir(spi_csn,GPIO_OUT);
    gpio_put(spi_csn,1);
     hw_set_bits(&spi_get_hw(spi0)->imsc, SPI_SSPIMSC_RTIM_BITS | SPI_SSPIMSC_RORIM_BITS | SPI_SSPIMSC_RXIM_BITS);
    irq_set_exclusive_handler(SPI0_IRQ,SPI_RX_INTERRUPT);
    irq_set_enabled(SPI0_IRQ,true);     

}




void blink_task()
{
// code of blink task
while(1){
/*
.
.
.
*/
vtaskdelay(20);
}
}

void uart_task()
{
// this task will send data over uart

while(1){
/*
.
.
.
*/

vtaskdelay(10);

}
}



void main(){
  gpio_init(23);
  
  gpio_set_dir(23,GPIO_OUT);
 spi_init();
 xTaskCreate(blink_task,"blink_task",configMINIMAL_STACK_SIZE,NULL,1,NULL);
 xTaskCreate(uart_task,"uart_task",configMINIMAL_STACK_SIZE,NULL,1,NULL);
 vTaskStartScheduler();
  
 }

First, your tasks should be implementing infinite loops. Second, your spi init function should not be called before the scheduler is started, as has been mentioned before. Third, do not use printf in isrs.

@RAc I already have an infinite loop in my tasks, but I forgot to include it in the code that I shared with you.

Here is the udpated code with @RAc’s suggestions -

#include "pico/stdlib.h"
#include "FreeRTOS.h"
#include "task.h"


uint8_t data;
bool data_isReady = false;

#define CHECK_WO_FREERTOS 0


void TIMO_SPI_RX_INTERRUPT( void ){

	if(!data_isReady){
	  data = (uint8_t)spi_get_hw(Timo_spi_port)->dr;
	  data_isReady = true;
	}
	

}

void spi_init( void ){

    spi_init(spi0,2000*1000);
    gpio_set_function(spi_RX,GPIO_FUNC_SPI);
    gpio_set_function(spi_TX,GPIO_FUNC_SPI);
    gpio_set_function(spi_sck,GPIO_FUNC_SPI);
    gpio_set_dir(spi_irq,GPIO_IN);
    gpio_init(spi_csn);
    gpio_set_dir(spi_csn,GPIO_OUT);
    gpio_put(spi_csn,1);
     hw_set_bits(&spi_get_hw(spi0)->imsc, SPI_SSPIMSC_RTIM_BITS | SPI_SSPIMSC_RORIM_BITS | SPI_SSPIMSC_RXIM_BITS);
    irq_set_exclusive_handler(SPI0_IRQ,SPI_RX_INTERRUPT);
    irq_set_enabled(SPI0_IRQ,true);     

}




void blink_task( void * param )
{
  ( void ) param;

  gpio_init(23);
  gpio_set_dir(23,GPIO_OUT);
  spi_init();

  for( ;; )
  {
    // code of blink task
    vtaskdelay(20);
  }
}

void uart_task( void * param )
{
  ( void ) param;

  for( ;; )
  {
    // this task will send data over uart
    vtaskdelay(10);
  }
}

void main( void ){
  #if ( CHECK_WO_FREERTOS == 1 )
  {
    gpio_init(23);
    gpio_set_dir(23,GPIO_OUT);
    spi_init();
    for( ;; );
  }
  #else
  {
    xTaskCreate(blink_task,"blink_task",configMINIMAL_STACK_SIZE,NULL,1,NULL);
    xTaskCreate(uart_task,"uart_task",configMINIMAL_STACK_SIZE,NULL,1,NULL);
    vTaskStartScheduler();
  }
}

I did the following things too -

  1. Initialize data_isReady to false.
  2. Fix the task function signatures.

Try this code and see if it works. If it does not, change CHECK_WO_FREERTOS to 1 and see if that work. Let us know your findings.

so why don’t you share the code you are using? Also, did you ensure to register your isr with the ivt?

When CHECK_WO_FREERTOS is set to 1, the scheduler doesn’t start, and the interrupt is fired, everything works fine. However, when CHECK_WO_FREERTOS is set to 0, the entire system crashes when data is present in the SPI RX FIFO

What do you mean by crash? Do you have a callstack? Can you share complete code?