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.

This Semaphore is a shared counter that can be incremented and decremented, Counter can be set when the semaphore is created and is used to count events that happened or resource management. For example, in resource management, different processes can access to a shared resource until the count reaches zero therefore other tasks can not access to resources.

Code Example:

#include "bsp.h"

void vPeriodicTask( void *pvParameters );   /* Declaration Task function */
void vHandlerTask( void *pvParameters );    /* Declaration Task function */

SemaphoreHandle_t xCountSemaphore;          /* Counter semaphore handler */

int main( void )
{
    HAL_Init( );
    /*enable RTT and system view*/
    SEGGER_SYSVIEW_Conf( );
    SEGGER_SYSVIEW_Start( );
    
    xCountSemaphore = xSemaphoreCreateCounting( 10, 0 );        /* Creation of counter semaphore with a max count of 10 */
    
    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, 3, NULL );  /* Register on Kernel task with priority 3 */

    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( xCountSemaphore, portMAX_DELAY );
        SEGGER_RTT_printf( 0, "HandlerTask - Processing event\n" );            
    }
}

void WWDG_IRQHandler( void ) 
{
    /* Interruption Vector */
    BaseType_t xTaskWoken = pdFALSE;

    xSemaphoreGiveFromISR(xCountSemaphore, &xTaskWoken);    /* Give the semaphore 1 */
    xSemaphoreGiveFromISR(xCountSemaphore, &xTaskWoken);    /* Give the semaphore 2 */
    xSemaphoreGiveFromISR(xCountSemaphore, &xTaskWoken);    /* Give the semaphore 3 */
    portEND_SWITCHING_ISR(xTaskWoken);                      /* Change context if xTaskWoken=pdTRUE */
}

On file FreeRTOSConfig.h identify the next line and change its value from zero to one.

#define configUSE_COUNTING_SEMAPHORES     1

This second program is identical to the previous one, except that this time counting semaphores is used. The interrupt now gives up the semaphore three times (simulating three consecutive events). This causes the vHandlerTask task to have up to three available semaphores to take, and this is reflected when three identical lines are printed on the screen by the task. Remember that the context switch occurs when the interrupt function finishes, and that's when vHandlerTask is executed and discovers that it has three pending events to handle.

💡
Before continuing: Review the xSemaphoreCreateCounting function, and remember that you can assign taken semaphores to it. Change the value from zero (second parameter) to four and observe the output on the screen (right at the beginning).

SystemView Output

Observe the message on the terminal, the application works similarly to the previous example. Now the interruption unlocks several events to execute, these events can be observed with the messages on the terminal.

Document

Timeline:

The timeline shows the execution of task1 that executes the interruption and the interruption unlocks a series of events. Task2 in charge of executing all the events just print a message for each event unlocked, when task2 finishes the execution, task1 takes the control again to print a message to indicate that all the events are generated.

Document