Программирование ARM FreeRTOS: функции группы бит событий Thu, November 21 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


FreeRTOS: функции группы бит событий Печать
Добавил(а) microsin   

В этой статье рассматриваются такие примитивы синхронизации FreeRTOS, как биты событий, или флаги событий (Event Bits, другое название Event Flags), а также группы событий (Event Groups).

Event Bits (Event Flags). Биты событий используются для индикации, произошло событие или нет. Биты событий часто называют флагами событий (event flags). Например, приложение может:

• Определить бит (флаг), который означает "Было принято сообщение, и оно готово к обработке", когда он установлен в 1, и "Нет сообщений, ожидающих обработки", когда он установлен в 0.
• Определить бит (флаг), который означает "Приложение поставило в очередь сообщение, готовое для отправки через сеть", когда он установлен в 1, и "В очереди для отправки по сети нет сообщений", когда он установлен в 0.
• Определить бит (флаг), который означает "Пора отправить по сети сообщение метки времени (heartbeat message)", когда он установлен в 1, и "Пока не надо отправлять очередное сообщение метки времени", когда он установлен в 0.

Event Groups. Группа событий это набор бит событий. К отдельным битам событий в группе событий обращаются по номеру бита. Если рассмотреть показанный выше пример:

• Бит события, который означает "Было принято сообщение, и оно готово к обработке", может в группе событий быть битом с номером 0.
• Бит события, который означает "Приложение поставило в очередь сообщение, готовое для отправки через сеть", может в группе событий быть битом с номером 1.
• Бит события, который означает "Пора отправить по сети сообщение метки времени (heartbeat message)", может в группе событий быть битом с номером 2.

[Типы данных для группы событий и бит событий]

К группам событий обращаются через переменные типа EventGroupHandle_t.

Количество бит (или флагов), сохраненных в группе событий, может быть 8, если configUSE_16_BIT_TICKS установлена в 1, или 24, если configUSE_16_BIT_TICKS установлена в 0. Зависимость от опции конфигурации configUSE_16_BIT_TICKS является результатом размера типа данных, используемого для локального хранилища потока во внутренней реализации задач FreeRTOS.

Все биты событий в группе событий хранятся в одной unsigned-переменной типа EventBits_t. Бит события 0 находится в позиции 0 этой переменной, бит события 1 в позиции 1, и так далее.

На рисунке ниже представлено 24-разрядное хранилище для группы событий (когда configUSE_16_BIT_TICKS == 0), которое использует 3 бита для хранения трех бит из примера выше. На этой картинке установлен только бит 2 (пора передавать по сети метку времени).

FreeRTOS 24 bit event group

Рис. 1. Группа событий емкостью в 24 бита событий, в котором используется только 3 бита.

[API-функции FreeRTOS для групп событий]

API-функции групп событий позволяют задаче, помимо всего прочего, установить один или большее количество бит событий в группе событий, очистить один или большее количество бит событий в группе событий, и ждать (войти в состояние Blocked, чтобы задачая не потребляла процессорное время) установки одного или большего количества бит, когда они будут установлены (из другой задачи или прерывания) в группе событий.

Группы событий также могут использоваться для синхронизации задач, создавая то, что часто называют "задачей свидания" (task 'rendezvous'). Точка синхронизации задач в приложении это то место в коде приложения, где одна из задач ждет в состоянии Blocked (не потребляя время CPU), пока все другие задачи, участвующие в синхронизации, достигнут своего момента синхронизации.

Проблемы для внедрения групп событий. При реализации групп событий RTOS должны быть преодолены два сложных момента:

1. Избежать ситуации возникновения гонки (race condition) в приложении пользователя. Реализация группы событий создаст гонку, если:

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

Реализация групп событий FreeRTOS устраняет потенциальные условия возникновения гонки, включая встроенный интеллектуальный алгоритм для гарантированно атомарной установки, проверки и очистки бит. Это стало возможным благодаря локальному хранилищу потока и корректному использованию возвращаемых из API-функций значений.

