Need help with "button" interrupt task (xSemaphoreGiveFromISR)

re-play wrote on Wednesday, July 30, 2014:

Hello again,

I thought it’s better to create a new topic for this case. I’m a FreeRTOS newbie, currently studying interrupt generation in FreeRTOS. So far I’ve managed to write and successfully run my basic non-RTOS example of using the USER button on my STM32 ARM Cortex board as an external interrupt using the libraries to configure it (EXTI line and NVIC). Now I want to make a next step and move to FreeRTOS environment, obviously using a semaphore mechanism (xSemaphoreGiveFromISR).

My goal is to create a simple FreeRTOS project with 3 tasks - 2 of them for flashing a single LED (one for blue and another for a green one) at different frequency while the 3rd task should be waken by the ISR (that processes GPIO interrupts originating from the push button) and should do some simple job, like turning both LEDs off when the button is pressed. So far I’ve managed to create and run the first two tasks (flashing both LEDS at different speed, using no semaphores) so now I need to add the third - “user button” task and obviously the binary semaphore for signal the task from interrupt and also to synchronise the task with interrupt. I’m just not sure how to do it correctly. I’m also not sure about the priority of the button interrupt task - should it be higher than the priority of my first two tasks or not?

Now, I don’t expect you to write any code for me, but if someone can just point me in the right direction how to start to add this button interrupt task I would be very grateful. Could be just in few sentences, kind of step-by-step plan. Something like: first you need to add button initialization (GPIO, RCC), you also need to create a binary semaphore to synch. the task with interrupt, then create a button task, check your configMAX_LIBRARY_SYSCALL_INTERRUPT_PRIORITY settings etc. Hope I’m not asking too much.

re-play wrote on Friday, August 01, 2014:

UPDATE: Before using a button for generating GPIO interrupt I decided to try another, easier way - using software generated interrupts. For that purpose I modified “EXAMPLE 12” (Using a Binary Semaphore to Synchronize a Task with an Interrupt) code from R.Barry’s RTOS Practical Guide book. In short, it’s a simple example with 2 tasks (vPeriodTask and vHandlerTask) and 1 interrupt handler. vPeriodTask is supposed to generate (SW) interrupt every 1500 ms while vHandlerTask is used for synchronizing the task with SW interrupt using binary semaphore and has higher priority. Interrupt handler waits for interrupt, then “gives” the semaphore to unlock the high priority task (vHandlerTask) and switch to it.

However, my code doesn’t work as I expected. Judging by my testing with breakpoint, vInterruptHandler obviously never gets called so there is no switching to high priority task. What happens is the following:

1.) tasks and semaphore get created and schedular gets running
2.) vHandlerTask “takes” semaphore and prints “Handler task ->> Processing event” and turns on the LED. This is ok.
3.) Next, schedular switches to vPeriodicTask that waits for delay and then prints “About to generate SW interrupt…”. Then it generates SW interrupt and at that point the program freezes. Generated SW interrupt should wake up interrupt handler (that should then switch to vHandlerTask once again) but it does not.

My code (parts):

TASKS:

/*************************************************************************/
void vPeriodicTask(void * parameter){

for(;;){
{
vTaskDelay(1500);
printf( “About to generate SW interrupt…\r\n\r\n” );
EXTI_GenerateSWInterrupt(EXTI_Line0);
printf( “…Interrupt was generated…\r\n” );
}
}
}

/************************************************************************************/

static void vHandlerTask( void * pvParameters )
{
for( ;; )
{
// Use the semaphore to wait for an event.
if (xSemaphoreTake( SemaphoreTask, portMAX_DELAY ) == pdTRUE)
{
/* To get here the event must have occurred. Process the event. */
GPIO_WriteBit(GPIOC,GPIO_Pin_9,Bit_SET); // turn on LED
printf( “Handler task ->> Processing event.\r\n” );
}
}
}

/************************************************************************************/
void vInterruptHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET) // when interrupt …
{
portBASE_TYPE xHigherPriorityTaskWoken = pdFALSE;
xSemaphoreGiveFromISR(SemaphoreTask, &xHigherPriorityTaskWoken);
if (xHigherPriorityTaskWoken != pdFALSE)
{
portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
}
}
// Clear the EXTI line 0 pending bit
EXTI_ClearITPendingBit(EXTI_Line0);
}

/************************************************************************************/

MAIN:

int main(void) {

LEDInit();
EXTI0_Config();

vSemaphoreCreateBinary(SemaphoreTask);
if( SemaphoreTask != NULL )
{
/* The handler task is created with a high priority to ensure it runs immediately after the interrupt exits. /
xTaskCreate( vHandlerTask, “H”, configMINIMAL_STACK_SIZE
1, NULL, 3, NULL );
xTaskCreate( vPeriodicTask, “P”, configMINIMAL_STACK_SIZE1, NULL , 1, NULL );
vTaskStartScheduler();
}
for(;;);
return 0;
}
/
***********************************************************************************/

I’m not sure what I’m doing wrong. I’m not even sure it is FreeRTOS issue after all. Any suggestion on this?

rtel wrote on Friday, August 01, 2014:

If the interrupt is never being entered then I would agree - it does not sound like FreeRTOS is causing the problem. I would recommend getting the interrupt to work with a very simply loop in main(), before creating any tasks or starting the scheduler.

I presume the interrupt requires some hardware configuration in order for it to work configuration - and presumably that is done in the EXTI0_Config(); function. Have you looked in that function to see what it does? I imagine it will as a minimum enable the interrupt. Is that the same function you were using before? Next the EXTI_GenerateSWInterrupt() function - have a look in that to see what it is doing. I suspect it will just be writing to one of the processors Set Pending registers.

When you say the code freezes, what is it actually doing? If you stop on the debugger where are you? If you are in a default interrupt handler, rather than the EXTI0 handler, then maybe it is the wrong interrupt that is being raised in the ‘generate’ function, or that the handler for the interrupt is in the incorrect vector (did you install the handler in the vector table?).

Regards.

re-play wrote on Monday, August 04, 2014:

Thanks for your answer. Yes, EXTI0_Config() function simply initializes GPIO and RCC clock, configures EXT0 line and enables EXT0 interrupt (via NVIC configuration). I’ve been using it before in my non-RTOS examples.

To test if SW generated interrupt (EXTI_GenerateSWInterrupt() ) really works as it should I modified my interrupt handler part into this:


void EXTI0_1_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line0) != RESET) // when interrupt …
{
static uint32_t last=0;
if(last)
{
last=0;
GPIO_WriteBit(GPIOC,GPIO_Pin_9,Bit_SET);
GPIO_WriteBit(GPIOC,GPIO_Pin_8,Bit_RESET);
}
else
{
last=1;
GPIO_WriteBit(GPIOC,GPIO_Pin_9,Bit_RESET);
GPIO_WriteBit(GPIOC,GPIO_Pin_8,Bit_SET);
}
// Clear the EXTI line 0 pending bit
EXTI_ClearITPendingBit(EXTI_Line0);
}
}

Obviously this interrupt handler doesn’t work by RTOS mechanism anymore but rather as simple polling action. As the result, I got blinking LEDS (1 at time) every 1500 ms, just like it’s supposed to be according to my new interrupt handler. On my console window I could read the following events:

Handler task ->> Processing event.
About to generate SW interrupt…
…Interrupt was generated…
About to generate SW interrupt…
…Interrupt was generated…

Therefore I can conclude that SW generated interrupt works just fine. As for the two tasks, the schedular starts with Handler task (higher priority) which takes semaphore for the first time and then switches to the Period task which then keeps running and never switches back to Handler task (because it has no more free semaphores to take). The interrupt being raised in the ‘generate’ function is obviously correct.

This is just an update. Now I’ll be back to my original code trying to figure out what part of my “original” interrupt handler code is problematic and what exactly happens when the program runs.

davedoors wrote on Monday, August 04, 2014:

How is the console text output? If it is using semihosting then it can mess up the RTOS interrupts. Try the RTOS version again with semihosting turned off in the project options.

re-play wrote on Monday, August 04, 2014:

Hello again. Yes I’m using semihosting console but this doesn’t seem to be issue, at least not in my case. Actually I just find the problem - it was the interrupt handler function name! As soon as I changed the name from void vInterruptHandler(void) to void EXTI0_1_IRQHandler(void) it started to work as it should. Now the text sequence in console during program run is the following:

(Periodic task:) About to generate SW interrupt…
Handler task ->> Processing event!!
(Periodic task:) …Interrupt was generated…

Which is just how it should be. It was silly from me that I didn’t think of this issue before… What confused me was different handler interrupt name in that example I mentioned - but that was obviously because of of using different Cortex family I guess. Thanks for your answers anyway. Now I can finally move on my original idea - to use user button to generate interrupt.

davedoors wrote on Monday, August 04, 2014:

The name has to match the name in the interrupt vector table. If you call the function a different name then you will have to edit the vector table to so the names match. You will probably find the name in the vector table is EXTI0_1_IRQHandler, but the provided implementation of EXTI0_1_IRQHandler is declared weak, so it is replaced by your definition in the application code.

re-play wrote on Tuesday, August 05, 2014:

Yes, you are totally right. Anyway just to let you know, I’ve also managed to modify my code to use user button for generating interrupts (instead of SW generated interrupts from periodic task), which was my original goal. Thanks again for your help and useful info, everyone.