Embedded HandbookПриложения
Все разделы › IV. FreeRTOS
STM32F103, STM32F407 · уровень справочник · 15 мин чтения
Открыть в интерактивном справочнике

34. Макросы и функции FreeRTOS: справочник

Всё, что встретилось в разделах 23–33, — в одном месте, по объектам. Каждое имя из таблиц этого раздела вызвано в проверочном файле и собрано с ядром 10.5.1: опечатка здесь была бы ошибкой компиляции. Для каждой функции указано, откуда её можно вызывать: З — из задачи, П — из прерывания, Н — до запуска планировщика.

Три контекста вызова API FreeRTOS: до vTaskStartScheduler создают задачи, очереди и таймеры, из задачи доступен весь API с ожиданием от нуля до portMAX_DELAY, из прерывания — только двойники FromISR без ожидания с признаком woken и portYIELD FROM ISR на выходе; прерывание срочнее порога — с числом приоритета меньше 5 — звать ядро не может вовсе, а мьютекс из прерывания недоступен ни в каком виде

Обозначения

З из задачи; может ждать, если задано время
П из обработчика прерывания; двойник с FromISR, ждать не умеет
Н до vTaskStartScheduler — при инициализации
ticks время ожидания в тиках: 0, pdMS_TO_TICKS(n) или portMAX_DELAY
*woken выходной признак для portYIELD_FROM_ISR

Как это называется в документации

Оригинал Перевод
task handle описатель задачи
queue очередь
binary / counting semaphore двоичный / считающий семафор
mutual exclusion / mutex взаимное исключение / мьютекс
recursive mutex рекурсивный мьютекс
software timer программный таймер
timer daemon / service task задача-демон, обслуживающая таймеры
event group группа событий
stream buffer потоковый буфер
message buffer буфер сообщений
task notification уведомление задачи
block time / ticks to wait время ожидания
interrupt safe API функции, допустимые в обработчике
yield from ISR переключиться на выходе из обработчика

Объекты ядра FreeRTOS вокруг задачи: очередь, семафор и мьютекс, программный таймер, группа событий, потоковый буфер и буфер сообщений, уведомление задачи; из прерывания — двойники FromISR


34.1 Задачи

Создание и удаление

Имя Что делает Откуда Раздел
xTaskCreate(fn, name, depth, arg, prio, *h) создать из кучи; pdPASS или отказ З Н 24, 26
xTaskCreateStatic(fn, name, depth, arg, prio, stack, *tcb) создать из своей памяти; описатель З Н 24, 26
vTaskDelete(h) удалить; NULL — себя З 26
vTaskStartScheduler() запустить; не возвращается Н 24

depth — в словах, не в байтах. Приоритет не меньше configMAX_PRIORITIES: с configASSERT — остановка при создании, без него — молча урезается до configMAX_PRIORITIES − 1.

Ожидание и время

Имя Что делает Откуда Раздел
vTaskDelay(ticks) заснуть на ticks от момента вызова З 26
xTaskDelayUntil(*last, period) заснуть до last + period; период точный З 26
xTaskGetTickCount() счётчик тиков З 26
xTaskGetTickCountFromISR() то же П 28
taskYIELD() уступить равным по приоритету З 26

Состояние и приоритет

Имя Что делает Откуда Раздел
vTaskSuspend(h) / vTaskResume(h) приостановить / вернуть З 26
xTaskResumeFromISR(h) вернуть из прерывания П 28
vTaskPrioritySet(h, prio) / uxTaskPriorityGet(h) сменить / узнать приоритет З 26
eTaskGetState(h) eRunning…eDeleted; вечное ожидание — eBlocked, eSuspended — только после vTaskSuspend З 26
pcTaskGetName(h) имя З —
uxTaskGetStackHighWaterMark(h) наименьший остаток стека, в словах З 26
xTaskGetCurrentTaskHandle() описатель текущей; нужен INCLUDE_xTaskGetCurrentTaskHandle З —
xTaskGetSchedulerState() не запущен / идёт / приостановлен З —
uxTaskGetNumberOfTasks() сколько задач З —
vTaskGetInfo(h, *status, stack?, state) всё о задаче разом З 35

Планировщик и критические секции

Имя Что делает Откуда Раздел
vTaskSuspendAll() / xTaskResumeAll() запретить / разрешить переключение; прерывания идут З 30
taskENTER_CRITICAL() / taskEXIT_CRITICAL() BASEPRI до порога; вкладываются З 30
taskENTER_CRITICAL_FROM_ISR() / taskEXIT_CRITICAL_FROM_ISR(saved) то же с сохранением маски П 28, 30
taskDISABLE_INTERRUPTS() / taskENABLE_INTERRUPTS() маска без счётчика З —
portYIELD_FROM_ISR(woken) переключиться на выходе из обработчика П 28
portEND_SWITCHING_ISR(woken) то же под другим именем П 28

Уведомления задачи

Имя Что делает Откуда Раздел
xTaskNotifyGive(h) +1, как семафор З 31
vTaskNotifyGiveFromISR(h, *woken) то же П 28, 31
ulTaskNotifyTake(clear, ticks) ждать; pdTRUE — обнулить, pdFALSE — −1 З 31
xTaskNotify(h, value, action) послать значение; действие из пяти З 31
xTaskNotifyFromISR(h, value, action, *woken) то же П 31
xTaskNotifyWait(clearIn, clearOut, *value, ticks) ждать значение З 31
xTaskNotifyStateClear(h) сбросить ожидание З —
ulTaskNotifyValueClear(h, mask) сбросить биты значения З —

Действия: eNoAction, eSetBits, eIncrement, eSetValueWithOverwrite, eSetValueWithoutOverwrite.


34.2 Очереди

Имя Что делает Откуда Раздел
xQueueCreate(len, itemSize) из кучи; NULL при отказе З Н 27
xQueueCreateStatic(len, itemSize, storage, *buf) из своей памяти З Н 27
xQueueSend(q, *item, ticks) в конец; то же, что SendToBack З 27
xQueueSendToBack(q, *item, ticks) в конец З 27
xQueueSendToFront(q, *item, ticks) в начало — вне очереди З 27
xQueueOverwrite(q, *item) перезаписать; только для очереди длиной 1 З 27
xQueueReceive(q, *item, ticks) забрать З 27
xQueuePeek(q, *item, ticks) посмотреть, не забирая З 27
…FromISR(q, *item, *woken) двойники шести выше, без ticks; у xQueuePeekFromISR(q, *item) признака нет П 27, 28
uxQueueMessagesWaiting(q) сколько лежит З 27
uxQueueMessagesWaitingFromISR(q) то же П —
uxQueueSpacesAvailable(q) сколько влезет З —
xQueueIsQueueEmptyFromISR(q) / xQueueIsQueueFullFromISR(q) пуста / полна П —
xQueueReset(q) очистить З —
vQueueDelete(q) удалить З —

Очередь копирует элемент. errQUEUE_EMPTY, errQUEUE_FULL и pdFAIL — один и тот же ноль.


34.3 Семафоры и мьютексы

Имя Что делает Откуда Раздел
xSemaphoreCreateBinary() / …Static(*buf) двоичный; создаётся пустым З Н 28
xSemaphoreCreateCounting(max, init) / …Static(max, init, *buf) считающий З Н 28
xSemaphoreCreateMutex() / …Static(*buf) мьютекс; создаётся свободным, в отличие от двоичного семафора З Н 30
xSemaphoreCreateRecursiveMutex() / …Static(*buf) рекурсивный З Н 30
xSemaphoreTake(s, ticks) взять З 28, 30
xSemaphoreTakeFromISR(s, *woken) взять без ожидания; не для мьютекса П 28
xSemaphoreGive(s) отдать; мьютекс — только владелец З 28, 30
xSemaphoreGiveFromISR(s, *woken) отдать; не для мьютекса П 28
xSemaphoreTakeRecursive(m, ticks) / xSemaphoreGiveRecursive(m) для рекурсивного; обычные не работают З 30
uxSemaphoreGetCount(s) текущее значение З —
xSemaphoreGetMutexHolder(m) кто держит; нужен INCLUDE_xSemaphoreGetMutexHolder З —
vSemaphoreDelete(s) удалить З —

