Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


RTOS Task Notifications
Used As Light Weight Counting Semaphores

Related pages:


Unblocking an RTOS task with a direct notification is 45% faster and uses less RAM than unblocking a task with a semaphore. This page demonstrates how this is done.

A counting semaphore is a semaphore that can have a count value of zero up to a maximum value set when the semaphore is created. A task can only 'take' the semaphore if it is available, and the semaphore is only available if its count is greater than zero.

When a task notification is used in place of a counting semaphore the receiving task's notification value is used in place of the counting semaphore's count value, and the ulTaskNotifyTake() API function is used in place of the semaphore's xSemaphoreTake() API function. The ulTaskNotifyTake() function's xClearOnExit parameter is set to pdFALSE so the count value is only decremented (rather than cleared) each time the notification is taken - emulating a counting semaphore.

Likewise the xTaskNotifyGive() or vTaskNotifyGiveFromISR() functions are used in place of the semaphore's xSemaphoreGive() and xSemaphoreGiveFromISR() functions.

The first example below uses the receiving task's notification value as a counting semaphore. The second example provides a more pragmatic and efficient implementation.


Example 1:

/* An interrupt handler that does not process interrupts directly,
but instead defers processing to a high priority RTOS task.  The ISR
uses RTOS task notifications to both unblock the RTOS task and increment
the RTOS task's notification value. */
void vANInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;

    /* Clear the interrupt. */
    prvClearInterruptSource();

    /* xHigherPriorityTaskWoken must be initialised to pdFALSE.
    If calling vTaskNotifyGiveFromISR() unblocks the handling
    task, and the priority of the handling task is higher than
    the priority of the currently running task, then
    xHigherPriorityTaskWoken will be automatically set to pdTRUE. */
    xHigherPriorityTaskWoken = pdFALSE;

    /* Unblock the handling task so the task can perform any processing
    necessitated by the interrupt.  xHandlingTask is the task's handle, which was
    obtained when the task was created.  vTaskNotifyGiveFromISR() also increments
    the receiving task's notification value. */
    vTaskNotifyGiveFromISR( xHandlingTask, &xHigherPriorityTaskWoken );

    /* Force a context switch if xHigherPriorityTaskWoken is now set to pdTRUE.
    The macro used to do this is dependent on the port and may be called
    portEND_SWITCHING_ISR. */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
/*-----------------------------------------------------------*/

/* A task that blocks waiting to be notified that the peripheral needs
servicing. */
void vHandlingTask( void *pvParameters )
{
BaseType_t xEvent;
const TickType_t xBlockTime = pdMS_TO_TICS( 500 );
uint32_t ulNotifiedValue;

    for( ;; )
    {
        /* Block to wait for a notification.  Here the RTOS task notification
        is being used as a counting semaphore.  The task's notification value
        is incremented each time the ISR calls vTaskNotifyGiveFromISR(), and
        decremented each time the RTOS task calls ulTaskNotifyTake() - so in
        effect holds a count of the number of outstanding interrupts.  The first
        parameter is set to pdFALSE, so the notification value is only decremented
        and not cleared to zero, and one deferred interrupt event is processed
        at a time.  See example 2 below for a more pragmatic approach. */
        ulNotifiedValue = ulTaskNotifyTake( pdFALSE,
                                            xBlockTime );

        if( ulNotifiedValue > 0 )
        {
            /* Perform any processing necessitated by the interrupt. */
            xEvent = xQueryPeripheral();

            if( xEvent != NO_MORE_EVENTS )
            {
                vProcessPeripheralEvent( xEvent );
            }
        }
        else
        {
            /* Did not receive a notification within the expected time. */
            vCheckForErrorConditions();
        }
    }
}


Example 2:

This example shows a more pragmatic and efficient implementation for the RTOS task. In this implementation the value returned from ulTaskNotifyTake() is used to know how many outstanding ISR events must be processed, allowing the RTOS task's notification count to be cleared back to zero each time ulTaskNotifyTake() is called. The interrupt service routine (ISR) is assumed to be as demonstrated in example 1 above.

/* A task that blocks waiting to be notified that the peripheral needs
servicing. */
void vHandlingTask( void *pvParameters )
{
BaseType_t xEvent;
const TickType_t xBlockTime = pdMS_TO_TICS( 500 );
uint32_t ulNotifiedValue;

    for( ;; )
    {
        /* As before, block to wait for a notification form the ISR.  This
        time however the first parameter is set to pdTRUE, clearing the task's
        notification value to 0, meaning each outstanding outstanding deferred
        interrupt event must be processed before ulTaskNotifyTake() is called
        again. */
        ulNotifiedValue = ulTaskNotifyTake( pdTRUE,
                                            xBlockTime );

        if( ulNotifiedValue == 0 )
        {
            /* Did not receive a notification within the expected time. */
            vCheckForErrorConditions();
        }
        else
        {
            /* ulNotifiedValue holds a count of the number of outstanding
            interrupts.  Process each in turn. */
            while( ulNotifiedValue > 0 )
            {
                xEvent = xQueryPeripheral();

                if( xEvent != NO_MORE_EVENTS )
                {
                    vProcessPeripheralEvent( xEvent );
                    ulNotifiedValue--;
                }
                else
                {
                    break;
                }
            }
        }
    }
}





[ Back to the top ]    [ About FreeRTOS ]    [ Privacy ]    [ Sitemap ]    [ ]


Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

NXP tweet showing LPC5500 (ARMv8-M Cortex-M33) running FreeRTOS.

Meet Richard Barry and learn about running FreeRTOS on RISC-V at FOSDEM 2019

Version 10.1.1 of the FreeRTOS kernel is available for immediate download. MIT licensed.

View a recording of the "OTA Update Security and Reliability" webinar, presented by TI and AWS.


Careers

FreeRTOS and other embedded software careers at AWS.



FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Espressif ESP32

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Renesas

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS

Xilinx Microblaze and Zynq partner