Как использовать Freertos для эффективной разработки встроенных приложений

Введение: Роль операционных систем реального времени в встроенном дизайне

Встроенные системы все чаще требуют предсказуемого исполнения, низкой задержки и одновременной обработки нескольких событий. Операционная система реального времени (RTOS) обеспечивает основу для удовлетворения этих требований, не заставляя разработчиков строить логику планирования с нуля. Среди доступных опций FreeRTOS выделяется как легкое ядро с открытым исходным кодом, которое стало фактическим выбором для микроконтроллеров (MCU) и небольших процессоров. Его минимальный охват, обширная портативность и богатый набор функций позволяют инженерам создавать эффективные, надежные и масштабируемые приложения, сохраняя при этом низкие затраты на оборудование.

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

Что такое FreeRTOS?

FreeRTOS - это ядро операционной системы реального времени, разработанное специально для встроенных систем. Он был создан Ричардом Барри и в настоящее время поддерживается под зонтиком Amazon Web Services (AWS) FreeRTOS, обеспечивая постоянную поддержку и согласование с экосистемами Интернета вещей (IoT). Ядро обеспечивает упреждающую многозадачность, межзадачную связь, примитивы синхронизации и программные таймеры, при этом требуя всего несколько килобайт оперативной памяти.

Ключевые атрибуты, которые делают FreeRTOS настолько широко принятыми, включают:

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

Основные понятия и объекты ядра

Прежде чем перейти к практическим шагам, важно понять основные строительные блоки, которые предоставляет FreeRTOS.

Задачи

Задачи — это независимые потоки выполнения, которые разделяют время процессора по приоритетному превентивному планировщику. Каждая задача имеет свой стек и контекст. FreeRTOS поддерживает неограниченное количество задач (ограничено только доступной памятью). Планировщик переключается между задачами исходя из их приоритетов и состояния (готовый, запущенный, заблокированный, приостановленный).

Очередь

Очереди позволяют передавать сообщения между задачами и между прерываниями и задачами. Они являются основным механизмом межзадачной связи. Очереди FreeRTOS являются FIFO или LIFO (последние через ] и могут содержать элементы данных фиксированного размера. Операции очереди предназначены для эффективного и безопасного прерывания при использовании с правильными вариантами API.

Семафоры и мутексы

FreeRTOS предлагает бинарные семафоры, подсчет семафоров и мутексов. Бинарные семафоры действуют как простые флаги, используемые для передачи сигналов или синхронизации задач (например, уведомление о том, что произошло прерывание). Подсчет семафоров управляет несколькими ресурсами, в то время как мутексы обеспечивают взаимное исключение со встроенным механизмом наследования приоритетов для предотвращения инверсии приоритета.

Программное обеспечение Timers

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

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

Группы событий позволяют задаче ждать, пока будет установлена комбинация нескольких битов (событий). Они упрощают сценарии, где действие зависит от нескольких асинхронных условий, таких как показания датчиков и нажатие кнопки пользователя.

Начало работы с FreeRTOS

Принятие FreeRTOS требует оценки целевого оборудования, получения источника ядра и настройки его в соответствии с ограничениями вашего приложения.

Выбор совместимого микроконтроллера

Порты FreeRTOS существуют практически для каждого популярного семейства MCU. Подтвердите, что выбранное вами устройство имеет достаточно оперативной памяти и вспышки для ядра плюс ваши задачи. Например, ARM Cortex-M0+ с 16 КБ ОЗУ может с комфортом выполнять несколько задач, в то время как Cortex-M4 с 256 КБ ОЗУ поддерживает многие. Проверьте официальную страницу порта FreeRTOS или SDK вашего поставщика кремния для предварительно интегрированных примеров.

Скачать и интегрировать ядро

Последний исходный код FreeRTOS доступен на официальном сайте FreeRTOS или через GitHub. Дистрибутив включает в себя ядро ядра (), файлы переносного уровня () и демо-проекты. Для эффективного старта скопируйте и соответствующий переносной каталог в дерево проекта. Многие современные IDE, такие как STM32CubeIDE, IAR Embedded Workbench и VS Code с PlatformIO, предлагают прямую интеграцию.

Конфигурация: Файл

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

  • : Настройка на 1 для превентивного планирования (типично для приложений реального времени). Настройка на 0 для совместного планирования.
  • : Фактическая тактовая частота ЦПУ (используется ядром для правильного синхронизации).
  • : Частота прерывания таймера системы тика. Общие значения составляют 100 Гц (10 мс тика) или 1000 Гц (1 мс тика). Более высокие скорости улучшают разрешение, но увеличивают накладные расходы.
  • : Общее количество оперативной памяти, доступной для динамического распределения памяти (используется ).
  • : Размер стека (словами) для незанятой задачи и по умолчанию для новых задач, если не указано. Убедитесь, что он достаточно велик для вложенных вызовов функций и контекстов прерываний.
  • : Максимальная длина читаемых человеком имен задач (помогает отладке).
  • : Настройка на 1 для 16-битных счетчиков клещей (сохраняет оперативную память, но ограничивает максимальное значение клещей до 65535 — не рекомендуется для систем с длительной или высокой скоростью клещей).