Мьютекс — единственный, кто наследует приоритет. Из прерывания он недоступен ни в каком виде.


34.4 Программные таймеры

Имя Что делает Откуда Раздел
xTimerCreate(name, period, reload, id, cb) создать; не запущен З Н 29
xTimerCreateStatic(…, *buf) из своей памяти З Н 29
xTimerStart(t, ticks) запустить; идущий — перезапустить З Н 29
xTimerStop(t, ticks) остановить З 29
xTimerReset(t, ticks) перезапустить отсчёт З 29
xTimerChangePeriod(t, period, ticks) сменить период; запускает стоящий З 29
xTimerDelete(t, ticks) удалить З —
…FromISR(t, *woken) двойники Start, Stop, Reset, ChangePeriod; у xTimerChangePeriodFromISR ещё и период П 29
xTimerIsTimerActive(t) идёт ли З —
xTimerGetPeriod(t) / xTimerGetExpiryTime(t) период / срок З —
pvTimerGetTimerID(t) / vTimerSetTimerID(t, id) идентификатор З 29
xTimerGetReloadMode(t) / vTimerSetReloadMode(t, mode) вид З —
xTimerGetTimerDaemonTaskHandle() описатель демона З —
xTimerPendFunctionCall(fn, arg1, arg2, ticks) выполнить функцию в демоне З 29
xTimerPendFunctionCallFromISR(fn, arg1, arg2, *woken) то же П 29

ticks у команд — ожидание места в очереди демона, не задержка запуска. Обратный вызов выполняется в демоне и не имеет права блокироваться.


34.5 Группы событий

Имя Что делает Откуда Раздел
xEventGroupCreate() / …Static(*buf) создать; 24 флага З Н 31
xEventGroupSetBits(eg, bits) поставить З 31
xEventGroupSetBitsFromISR(eg, bits, *woken) поставить через демона таймеров П 31
xEventGroupClearBits(eg, bits) / …FromISR(eg, bits) снять З / П 31
xEventGroupGetBits(eg) / …FromISR(eg) прочитать без ожидания З / П 31
xEventGroupWaitBits(eg, bits, clearOnExit, waitAll, ticks) ждать любого / всех З 31
xEventGroupSync(eg, setBits, waitBits, ticks) поставить и ждать всех неразрывно З 31
vEventGroupDelete(eg) удалить З —

Возвращаемое значение — группа в момент пробуждения; разбирать его.


34.6 Буферы потоков и сообщений

Имя Что делает Откуда Раздел
xStreamBufferCreate(size, trigger) / …Static(size, trigger, storage, *buf) поток; Create сам берёт size + 1, у Static массив ровно size и влезает size − 1 З Н 32
xStreamBufferSend(sb, *data, len, ticks) записать; возвращает байты З 32
xStreamBufferSendFromISR(sb, *data, len, *woken) то же; ноль — потеряно П 32
xStreamBufferReceive(sb, *data, max, ticks) прочитать до max З 32
xStreamBufferReceiveFromISR(sb, *data, max, *woken) то же П —
xStreamBufferBytesAvailable(sb) / xStreamBufferSpacesAvailable(sb) лежит / влезет З 32
xStreamBufferIsEmpty(sb) / xStreamBufferIsFull(sb) пуст / полон З —
xStreamBufferSetTriggerLevel(sb, n) сменить триггер З —
xStreamBufferReset(sb) очистить; отказ, если кто-то ждёт З 32
vStreamBufferDelete(sb) удалить З —
xMessageBufferCreate(size) / …Static(size, storage, *buf) с границами; +4 байта на сообщение З Н 32
xMessageBufferSend(mb, *data, len, ticks) целиком или никак З 32
xMessageBufferSendFromISR(mb, *data, len, *woken) то же П —
xMessageBufferReceive(mb, *data, max, ticks) одно сообщение; max ≥ самого длинного З 32
xMessageBufferReceiveFromISR(mb, *data, max, *woken) то же П —
xMessageBufferSpaceAvailable(mb) влезет З —
xMessageBufferIsEmpty(mb) / xMessageBufferIsFull(mb) / xMessageBufferReset(mb) состояние / очистить З —
vMessageBufferDelete(mb) удалить З —

