Пример проекта с использованим FreeRTOS

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

Усложняем логику приложения

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

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

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

Эскиз программы

За вывод информации на экран отвечает отдельная задача tskDisplay. Она отрабатывает достаточно быстро, поэтому назначим ей самый высокий приоритет (из всех наших задач) — osPriorityNormal. При отображении времени обновлять показание быстрее, чем за 1 секунду, не имеет смысла. Поэтому пусть блок RTC вызывает прерывание раз в секунду, а обработчик освобождает семафор, что ведет к пробуждению задачи tskDisplay. То же самое происходит в других режимах, а то, что должно отображаться, определяется через внешнюю переменную state.

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

Для получения данных от датчика температуры используется самописный драйвер интерфейса 1-Wire (задача tskOneWire). Он толерантен к временным задержкам, поэтому использовать критические секции там нет необходимости. Время выполнения довольно долгое, зададим приоритет ниже нормального (osPriorityBelowNormal).

Другая функция, о которой мы условились, это работа с компьютером через COM-порт. В микронтроллере имеется интерфейс USART, которым мы и будем пользоваться (задача tskUART). Установим ему такой же приоритет, как задаче tskOneWireosPriorityBelowNormal.

Изменение яркости дисплея (brightness) не самая важная задача в нашем устройстве, т.е. приоритет задачи, которая отвечает за данную функциональность, должен быть ниже, чем всех остальных задач. Путь это будет osPriorityIdle. К тому же эта задача не должна выполняться часто — период в 10 секунд более чем достаточен.

Чтобы продемонстрировать больше возможностей операционной системы, для воспроизведения звука мы будем использовать таймеры, а не задачу, тем более что всё, что они будут делать, это задавать нужную частоту ШИМ, т.е. не сильно нагружать систему.

Таким образом, диаграмма нашей системы будет выглядеть так:

Приступим к реализации.

Настройка FreeRTOS

Установим вытесняющий планировщик с квантованием времени.

#define configUSE_PREEMPTION                    1
#define configUSE_TIME_SLICING                  1

Нам потребуются мьютексы, подключим их.

#define configUSE_MUTEXES                       1

Сопрограммы мы использовать не будем.

#define configUSE_CO_ROUTINES                   0
#define configMAX_CO_ROUTINE_PRIORITIES         1

Нам потребуются функции для уведомлений задач.

#define configUSE_TASK_NOTIFICATIONS            1

Для реализации прошивки нам потребуются два таймера, зададим необходимые параметры.

#define configUSE_TIMERS                        1
#define configTIMER_TASK_PRIORITY               2
#define configTIMER_QUEUE_LENGTH                5
#define configTIMER_TASK_STACK_DEPTH            configMINIMAL_STACK_SIZE
0

Изменено: