1. Введение: зачем нужна многозадачность в встраиваемых системах
Современные микроконтроллерные приложения всё чаще требуют одновременного выполнения нескольких независимых функций:
- сбор данных с датчиков;
- обработка и фильтрация сигналов;
- управление исполнительными устройствами;
- обмен данными по беспроводным/проводным интерфейсам;
- отображение информации на экране;
- ведение журнала и диагностика.
Без RTOS программист вынужден:
- реализовывать «псевдомногозадачность» через циклический опрос (polling);
- вручную управлять приоритетами и временем выполнения;
- бороться с гонками и взаимными блокировками.
RTOS (Real‑Time Operating System) решает эти проблемы, предоставляя:
- планировщик задач (scheduler) — автоматически переключает контексты;
- механизмы синхронизации — семафоры, мьютексы, очереди;
- таймеры — точное выполнение действий по времени;
- управление памятью — безопасное выделение ресурсов.
2. Базовые понятия многозадачности в RTOS
2.1. Задача (Task)
- Независимый поток выполнения с собственным стеком и приоритетом.
- Состояния:
- Running — исполняется на CPU;
- Ready — готова к исполнению, ждёт CPU;
- Blocked — ждёт события (семафор, очередь, таймер);
- Suspended — временно отключена.
2.2. Приоритет задачи
- Числовое значение (например, от 0 до 255).
- Чем выше число — тем выше приоритет.
- Планировщик всегда выбирает готовую задачу с наивысшим приоритетом.
2.3. Переключение контекстов (Context Switch)
- Сохранение регистров и стека текущей задачи.
- Загрузка регистров и стека следующей задачи.
- Время переключения — десятки микросекунд.
2.4. Типы планировщиков
- Вытесняющий (Preemptive) — высокоприоритетная задача немедленно прерывает низкоприоритетную.
- Кооперативный (Cooperative) — задача сама отдаёт управление (редко в современных RTOS).
- Циклический (Round‑Robin) — задачи одного приоритета исполняются по очереди.
3. Проектирование многозадачной системы: шаги и рекомендации
3.1. Анализ требований
- Перечислить все функции системы (например: «считывать датчик каждые 100 мс», «отправлять данные по UART каждые 1 с»).
- Определить временные ограничения (дедлайны, периоды).
- Выделить критические секции (где недопустимы задержки).
- Оценить объём данных (размер очередей, буферов).
3.2. Разбиение на задачи
- Каждая задача — логически завершённый модуль.
- Примеры:
SensorTask— опрос датчиков;ControlTask— алгоритм управления;CommTask— обмен данными с ПК;DisplayTask— обновление экрана;LoggerTask— запись в энергонезависимую память.
Рекомендации:
- Не делать слишком много задач (накладные расходы на переключение).
- Задачи с жёсткими дедлайнами — высокий приоритет.
- Длительные операции — выносить в низкоприоритетные задачи.
3.3. Назначение приоритетов
- Метод Rate Monotonic Scheduling (RMS):
- Чем короче период задачи — тем выше приоритет.
- Пример: задача с периодом 10 мс > задача с периодом 100 мс.
- Учёт зависимостей: если задача
Aждёт данные отB, тоBдолжна иметь приоритет ≥A.
3.4. Распределение ресурсов
- Стек задачи:
- Минимальный размер — 128–512 байт (зависит от вложенных вызовов).
- Тестировать на переполнение (в RTOS есть механизмы контроля).
- Куча (heap):
- Выделять память статически, если возможно.
- Динамическое выделение — только для буферов данных.
- Периферия:
- Один ресурс (например, UART) — одна задача‑владелец.
- Доступ через очереди/семафоры.
4. Механизмы синхронизации и коммуникации
4.1. Семафоры (Semaphores)
- Binary Semaphore — сигнализация «событие произошло».
- Counting Semaphore — учёт доступных ресурсов (например, буферов).
Пример (FreeRTOS):
SemaphoreHandle_t xSemaphore;
void ProducerTask(void *pvParameters) {
for(;;) {
// Производим данные
xSemaphoreGive(xSemaphore); // Сигнализируем
vTaskDelay(100);
}
}
void ConsumerTask(void *pvParameters) {
for(;;) {
if (xSemaphoreTake(xSemaphore, portMAX_DELAY) == pdPASS) {
// Обрабатываем данные
}
}
}
4.2. Мьютексы (Mutexes)
- Защита критических секций от одновременного доступа.
- В отличие от семафора — только владелец может освободить.
Пример:
MutexHandle_t xMutex;
void Task1(void *pvParameters) {
xMutexTake(xMutex, portMAX_DELAY);
// Работаем с общим ресурсом
xMutexGive(xMutex);
}
void Task2(void *pvParameters) {
xMutexTake(xMutex, portMAX_DELAY);
// Работаем с тем же ресурсом
xMutexGive(xMutex);
}
4.3. Очереди (Queues)
- Передача данных между задачами.
- Буферизация сообщений.
Пример:
QueueHandle_t xQueue;
typedef struct {
uint32_t sensor_id;
float value;
} SensorData_t;
void SensorTask(void *pvParameters) {
SensorData_t data = {.sensor_id = 1, .value = 25.5};
xQueueSend(xQueue, &data, portMAX_DELAY);
}
void ProcessingTask(void *pvParameters) {
SensorData_t received;
if (xQueueReceive(xQueue, &received, portMAX_DELAY) == pdPASS) {
// Обрабатываем полученные данные
}
}
4.4. События (Event Flags)
- Многобитовая сигнализация (каждому биту — своё событие).
- Эффективнее, чем множество семафоров.
5. Управление временем: таймеры и задержки
5.1. Системный тик (Tick)
- Базовый квант времени RTOS (обычно 1–10 мс).
- Определяет точность планировщика и таймеров.
5.2. Задержки выполнения
vTaskDelay()(FreeRTOS) /k_sleep()(Zephyr) — блокировка задачи на N тиков.vTaskDelayUntil()— точная периодичность (учитывает время исполнения).
5.3. Программные таймеры
- Однократные или периодические вызовы функций.
- Отдельный поток выполнения (не блокирует вызывающую задачу).
Пример (FreeRTOS):
TimerHandle_t xTimer;
void TimerCallback(TimerHandle_t xTimer) {
printf("Timer expired!\n");
}
xTimer = xTimerCreate("MyTimer", pdMS_TO_TICKS(1000),
pdTRUE, 0, TimerCallback);
xTimerStart(xTimer, 0);
6. Практические аспекты разработки
6.1. Отладка многозадачности
- Мониторинг задач:
- список задач, их приоритеты, состояние, использование стека;
- в FreeRTOS:
uxTaskGetSystemState().
- Трассировка:
- логирование переключений задач, событий;
- инструменты типа Tracealyzer.
- Проверка на взаимные блокировки (deadlocks):
- анализ графов зависимостей;
- использование тайм‑аутов в
xSemaphoreTake().
6.2. Оптимизация производительности
- Минимизировать переключение контекстов (избегать частых коротких задач).
- Использовать статическое выделение памяти (без
malloc/free). - Для высокочастотных событий — обработчики прерываний (ISR), а не задачи.
6.3. Энергосбережение
- Задачи, не требующие немедленного исполнения, переводить в Blocked/Suspended.
- Использовать режимы сна CPU между тиками (в RTOS есть



