FreeRTOS offers special mechanisms to handle interrupts and ensure that the code executed within them works harmoniously with the rest of the created tasks. Semaphores, both binary and counting, are employed to synchronize an interrupt and a task. When data exchange is required, dedicated queues for interrupts can be utilized. It's crucial to note that priority levels play a significant role within the FreeRTOS framework.
A binary semaphore is a mechanism for both, mutual exclusion (Protect shared resources) and synchronization purposes. This semaphore only counts to 1.
Although mutual exclusion is possible using a binary semaphore is not recommended, a semaphore is given and taken by any task and is better to use as a signal mechanism to synchronize tasks, for example, is perfect to use with interrupts because a binary semaphore does not block task.
Code example:
#include "bsp.h"
void vPeriodicTask( void *pvParameters ); /* Declaration Task function */
void vHandlerTask( void *pvParameters ); /* Declaration Task function */
SemaphoreHandle_t xBinSemaphore; /* Binary semaphore handler */
int main( void )
{
HAL_Init( );
/*enable RTT and system view*/
SEGGER_SYSVIEW_Conf( );
SEGGER_SYSVIEW_Start( );
xBinSemaphore = xSemaphoreCreateBinary(); /* Create Binary semaphore */
HAL_NVIC_SetPriority( WWDG_IRQn, 2, 0 ); /* Set the priority of WWDG interrupt */
HAL_NVIC_EnableIRQ( WWDG_IRQn ); /* Enable the interruption */
xTaskCreate( vPeriodicTask, "task1", 240, NULL, 1, NULL ); /* Register on Kernel task with priority 1 */
xTaskCreate( vHandlerTask, "task2", 240, NULL, 2, NULL ); /* Register on Kernel task with priority 2 */
vTaskStartScheduler(); /* Kernel execution */
}
void vPeriodicTask( void *pvParameters )
{
/* Task generate an interruption each 500ms */
for(;;)
{
vTaskDelay( 500 / portTICK_PERIOD_MS ); /* vtask dleay of 500ms */
SEGGER_RTT_printf( 0, "PeriodicTask - Generatin interruption\n" );
HAL_NVIC_SetPendingIRQ( WWDG_IRQn ); /* Activation of interruption */
SEGGER_RTT_printf( 0, "PeriodicTask - Interruption generated\n" );
}
}
void vHandlerTask( void *pvParameters )
{
/* Task to synchronize the interruption using a semaphore */
for(;;)
{
/* The Task blocks until the interruption release the semaphore */
xSemaphoreTake( xBinSemaphore, portMAX_DELAY );
SEGGER_RTT_printf( 0, "HandlerTask - Processing event\n" );
}
}
void WWDG_IRQHandler( void ) /* Callback of the interrupt */
{
/* Interruption Vector */
BaseType_t xTaskWoken = pdFALSE;
xSemaphoreGiveFromISR( xBinSemaphore, &xTaskWoken ); /* Release semaphore */
portEND_SWITCHING_ISR(xTaskWoken); /* Change context if xTaskWoken=pdTRUE */
}
BaseType_t xTaskWoken = pdFALSE;
Declaration of this variable helps to know if xSemaphoreGiveFromISR
was successful. portEND_SWITCHING_ISR(xTaskWoken);
Function to change the context, if xTaskWoken
is pdTRUE
function will change the context immediately, Otherwise the change will not be imminent.In this program, two tasks are created and an interrupt vector is managed. The vHandlerTask
task executes first and attempts to take the semaphore within the for
loop, but it has not been released, causing the task to be suspended indefinitely. The vPeriodicTask
task executes by triggering an interrupt, which is immediately handled by the vInterruptHandler
function. This interrupt releases the semaphore and triggers a context switch (line 53), allowing the higher-priority vHandlerTask
to preempt the vPeriodicTask
(which was in the running state), eventually taking the semaphore, printing to the screen, and repeating the cycle.
vPeriodicTask
?traceISR_ENTER()
and traceISR_EXIT()
to visualize the interrupt.SytemView Output
Terminal Shows the order in which the tasks are running although task2 has the highest priority. Terminal shows that Task2 is executed only when the interruption is generated.
Timeline:
Task 2 is blocked by the binary semaphore, observe that task1 is executed to generate an interruption where the semaphore will be given, when the callback of the interruption executes will change the context, and task2 now can take CPU time, to after take binary semaphore blocking itself again, finally task1 resume the execution to print that the interrupt was generated.