Один писатель, один читатель.


34.7 Куча

Имя Что делает Откуда Раздел
pvPortMalloc(bytes) выделить; NULL при отказе З Н 25
vPortFree(p) вернуть; в heap_1 — останов З 25
xPortGetFreeHeapSize() свободно сейчас З 25
xPortGetMinimumEverFreeHeapSize() худшее за всё время; heap_4, heap_5 З 25
vPortGetHeapStats(*stats) подробный отчёт; heap_4, heap_5 З 25
vPortDefineHeapRegions(regions) области для heap_5; по возрастанию адресов Н 25
vApplicationMallocFailedHook() ловушка исчерпания — пишете вы — 25

34.8 Константы и макросы времени

Имя Значение Смысл
pdMS_TO_TICKS(ms) ms × tickRate / 1000 миллисекунды в тики; округляет вниз
portTICK_PERIOD_MS 1 при 1 кГц миллисекунд в тике
portMAX_DELAY 0xFFFFFFFF ждать без ограничения при INCLUDE_vTaskSuspend 1, иначе 49,7 суток при 1 кГц (раздел 27.2)
pdTRUE / pdFALSE 1 / 0 логические
pdPASS / pdFAIL 1 / 0 успех / отказ
errQUEUE_EMPTY / errQUEUE_FULL 0 / 0 неразличимы между собой и с pdFAIL
errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY −1 нет памяти
tskIDLE_PRIORITY 0 приоритет задачи простоя
configMAX_PRIORITIES из конфигурации старший доступный — на единицу меньше

Ловушки, которые пишете вы

Имя Когда вызывается Включается
vApplicationStackOverflowHook(h, name) переполнение стека, при переключении configCHECK_FOR_STACK_OVERFLOW
vApplicationMallocFailedHook() отказ кучи configUSE_MALLOC_FAILED_HOOK
vApplicationIdleHook() каждый проход простоя; не блокировать configUSE_IDLE_HOOK
vApplicationTickHook() каждый тик, из прерывания; только FromISR configUSE_TICK_HOOK
vApplicationGetIdleTaskMemory(…) память под задачу простоя configSUPPORT_STATIC_ALLOCATION
vApplicationGetTimerTaskMemory(…) память под демона таймеров то же + configUSE_TIMERS

34.9 Цена объектов

Всё измерено сборкой под Cortex‑M3 в конфигурации справочника.

Объект Байт Плюс
блок управления задачей 92 стек, минимум 512
очередь 80 len × itemSize
семафор, мьютекс 80 —
программный таймер 44 —
группа событий 32 —
потоковый буфер, буфер сообщений 36 size + 1
уведомление задачи 0 уже в блоке управления
ядро в прошивке ≈ 6,4 КБ + куча

Цена объектов FreeRTOS под Cortex‑M3 в байтах: блок управления задачей 92 плюс стек минимум 512, очередь 80 плюс len × itemSize, семафор и мьютекс 80, программный таймер 44, группа событий 32, потоковый буфер и буфер сообщений 36 плюс size + 1, уведомление 0; одна задача с минимальным стеком — 604 байта, дороже остальных объектов вместе без данных — 272

Прежде чем считать байты, выбирают сам объект. Таблицы выбора из 30.9, 31.4 и 32.7 и очередь на несколько писателей из 27.4 собраны на одной схеме, с ценой из таблицы выше:

