You are here

Использование уведомлений в качестве счётных семафоров

Source: https://www.freertos.org/RTOS_Task_Notification_As_Counting_Semaphore.html

Перевод может содержать ошибки. Читайте первоисточник.


[Task Notifications - Уведомления]

RTOS Task Notifications Used As Light Weight Counting Semaphores - Использование уведомлений в качестве счётных семафоров

Разблокировка задачи с помощью уведомления на 45% быстрее и требует меньше ОЗУ, чем разблокировка с помощью счётного семафора. На этой странице продемонстрировано, как это может быть реализовано в приложении.

Счётный семафор - это семафор, который имеет счётчик значений. Значения находятся в диапазоне от нуля до максимального значения, которое указывается в момент создания семафора. Задача может "взять" семафор только если он доступен, а семафор доступен только если значение его счётчика больше нуля.

Когда уведомление используется вместо счётного семафора, поле значения уведомления в задаче-получателе используется вместо счётчика счётного семафора, а функция API ulTaskNotifyTake() используется вместо функции API xSemaphoreTake(). Параметр xClearOnExit при вызове функции ulTaskNotifyTake() должен быть установлен в значение pdFALSE для того, чтобы значение уведомления каждый раз "при забирании семафора" только уменьшалось на единицу (а не обнулялось). Благодаря этому эмулируется именно счётный семафор.

Аналогично функции API xTaskNotifyGive() и vTaskNotifyGiveFromISR() используются вместо функций xSemaphoreGive() и xSemaphoreGiveFromISR().

Примеры

Первый пример использует уведомление принимающей задачи в качестве счётного семафора. Второй пример предоставляет более прагматичную и эффективную реализацию.

Пример 1:

/*----------------------------------------------------------------------------*/
/* Обработчик прерывания не обрабатывает прерывание напрямую, но вместо этого
сразу же передаёт обработку задаче RTOS с высоким приоритетом. Обработчик
прерывания использует уведомление: 1) для разблокировки задачи RTOS, 2) для
увеличения значения в поле уведомления задачи RTOS. */
void vANInterruptHandler( void )
{
BaseType_t xHigherPriorityTaskWoken;
 
    /* Очищаем флаг прерывания. */
    prvClearInterruptSource();
 
    /* Переменная xHigherPriorityTaskWoken должна быть инициализирована
    значением pdFALSE. Если вызов функции vTaskNotifyGiveFromISR() разблокирует
    задачу-обработчик, и приоритет задачи-обработчика выше, чем приоритет
    текущей запущенной задачи, тогда xHigherPriorityTaskWoken будет
    автоматически установлен в значение pdTRUE. */
    xHigherPriorityTaskWoken = pdFALSE;
 
    /* Разблокируем задачу-обработчик, чтобы она могла выполнить все необходимые
    действия по обработке прерывания. xHandlingTask это хэндлер задачи, который
    получаем в момент создания задачи. vTaskNotifyGiveFromISR() также
    увеличивает на единицу значение в поле уведомления задачи. */
    vTaskNotifyGiveFromISR( xHandlingTask, &xHigherPriorityTaskWoken );
 
    /* Принудительно переключаем контекст, если значение xHigherPriorityTaskWoken
    изменилось на pdTRUE. Макрос, используемый для этого, зависит от порта
    для вашего процессора, и может называться portEND_SWITCHING_ISR. */
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
 
/*----------------------------------------------------------------------------*/
/* Задача, которая блокируется на время ожидания уведомления о необходимости
обработки периферии. */
void vHandlingTask( void *pvParameters )
{
BaseType_t xEvent;
const TickType_t xBlockTime = pdMS_TO_TICS( 500 );
uint32_t ulNotifiedValue;
 
    for( ;; )
    {
        /* Блокируем задачу для ожидания уведомления. Здесь уведомление
        используется в качестве счётного семафора. Значение в поле уведомления
        увеличивается на единицу каждый раз, когда обработчик прерывания
        вызывает функцию vTaskNotifyGiveFromISR(), и уменьшается на единицу
        каждый раз, когда задача вызывает функцию ulTaskNotifyTake() - так
        получаем эффект сохранения количества поступивших запросов обработки
        прерывания. Первый параметр при вызове должен быть pdFALSE, чтобы
        значение уведомления только уменьшалось на единицу (а не обнулялось),
        и за один раз обрабатывался один запрос от прерывания. Дополнительно
        посмотрите пример 2 (ниже), где реализован более прагматичный подход.
        */
        ulNotifiedValue = ulTaskNotifyTake( pdFALSE,
                                            xBlockTime );
 
        if( ulNotifiedValue > 0 )
        {
            /* Выполняем некие действия, которые требуются для обработки
            прерывания. */
            xEvent = xQueryPeripheral();
 
            if( xEvent != NO_MORE_EVENTS )
            {
                vProcessPeripheralEvent( xEvent );
            }
        }
        else
        {
            /* Не получили уведомления за отведённое время. */
            vCheckForErrorConditions();
        }
    }
}

Пример 2:

Этот пример показывает более прагматичный и эффективный подход к реализации задачи-обработчика. Здесь значение, возвращаемое функцией ulTaskNotifyTake() сохраняется и используется для определения, сколько запросов на обслуживание было сгенерировано в обработчике прерывания. Это позволяет обнулять значение в поле уведомления каждый раз, когда вызывается ulTaskNotifyTake(). Обработчик прерывания для этого примера аналогичен примеру 1 (выше).

/*----------------------------------------------------------------------------*/
/* Задача переходит в заблокированное состояние для ожидания уведомления о
том, что периферия требует обслуживания (servicing) */
void vHandlingTask( void *pvParameters )
{
BaseType_t xEvent;
const TickType_t xBlockTime = pdMS_TO_TICS( 500 );
uint32_t ulNotifiedValue;
 
    for( ;; )
    {
        /* Как и в примере выше, переходим в заблокированное состояние для
        ожидания уведомления от обработчика прерывания. Но первый параметр в
        вызове функции ulTaskNotifyTake() установлен в значение pdTRUE, что
        вызовет сброс в 0 значения в поле уведомления. Это означает, что запросы
        на обслуживание от обработчика прерывания должны быть выполнены до
        следующего вызова функции ulTaskNotifyTake(). */
        ulNotifiedValue = ulTaskNotifyTake( pdTRUE, xBlockTime );
 
        if( ulNotifiedValue == 0 )
        {
            /* Не получили уведомления за отведённое время. */
            vCheckForErrorConditions();
        }
        else
        {
            /* ulNotifiedValue содержит количество запросов на обслуживание от
            обработчика прерывания. Здесь необходимо поочерёдно обработать все
            имеющиеся запросы. */
            while( ulNotifiedValue > 0 )
            {
                xEvent = xQueryPeripheral();
 
                if( xEvent != NO_MORE_EVENTS )
                {
                    vProcessPeripheralEvent( xEvent );
                    ulNotifiedValue--;
                }
                else
                {
                    break;
                }
            }
        }
    }
}

Похожие страницы

Hobby's category: