При разработке электронных устройств до поры до времени (иногда довольно долго) удается обходиться без использования операционной системы. Но наступает момент, когда разрабатываемый девайс должен выполнять огромное количество различных функций или, например, должен обеспечивать возможность добавления новой задачи позднее. В этих случаях обойтись без RTOS (операционная система реального времени, ОСРВ) становится невозможно. Как раз про RTOS эта статья, а точнее про ее установку и использование для микроконтроллеров STM32.
Сразу скажу, что я остановил свой выбор на FreeRTOS – эта ОСРВ портирована на огромное количество архитектур, что является, безусловно, большим плюсом. Есть в сети очень хороший мануал на русском языке, там правда очень много букв, но зато все описано максимально подробно и понятно.
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32, а также статья на смежную тему из нового курса: STM32CubeMx. Быстрый старт с FreeRTOS для STM32.
Итак, в чем же кроется основная идея ОСРВ?
Операционная система реального времени позволяет организовать множество разнообразных задач и обеспечивает их поочередное выполнение и при этом учитывает приоритет каждой задачи. Также благодаря RTOS можно замутить запуск какой-либо задачи через определенные промежутки времени (причем обеспечивается высокая точность временных интервалов). Задачи могут обмениваться информацией друг с другом при помощи реализованной в RTOS очереди. И наконец, при необходимости возможно легким движением руки добавлять все новые и новые задачи. Конечно же, есть и минусы, связанные в основном с тем, что RTOS занимает довольно много памяти. Но эта проблема актуальна скорее для более старых моделей AVR и других контроллеров с небольшими объемами памяти. Так что нас это не будет сильно волновать.
Что можно сказать конкретно про FreeRTOS... FreeRTOS – операционная система с открытым исходным кодом. Можно абсолютно бесплатно и без проблем скачать ее последнюю версию (что кстати необходимо сделать, поскольку мы скоро перейдем к созданию проекта под FreeRTOS). Написана библиотека на C с наличием небольших ассемблерных вставок. Опять же, очень советую прочитать мануал на нее. Там и про различные типы многозадачности, про ОСРВ в целом, про организацию прерываний, про разделение процессорного времени между задачами. В общем, почитать стоит. Мы же рассмотрим практические аспекты установки FreeRTOS, естественно по ходу процесса постараюсь по максимуму объяснять, что и зачем делается. Итак, приступаем...
Для начала дружно скачаем FreeRTOS. У меня, например, FreeRTOS v.7.1.0, по-моему, это последняя версия (на момент написания статьи). Копируем папку с FreeRTOS куда-нибудь поближе к проекту. Именно копируем а не вырезаем/вставляем, поскольку сейчас мы будем удалять из нее то, что нам пока не нужно )
Скопировали, продолжаем…
Я создал в папке с проектом пару папок – Header_Files и Source_Files для заголовочных файлов и файлов с исходным кодом соответственно. Пусть пока для простоты все файлы, которые понадобятся для запуска RTOS лежат там.
Заходим в папку FreeRTOSV7.1.0\FreeRTOSV7.1.0\Demo и удаляем оттуда все, кроме папки CORTEX_STM32F103_Keil. Заходим в единственную оставшуюся папку и обращаем наше внимание на файл FreeRTOSConfig.h. Копируем его в папку header’ов. Давайте его сразу же и отредактируем. Открываем и копируем в этот файл следующие строки:
#define xPortSysTickHandler SysTick_Handler #define xPortPendSVHandler PendSV_Handler #define vPortSVCHandler SVC_Handler
Давайте разбираться, что мы сделали. А всего лишь переопределили обработчики прерываний для того, чтобы прерывания из FreeRTOS попали в вектор прерываний.
Папка FreeRTOSV7.1.0\FreeRTOSV7.1.0\Source – копируем из нее все .c файлы в STMProjects\FreeRTOS\Source_Files. Аналогично, файлы из FreeRTOSV7.1.0\FreeRTOSV7.1.0\Source\include помещаем в STMProjects\FreeRTOS\Header_Files.
Теперь проследуем в FreeRTOSV7.1.0\FreeRTOSV7.1.0\Source\portable и там прямо манит папка под названием Keil. Заходим и видим "Nothing to see here". Первая мысль – "Как же так?...". Но не отчаиваемся, нам намекают посмотреть папку RVDS – заходим в нее, выбираем наше ядро (папка ARM_CM3) и забираем оттуда два файла к нам в проект.
Осталось зайти в папку FreeRTOSV7.1.0\Source\portable\MemMang и скопировать файл heap_2.c. Все файлы собрали, создаем новый проект в Keil и приступаем ко второй части.
Теперь нам нужно добавить все файлы в проект – это и CMSIS, и SPL, и FreeRTOS:
В настройках проекта прописываем все пути к файлам, присутствующим в проекте, а также не забываем про USE_STDPERIPH_DRIVER:
И, наконец, создадим практический пример! Давайте сделаем традиционное мигание диодом, но уже с использованием ОСРВ. Для начала надо подключить файлы:
#include "task.h" #include "queue.h" #include "FreeRTOS.h" #include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h"
Теперь надо настроить нужный нам вывод, на который мы повесим светодиод. Кстати, в мануале на FreeRTOS есть еще и правила, в соответствии с которыми рекомендуется оформлять код:
void vFreeRTOSInitAll() { RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); GPIO_StructInit(&port); port.GPIO_Mode = GPIO_Mode_Out_PP; port.GPIO_Pin = GPIO_Pin_0; port.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &port); }
Тут все как обычно, включили тактирование, настроили вывод. Двигаемся дальше:
void vLedTask (void *pvParameters) { while(1) { if (state == 0) { GPIO_SetBits(GPIOA,GPIO_Pin_0); state = 1; } else { GPIO_ResetBits(GPIOA,GPIO_Pin_0); state = 0; } } vTaskDelete(NULL); }
Это задача, которую мы будем выполнять. Тут реализовано, собственно, мигание диодом. В конце функции, за пределами цикла, принято вызывать функцию vTaskDelete(NULL), которая удалит задачу в случае выхода из бесконечного цикла.
Теперь осталось только реализовать функцию main():
int main() { vFreeRTOSInitAll(); xTaskCreate(vLedTask,(signed char*)"LedTask", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL); vTaskStartScheduler(); }
Сначала вызываем функцию для конфигурирования вывода PA0. А вот дальше интереснее – создаем задачу при помощи функции xTaskCreate(). Рассмотрим этот момент подробнее. Функция принимает аргументы:
- pvTaskCode — указатель на функцию, реализующую задачу
- pcName — нуль-терминальная (заканчивающаяся нулем) строка, определяющая имя функции
- usStackDepth — глубина (размер) собственного стека создаваемой задачи, в данном случае мы используем минимально допустимый размер стека, определенный макросом configMINIMAL_STACK_SIZE
- pvParameters— произвольный параметр, передаваемый задаче при ее создании
- uxPriority — определяет приоритет создаваемой задачи, мы берем tskIDLE_PRIORITY + 1 = (приоритет задачи бездействие) + 1
- pxCreatedTask — может использоваться для получения дескриптора (handle) создаваемой задачи, который помещается по адресу pxCreatedTask после успешного создания задачи.
Остается в функции main() вызвать планировщика, и все, готово!
Запускаем отладчик и смотрим, что из всего этого вышло. Открываем окошко логического анализатора и настраиваем его на мониторинг вывода РА0 (про это написано в предыдущих статьях по STM32, вот тут, например). Запускаем программу:
Все работает четко по плану )
Подведем итог. Мы установили FreeRTOS, разобрались с ее работой и даже создали небольшую программку с применением ОСРВ. В следующей статье тоже будем писать под FreeRTOS, но это будет что-нибудь посложнее и поинтереснее. Также вот ссылка на еще один пример организации работы программы с операционной системой.
P. S. Вот, кстати, хорошее описание FreeRTOS на русском - ссылка.