2. Следует избегать потери детерминизма выполнения кода.

Концепция группы событий подразумевает недетерминированное поведение, поскольку неизвестно, сколько задач заблокировано на группе событий, и как следствие неизвестно, сколько условий необходимо проверить, или разблокировать задачи, когда установлен бит события.

Стандарты качества FreeRTOS не допускают недетерминированные действия, когда запрещены прерывания, или из тела обработчиков прерывания (ISR). Для гарантии, что эти жесткие стандарты качества не будут нарушены при установки бита события:

- Используется механизм блокировки планировщика RTOS для гарантии, что прерывания останутся разрешенными, когда бит события установлен из задачи RTOS.
- Используется централизованный механизм отложенного прерывания (deferred interrupt), чтобы отложить действие установки бита для задачи, кода делается попытка установки бита события из ISR.

Пример кода. Примеры предоставляются в документации по API, а также подробный пример есть в наборе стандартных демонстрационных задач EventGroupsDemo.c (этот файл находится в директории FreeRTOS/Demo/Common/Minimal основного пакета загрузки FreeRTOS).

[API-функции групп событий]

Декларация описанных ниже функций находится в заголовочном файле event_groups.h. Перед использованием этих функций предварительно должна быть создана группа событий с помощью вызова xEventGroupCreate().

xEventGroupSetBits. Устанавливает биты (флаги) в группе событий RTOS. Эта функция не может вызываться из прерываний, для вызова из прерываний есть специальная версия xEventGroupSetBitsFromISR (созданная группа событий передается через параметр EventGroupHandle_t).

Установка бит в группе событий автоматически разблокирует задачи, которые заблокированы в ожидании для этих бит (см. xEventGroupWaitBits).

EventBits_t xEventGroupSetBits (EventGroupHandle_t xEventGroup,
                                const EventBits_t uxBitsToSet);

Параметры:

xEventGroup Группа событий, в которой должны быть установлены биты.

uxBitsToSet Битовая маска, показывающая, какие биты в группе должны быть установлены. Например, uxBitsToSet устанавливается в 0x08, чтобы установить только бит 3. Передача значения uxBitsToSet 0x09 установит биты 3 и 0.

Возвращаемое значение: значение бит в группе событий на момент возврата из xEventGroupSetBits().

Существуют 2 причины, почему в возвращаемом значении могут быть очищены биты, указанные в параметре uxBitsToSet:

1. Если установка бита приведет к тому, что ожидающая его задача выйдет из заблокированного состояния, тогда возможно этот бит будет очищен автоматически (см. параметр xClearBitOnExit функции xEventGroupWaitBits()).
2. В любой разблокированной (или иным образом находящейся в состоянии Ready) с более высоким приоритетом задачи (чем у той, где был вызов xEventGroupSetBits()) было изменение значений бит этой группы событий.

Пример использования:

#define BIT_0 (1 << 0)
#define BIT_4 (1 << 4)
 
void aFunction (EventGroupHandle_t xEventGroup)
{
   EventBits_t uxBits;
 
   /* Установка бит 0 и 4 в xEventGroup. */
   uxBits = xEventGroupSetBits(xEventGroup,
                               BIT_0 | BIT_4);
   /* Группа событий обновилась, в ней установились в 1
      биты 0 и 4. */
 
   if ( (uxBits & (BIT_0 | BIT_4)) == (BIT_0 | BIT_4) )
   {
      /* После выхода из функции xEventGroupSetBits оба бита 0 и 4
         остались установленными. */
   }
   else if ( (uxBits & BIT_0) != 0 )
   {
      /* Бит 0 остался установленным при возврате из функции, но бит 4
         сбросился. Это могло произойти из-за того, что произошла
         автоматическая очистка бита 4, поскольку задача, которая
         ждала установки бита 4, вышла из состояния Blocked. */
   }
   else if( ( uxBits & BIT_4 ) != 0 )
   {
      /* Бит 4 остался установленным при возврате из функции, но бит 0
         сбросился. Это могло произойти из-за того, что произошла
         автоматическая очистка бита 0, поскольку задача, которая
         ждала установки бита 0, вышла из состояния Blocked. */
   }
   else
   {
      /* Ни один из бит 0 и 4 не остался установленным. Это может быть,
         когда были задачи в ожидании на обоих этих битах, и они были
         очищены из-за того, что задачи вышли из состояния Blocked. */
   }
}

