Quality RTOS & Embedded Software

KERNEL

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() (or ulTaskNotifyTakeIndexed()) 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 xTaskNotifyGiveIndexed()) or vTaskNotifyGiveFromISR() (or vTaskNotifyGiveIndexedFromISR()) 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.

/* The index within the target task's array of task notifications
to use. */
const UBaseType_t xArrayIndex = 0;

/* 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 = ulTaskNotifyTakeIndexed( xArrayIndex,
                                                   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;
                }
            }
        }
    }
}




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