Кроме того, выберите схему управления кучой через и . Ядро включает в себя пять реализаций кучи (heap 1 to heap 5) с различными компромиссами в фрагментации, скорости распределения и детерминизме. Большинство приложений используют (первое соответствие с коалесцированием) или (неоднородные области памяти).

Создание задач и управление жизненным циклом

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

Задача создается путем определения функции, которая никогда не возвращается (типичная петля ) и вызова :

TaskHandle_t xHandle;
xTaskCreate(
 vTaskFunction, // Task function pointer
 "MyTask", // Name for debugging
 configMINIMAL_STACK_SIZE, // Stack size in words
 NULL, // Parameters passed to task
 2, // Priority (higher number = higher priority)
 &xHandle // Optional task handle
);

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

Приоритеты задач и планировщик

FreeRTOS поддерживает приоритеты от 0 (самый низкий) до (самый высокий). Бездействующая задача работает при приоритете 0. Превентивный планировщик всегда будет выполнять задачу с самым высоким приоритетом. Если две задачи имеют один и тот же приоритет, они имеют временный срез (круглый кусочек) с продолжительностью, равной одному клещевому периоду. Избегайте предоставления некритических задач с тем же приоритетом, что и критические; в противном случае срез времени может вызвать дрожь в высокоприоритетных задачах.

Задача государств

Каждая задача существует в одном из этих состояний:

  • Бег: В настоящее время выполняется задача (только одна задача на ядро процессора).
  • Готовность: Задание может выполняться, но в настоящее время выполняется задача более высокого приоритета или равного приоритета.
  • Заблокировано: Задание — ожидание события (тайм-аут, сообщение о очереди, семафор и т. д.).
  • Отложена: Задание удаляется из списка готовности планировщика, как правило, через .

Блокировка является основным механизмом эффективного использования процессора: вместо опроса задача просто ждет условия, позволяя выполнять задачи с более низким приоритетом.

Защита от уплотнений и избыточной защиты

Одним из наиболее распространенных источников ошибок FreeRTOS является переполнение стека. Ядро обеспечивает две дополнительные проверки: . При включении оно может обнаруживать переполнения во время переключения контекста. Всегда выделяйте щедрые размеры стека во время разработки и используйте функцию , чтобы увидеть минимальное свободное пространство стека. Увеличьте стек по мере необходимости перед выпуском.

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

Синхронизация и межзадачная коммуникация

Задачи редко работают изолированно; им необходимо координировать и обмениваться данными. FreeRTOS предоставляет несколько механизмов, каждый из которых подходит для конкретных моделей.

Очередь за передачу данных

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

  • [29] — отправьте пункт из задания (блок, если он заполнен).
  • — получить товар (блок, если пустой).
  • В этом случае они будут иметь следующие значения: и .

Всегда проверяйте значения возврата; очередь может быть полной или вызов может отсчитываться. В ISRs возврат указывает, нужен ли переключатель контекста.

Бинарные семафоры как простые сигналы

Бинарные семафоры идеально подходят для уведомления задачи о том, что произошло событие. Например, прерывание GPIO может «дать» семафор, а ожидающая задача может «взять» его и обработать событие. Это отсоединяет рутины обслуживания прерываний (ISR) от логики приложения. ISR использует ; задача использует в цикле.

Мутексы с приоритетным наследованием

При доступе к совместно используемому ресурсу (например, UART или структуре данных) несколько задач используют мутекс вместо двоичного семафора. Мутексы включают в себя механизм наследования приоритета, который временно повышает приоритет задачи, удерживающей замок, до наивысшего приоритета любой задачи ожидания. Это предотвращает бесконечное блокирование среднеприоритетных задач (приоритетная инверсия). Всегда удерживайте мутекс в течение как можно более короткого времени.

Подсчет семафоров для управления ресурсами

Подсчет семафоров отслеживает количество доступных экземпляров ресурса. Например, пул из пяти каналов DMA можно управлять с помощью подсчета семафора, инициализированного до 5. Задача «забирает» семафор для приобретения канала и «дает» его обратно, когда это сделано. Это предотвращает перераспределение.

Событийные группы для многокондиционной синхронизации

Если задача должна ждать, пока не произойдет несколько независимых событий, группы событий более эффективны, чем несколько семафоров. биты устанавливаются задачами или ISR, а задача ожидания может указывать маску битов и нужно ли устанавливать все или любое. API включает в себя , и их аналоги ISR.

Обработка прерываний: отложенная обработка