xEventGroupClearBits. Очистит биты (флаги) в группе событий RTOS. Эта функция не может быть вызвана из ISR, для этого предназначена функция xEventGroupClearBitsFromISR().

EventBits_t xEventGroupClearBits (EventGroupHandle_t xEventGroup,
                                  const EventBits_t uxBitsToClear);

Параметры:

xEventGroup Группа событий, в которой должны быть очищены биты.

uxBitsToSet Битовая маска, показывающая, какие биты в группе должны быть очищены. Например, uxBitsToSet устанавливается в 0x08, чтобы очистить только бит 3. Передача значения uxBitsToSet 0x09 очистит биты 3 и 0.

Возвращаемое значение: значение бит группы событий перед очисткой.

Пример использования:

#define BIT_0 (1 << 0)
#define BIT_4 (1 << 4)
 
void aFunction( EventGroupHandle_t xEventGroup )
{
   EventBits_t uxBits;
 
   /* Очистка бита 0 и 4 в xEventGroup. */
   uxBits = xEventGroupClearBits (xEventGroup,
                                  BIT_0 | BIT_4);
 
   if ( (uxBits & (BIT_0 | BIT_4)) == (BIT_0 | BIT_4) )
   {
      /* Оба бита 0 и 4 были установлены перед вызовом xEventGroupClearBits().
         Оба они будут очищены (не установлены). */
   }
   else if ( (uxBits & BIT_0) != 0 )
   {
      /* Бит 0 был установлен перед вызовом xEventGroupClearBits(). Теперь
         он будет очищенным. */
   }
   else if ( (uxBits & BIT_4) != 0 )
   {
      /* Бит 4 был установлен перед вызовом xEventGroupClearBits(). Теперь
         он будет очищенным. */
   }
   else
   {
      /* Ни один из бит 0 и 1 не был предварительно установлен. */
   }
}

xEventGroupGetBits. Возвратит текущее значение бит событий (флагов событий) в группе событий RTOS. Эта функция не может быть вызвана из ISR, для этой цели есть специальная версия xEventGroupGetBitsFromISR().

EventBits_t xEventGroupGetBits (EventGroupHandle_t xEventGroup);

Параметры:

xEventGroup Опрашиваемая группа событий.

Возвращаемое значение: биты событий в момент вызова xEventGroupGetBits().

xEventGroupWaitBits. Прочитает биты в группе событий RTOS, с опциональным входом в состояние Blocked (с таймаутом), чтобы ждать установки указанного бита или группы бит. Эта функция не может быть вызвана из прерывания.

EventBits_t xEventGroupWaitBits (const EventGroupHandle_t xEventGroup,
                                 const EventBits_t uxBitsToWaitFor,
                                 const BaseType_t xClearOnExit,
                                 const BaseType_t xWaitForAllBits,
                                 TickType_t xTicksToWait);

Параметры:

xEventGroup Группа событий, в которой проверяются биты.

uxBitsToWaitFor Битовая маска, идентифицирующая биты внутри группы бит событий. Например, чтобы ждать установки бита 0 и/или бита 2, передайте 0x05 в параметре uxBitsToWaitFor. Чтобы ждать установки любого из бит 0, 1 и 2 (установки по отдельности, или одновременно), установите uxBitsToWaitFor в 0x07. Параметр uxBitsToWaitFor не должен быть установлен в 0.

xClearOnExit Если xClearOnExit установлен в pdTRUE, то любой установленный бит из обозначенных маской uxBitsToWaitFor будет автоматически очищен перед возвратом xEventGroupWaitBits(), если этот возврат был по причине установки любого из обозначенных бит, но не по причине истечения таймаута.

