Давно не было статей с использованием FreeRTOS на нашем сайте. Что еще более удивительно, если учесть, что в повседневной жизни эту ОС использую регулярно. Так что сегодня без лишних слов и предисловий создадим базовый пример с поддержкой FreeRTOS для STM32. Прошли те времена, когда для включения ее в свой проект приходилось перетаскивать кучу файлов, некоторые из которых оказывались несовместимы, некоторых просто не хватало... В STM32CubeMx все намного менее интересно и делается в пару кликов. В общем, приступаем.
При работе с FreeRTOS я чаще всего придерживаюсь следующей схемы. Создаются несколько задач (task'ов), каждая из которых вызывается через равные промежутки времени со своим собственным периодом, например:
- 1 мс
- 10 мс
- 50 мс
- 100 мс
Конкретные значения могут зависеть уже от конкретных целей конкретного проекта. И далее вся работа распределяется по этим task'ам. Соответственно, те действия, которые необходимо выполнять максимально часто вызываются из задачи, период вызова которой равен 1 мс. Например, сохранение значений АЦП для последующей обработки. Другие же действия напротив нужно выполнять намного реже, к примеру, обновлять информацию на дисплее. И в итоге вся программа распределяется по этим временным уровням.
Вот сегодня и реализуем базовый проект для STM32 с поддержкой FreeRTOS и нескольких задач. Пусть task'и будут вызываться каждые 1 мс, 10 мс и 50 мс. По аналогии можно будет легко и быстро добавить и другие.
Запускаем STM32CubeMx. Сразу уточню - не будем подробно погружаться во все нюансы настройки FreeRTOS и рассматривать каждую конкретную опцию, иначе получится не статья, а книга. Максимально быстрый старт ) Если возникнут какие-либо вопросы, смело задавайте их в комментариях или на форуме, я буду рад помочь, итак, первым делом активируем FreeRTOS:
Но тут сразу же есть важный нюанс. При использовании FreeRTOS, в качестве базового таймера для HAL рекомендуется выбрать не SysTick (стоит по умолчанию), а другой. Для этого переходим в категорию SYS и меняем SysTick на один из свободных таймеров:
Поскольку мы уже условились делать базовый проект, то оставляем на этом этапе все настройки FreeRTOS без изменений:
Но добавляем наши задачи в разделе Tasks and Queues и задаем их приоритет - Normal:
Полностью аналогичным образом добавляем остальные задачи:
Все готово, генерируем, открываем и собираем проект. Видим, что CubeMx создал наши task'и:
/* creation of Task1ms */ Task1msHandle = osThreadNew(Task1msHandler, NULL, &Task1ms_attributes); /* creation of Task10ms */ Task10msHandle = osThreadNew(Task10msHandler, NULL, &Task10ms_attributes); /* creation of Task50ms */ Task50msHandle = osThreadNew(Task50msHandler, NULL, &Task50ms_attributes);
Но сейчас единственное, что связывает эти функции с нашим планом вызывать их через равные промежутки времени - это их название. Так что необходимо доработать непосредственно код функций. И для того, чтобы обеспечить периодичность выполнения task'ов мы будем использовать функцию:
void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement)
- Первый аргумент хранит значение времени, соответствующее моменту, когда задача была разблокирована в предыдущий раз. При первом вызове функции
vTaskDelayUntil()
необходимо инициализировать эту переменную текущим значением времени, а в дальнейшем функция сама будет обновлять это значение. - Второй аргумент - в нем мы уже задаем нужный нам период. В итоге функция
vTaskDelayUntil()
заблокирует нашу задачу до момента времени, равного (pxPreviousWakeTime + xTimeIncrement
).
Теперь реализуем все на практике. Подключаем:
#include "task.h"
Для task'а, который работает с периодом 10 мс получаем следующее:
void Task10msHandler(void *argument) { /* USER CODE BEGIN Task10msHandler */ TickType_t xLastWakeTime; const TickType_t xFrequency = 10 / portTICK_PERIOD_MS; xLastWakeTime = xTaskGetTickCount(); /* Infinite loop */ for(;;) { // Add code here vTaskDelayUntil(&xLastWakeTime, xFrequency); } /* USER CODE END Task10msHandler */ }
А непосредственно свой код мы добавляем перед вызовом vTaskDelayUntil()
, внутри цикла for(;;)
. Здесь значение 10 мс задается в строке:
const TickType_t xFrequency = 10 / portTICK_PERIOD_MS;
Абсолютно аналогично делаем и для других наших задач, меняя только значение периода. Кроме того, давайте добавим счетчики, которые будут инкрементироваться при вызове каждого из task'ов:
/* USER CODE BEGIN PV */ uint32_t task1msCnt = 0; uint32_t task10msCnt = 0; uint32_t task50msCnt = 0; /* USER CODE END PV */
И итоговый код, например, для task'а 50 мс:
void Task50msHandler(void *argument) { /* USER CODE BEGIN Task50msHandler */ TickType_t xLastWakeTime; const TickType_t xFrequency = 50 / portTICK_PERIOD_MS; xLastWakeTime = xTaskGetTickCount(); /* Infinite loop */ for(;;) { // Add code here task50msCnt++; vTaskDelayUntil(&xLastWakeTime, xFrequency); } /* USER CODE END Task50msHandler */ }
Вот такой механизм для организации периодических задач в FreeRTOS на контроллере STM32. Давайте соберем проект и проверим, как все это работает. Индикацией для нас будут служить счетчики вызовов task'ов:
Все отрабатывает четко по плану, задачи вызываются с заданной периодичностью. И на этом заканчиваем нашу статью, максимально быстрый старт с FreeRTOS для STM32 👍
Ссылка на полный проект - MT_FreeRTOS_Base.
Хорошая статья. Спасибо. Ждем новых статей по этой теме..
Благодарю!
Отличная статья! Расскажи, пожалуйста, побольше про работу FreeRTOS с прерываниями.
Спасибо за отличный отзыв! Постараюсь по FreeRTOS побольше статей добавить.
Вы хотели добавить статьи по FreeRTOS. Просто напоминаю 🙂
😀 Да у меня много хотелок, планов и идей, времени бы найти))
Спасибо за статью!
Всю голову сломал... При попытке компилировать вываливаются оршибки (на изображении). В чем может быть проблема?
На другом ПК всё без проблем заводится
Добрый вечер! Похоже, что с путями к файлам какая-то проблема. Точнее, либо файлы реально отсутствуют, либо пути к ним не прописаны.
А первопричиной может служить какая-нибудь галочка невыставленная в CubeMx итп.
Спасибо за ответ!
Продолжаем разбираться. Попробовали перекинуть проект с "Здорового" Cubeide - всё работает отлично. После попытки переконфигурировать через cube - всё опять слетело.
Все пути пробовал править вручную - не помогло.
Сносил Cubeide, чистил реестр по мануалам ST - не помогло.
Создал нового пользователя в системе и установил Cubeide - всё заработало..
Вероятно проблемы начались из за имени пользователя на русском. И еще выяснилось что свинота по имени CubeIde после удаления оставляет кучу мусора в пользовательской папке. Сейчас попробую почистить и отпишусь
Мда) Подтвердилось, что корень зла был из-за имени пользователя?
Да, абсолютно 🙂
Для чистоты эксперимента создал на заведомо рабочем ПК другого пользователя с именем на кириллице - выявилась та же проблема 🙂
Т.е. до попытки подключения freertos я спокойно работал со средой, но как только я решился освоить FREERTOS - сразу появилась эта проблема)
Неслабый такой внезапный сюрприз посреди работы)
Добрый день! Продолжение случайно не предвидится? Или, может быть, оно уже есть, просто я не могу найти? Ткните ссылкой, пожалуйста
Добрый! Подумаю на счет продолжения, вообще я из проектов для сайта наоборот всегда rtos убираю, чтобы не перегружать...