Что выбрать для обмена: только факт — уведомление, двоичный семафор или группа событий; данные — уведомление со значением, очередь на одно место, потоковый буфер, буфер сообщений или очередь; общий ресурс — запись слова, критическая секция, приостановка планировщика, мьютекс или привратник; цена каждого — из таблицы 34.9


34.10 Вопросы для самопроверки

Ответы — в разделах, указанных в скобках. Если ответ не вспоминается сразу, раздел стоит перечитать.

Задачи

  1. Задача с приоритетом 3 крутит пустой цикл без ожиданий. Что видят задачи с приоритетами 1 и 2? (26)
  2. vTaskDelay(10) в задаче, работающей 3 мс. Каков период? Сколько срабатываний потеряется за минуту? (26)
  3. Зачем два указателя стека — MSP и PSP? (23)
  4. uxTaskGetStackHighWaterMark вернула 30. Сколько это байт? (26)
  5. Задача ждёт с portMAX_DELAY — в одном случае очередь, в другом уведомление. Что покажут eTaskGetState и vTaskList? (26)
  6. Почему из функции задачи нельзя выйти оператором return? (26)

Память

  1. Запрос pvPortMalloc(512) — сколько байт уйдёт из кучи heap_4? (25)
  2. Чем heap_1 лучше heap_4, если он не умеет освобождать? (25)
  3. Куча 10 КБ на F103C8: сколько это от всей памяти и с какого момента она занята? (25)

Прерывания

  1. Прерывание с приоритетом 3 при пороге 5 вызвало xQueueSendFromISR. Что произойдёт при включённом configASSERT? Без него? (28)
  2. Что стоит забытый portYIELD_FROM_ISR — в микросекундах? (28)
  3. Почему мьютекс нельзя отдать из прерывания? (30)
  4. Значение 0x50 в BASEPRI — откуда оно? (23, 28)

Синхронизация

  1. counter++ — сколько инструкций и что между ними может случиться? (30)
  2. Три задачи L, M, H; L держит мьютекс, H его ждёт. Что делает M и что с этим делает мьютекс? (30)
  3. Две задачи берут два мьютекса в разном порядке. Что будет и как лечится? (30)
  4. Когда одна переменная не требует защиты вовсе? (30)

Объекты

  1. Очередь на 16 сообщений по 4 байта — сколько байт всего и какая часть служебная? (27)
  2. xTimerStart(t, 100) — что означает второй аргумент? (29)
  3. Обратный вызов таймера вызвал vTaskDelay. Что случится с другими таймерами? (29)
  4. Почему в группе событий 24 флага, а не 32? (31)
  5. Чем xEventGroupSync отличается от SetBits плюс WaitBits? (31)
  6. Потоковый буфер с триггером 16 при 115 200 бод: сколько пробуждений в секунду вместо скольких? (32)
  7. xMessageBufferReceive вечно возвращает ноль. Почему? (32)

Конфигурация

  1. configCPU_CLOCK_HZ = 36 МГц при реальных 72. Что с задержками? (33)
  2. Предел одного сна в режиме без тика на SysTick при 72 МГц? Почему? (33)
  3. SVC_Handler определён дважды — откуда обе копии? (24)

34.11 Грабли

Не понять, пуста очередь или полна. errQUEUE_EMPTY, errQUEUE_FULL и pdFAIL — один и тот же ноль; смысл задаёт вызванная функция (34.8).

Задержка короче заказанной. pdMS_TO_TICKS округляет вниз (34.8).

Приоритет configMAX_PRIORITIES не работает. Старший доступный — на единицу меньше (34.8).

Зависание в vApplicationIdleHook или сбой в vApplicationTickHook. Первую нельзя блокировать, вторая вызывается из прерывания — только функции FromISR (34.8).


34.12 Итоги


Дальше: раздел 34а «Zephyr: другая операционная система — обзор для того, кто знает FreeRTOS».

Открыть в интерактивном справочнике