Если xClearOnExit установлен в pdFALSE, то биты в группе бит событий при возврате из xEventGroupWaitBits() остаются не измененными.

xWaitForAllBits xWaitForAllBits используется для создания либо проверки логической операции И (где все биты должны быть установлены), либо логической операции ИЛИ (когда установлен один любой бит или установлено несколько бит) следующим образом:

Если xWaitForAllBits установлен в pdTRUE, то из xEventGroupWaitBits() произойдет возврат, когда будут установлены все биты, обозначенные маской uxBitsToWait, или когда истечет указанный таймаут блокировки.

Если xWaitForAllBits установлен в pdFALSE, то xEventGroupWaitBits() выполнит возврат, когда установится любой из бит, обозначенный маской uxBitsToWaitFor, или когда истечет указанный таймаут блокировки.

xTicksToWait Максимальное количество времени (указанное в 'тиках') ожидания установки для одного или всех (в зависимости от переданного значения xWaitForAllBits) бит, обозначенных маской uxBitsToWaitFor.

Возвращаемое значение: значение бит из группы бит событий в момент, когда желаемый бит (или несколько бит) были установлены, либо когда истек таймаут блокировки. Текущее значение бит событий в группе бит событий будет отличаться от возвращенного значения, если их значение будет изменено в более приоритетной задаче или в ISR между моментом, когда вызвавшая xEventGroupWaitBits() задача вышла из состояния Blocked, и моментом, когда произошел возврат из функции xEventGroupWaitBits().

Проверьте возвращаемое значение, чтобы определить, какие биты были установлены. Если из xEventGroupWaitBits() произошел возврат из-за того, что истек таймаут, то не все желаемые биты были установлены. Если из xEventGroupWaitBits() произошел возврат из-за того, что желаемые биты были установлены, то возвращенное значение будет значением бит событий перед тем, как они были автоматически очищены по причине установленного в pdTRUE параметра xClearOnExit.

Пример использования:

#define BIT_0 (1 << 0)
#define BIT_4 (1 << 4)
 
void aFunction (EventGroupHandle_t xEventGroup)
{
   EventBits_t uxBits;
 
   const TickType_t xTicksToWait = 100 / portTICK_PERIOD_MS;
 
   /* Ждать максимум 100 мс установки в группе событий любого
      из бит 0 или 4, с очисткой этих бит перед выходом
      из функции xEventGroupWaitBits. */
   uxBits = xEventGroupWaitBits (
            xEventGroup,   /* Тестируемая группа бит событий. */
            BIT_0 | BIT_4, /* Ждем установки бит 0 и/или 4. */
            pdTRUE,        /* Биты должны быть перед возвратом очищены. */
            pdFALSE,       /* Не ждать установки сразу обоих бит, делать
                              возврат если установлен любой из них. */
            xTicksToWait); /* Максимальное время ожидания 100 мс. */
 
   if ( (uxBits & (BIT_0 | BIT_4)) == (BIT_0 | BIT_4) )
   {
      /* Возврат xEventGroupWaitBits() из-за того, что были установлены
         оба бита одновременно. */
   }
   else if ( (uxBits & BIT_0) != 0 )
   {
      /* Возврат xEventGroupWaitBits() из-за установки бита 0. */
   }
   else if ( (uxBits & BIT_4) != 0 )
   {
      /* Возврат xEventGroupWaitBits() из-за установки бита 4. */
   }
   else
   {
      /* Возврат xEventGroupWaitBits(), потому что прошло время
         ожидания xTicksToWait тиков, и ни один из бит 0 или 4
         за это время не был установлен. */
   }
}

[Ссылки]

1. xEventGroupSetBits xEventGroupClearBits, xEventGroupGetBits xEventGroupWaitBits site:freertos.org.
2. Event Bits (or flags) and Event Groups site:freertos.org.
3. FreeRTOS: оповещения задач.

 

Добавить комментарий


Защитный код
Обновить

Top of Page