Одним из наиболее важных шаблонов в FreeRTOS является сохранение чрезвычайно короткой ISR. Вместо выполнения сложной обработки внутри прерывания используйте следующий подход:

  1. Внутри ISR собирайте минимальные данные и сигнализируйте о задаче (через семафор, очередь или уведомление о задаче).
  2. Разблокируйте задачу, которая выполняется в нормальном приоритете для выполнения тяжелой подъемной силы.
  3. Используйте версии вызовов FreeRTOS API (, и т.д.) и проверьте параметр . Если разблокированная задача имеет более высокий приоритет, чем прерванная задача, запрашивается переключатель контекста.

Эта отложенная обработка прерываний (также называемая обработчиком «снизу-полу») гарантирует, что система остается отзывчивой, сохраняя предсказуемую задержку прерываний. FreeRTOS также поддерживает вложение прерываний, но вы должны убедиться, что уровни приоритета прерываний настроены правильно — для ARM Cortex-M ядро требует, чтобы для таймера клещей и любых ISR, вызывающих API, использовался самый высокий уровень приоритета, доступный пользователю.

Лучшие практики для готовых приложений FreeRTOS

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

Управление памятью

Выберите реализацию кучи, которая соответствует вашему шаблону распределения. heap 4 , как правило, хороший по умолчанию, потому что он объединяет смежные свободные блоки. Если ваше приложение часто создает и удаляет задачи или очереди, избегайте heap 2 (без коалесцирования), поскольку это приводит к фрагментации. Для систем, критически важных для безопасности, полностью рассмотрите статическое распределение.

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

Стратегия приоритетного назначения

Назначить приоритеты на основе сроков и критичности каждой задачи. Типичная схема:

  • Наиболее приоритетный: Временные критические циклы управления (например, двигательный PID, обработка звука).
  • Средний приоритет: Периодическое получение данных с умеренными требованиями к задержке.
  • Низкий приоритет: Фоновые задачи, обновления пользовательского интерфейса, логирование.

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

Оптимизация мощности: Tickless Idle

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

Отладка и профилирование

FreeRTOS включает функцию статистики времени выполнения (включает и ), которая обеспечивает проценты выполнения задач. Это помогает идентифицировать борозды процессора и киоски. Кроме того, функции и выводят считываемые человеком резюме в буфер символов.

Для более глубокого анализа включите FreeRTOS + Trace (теперь часть AWS IoT Device Tester) (FLT:0) (FLT:1), этот инструмент записывает события ядра (переключатели контекста, операции очереди, записи ISR) и отображает их в временной шкале, что бесценно для диагностики проблем с временем и приоритетных инверсий.

Расширенные возможности

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

Уведомления о задачах обеспечивают легкую альтернативу семафорам и очередям для простой сигнализации. Каждая задача имеет встроенное 32-битное значение уведомления и ожидающее количество уведомлений. Отправка уведомления (через или ) может разблокировать принимающую задачу с нулевыми накладными расходами от отдельного объекта ядра. Это быстрее и использует меньше оперативной памяти, чем семафоры. Используйте уведомления для шаблонов связи один к одному.

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

Введенные в FreeRTOS V10.0.0, буферы потоков позволяют передавать данные переменной длины между задачами или между ISR и задачей без очереди фиксированного размера. Буфер сообщений - это буфер потоков, который также сохраняет границы сообщений. Они полезны для ситуаций, когда размер данных не известен заранее, таких как сетевые стеки или парсеры команд.

Co-Routines (Наследие)

FreeRTOS также включает в себя co-routines, которые являются задачами без стека, которые разделяют один стек. Они редко используются в современных приложениях, потому что они усложняют отладку и не имеют полных функций задач. Рекомендуется использовать стандартные задачи, если вы не сильно ограничены RAM (например, 8-битный MCU).

Заключение

FreeRTOS обеспечивает надежную, хорошо документированную основу для создания эффективных встроенных приложений. Его поддержка превентивных многозадачностей, богатых примитивов синхронизации и расширенных функций, таких как бесщелочное бездействие и уведомления о задачах, делает его подходящим для всего, от простых узлов датчиков до сложных шлюзов IoT. Понимая параметры конфигурации ядра, следуя лучшим практикам управления памятью и приоритетного назначения, и используя инструменты отладки, вы можете разработать надежные системы реального времени, которые максимизируют производительность оборудования при сохранении отзывчивости.

Для дальнейшего изучения, обратитесь к справочному руководству FreeRTOS и изучите демо-приложения, включенные в дерево источников. Многие производители кремния, такие как STMicroelectronics и NXP, предоставляют руководства по интеграции для микроконтроллеров. С практикой FreeRTOS становится естественной частью любой встроенной цепочки инструментов разработчика, что позволяет создавать масштабируемое, поддерживающее прошивку, которая отвечает требованиям современных периферийных устройств.