STM32, C++ и FreeRTOS. Разработка с нуля. Часть 1

Не так давно мой отдел столкнулся с трудностями поиска новых инженеров программистов для разработки встроенного ПО. Опытным и умным не нравился уровень зарплаты, а молодых просто нет в нашем городе. Поэтому под патронажем нашей доблестной глобальной компании со штаб квартирой где-то в Сент Луисе, мы начали сначала набирать студентов в интернатуру, а потом, решили пойти другим путем и сделать целых два курса по разработке ПО, а уже там самим выбирать самых “толковых” если понадобятся вдруг новые сотрудники. Это намного дешевле и позволяет охватить максимальное количество претендентов.
Немного отступлю от темы, сам я программировал последний раз очень давно, и вообще больше на С#, а последний глобальный проект на микроконтроллере (PIC16 на зыке Си) был сделан в далеком 2007 году.
Поэтому мне предстояло разобраться с современными микроконроллерами, языком С++ и операционной системой реального времени.
Конечно все наши проекты уже сейчас используют ОСРВ и пишутся на С++, но как разработчик я в них не учувствую, а занимаюсь тунеядством управлением проектами разработки такого ПО.

Выбор

Времени у меня на все про все было дано 1 месяц. С начала июня 2015 до начала июля 2015, потому что потом я собирался в отпуск, а после отпуска обычно полно работы. Надо было делать все быстро и четко.
Немного проконсультировавшись с коллегами, выяснил, что модное направление ARM Cortex различные ядра и из доступных отладочных плат можно заказать Olimex STM32P152 которые стоили 25 долларов. Они пришли очень быстро — 6 плат по цене примерно 2000 рублей. Стоит заметить, что эти платы были закуплены нами для университета, где собственно и будет проходить этот курс.
image

Исходные данные: Цель

Первым делом, нужно было определиться с тем, какую информацию и что давать студентам. Для себя я решил, что главной целью будет — показать весь процесс разработки ПО для устройств с использованием микропроцессора STM32, языка программирования С++ и операционной системой реального времени FreeRTOS, начиная от требований и разработки архитектуры и заканчивая кодированием.

Исходные данные: Ограничения

Итак задачей нашего проекта является создание демо приложения для отладочной платы Olimex STM32P152, по стандартам кодирования (который я тут приводить не буду), написанным на языке С++ с использование FreeRTOS.
Приложение должно быть понятным, простым и ненавязчивым, без заумных конструкций присущих языку С++. Архитектура должна быть описана на языке UML.

Исходные данные: Функциональные требования

SR0: Устройство должно измерять три параметра (иметь три переменных): Температуру микропроцессора, Напряжение VDDA, Напряжение с переменного резистора
SR1: Устройство должно выводить значение этих переменных на индикатор.
SR2: Единицы измерения для Температуры микропроцессора — градусы Цельсия, для остальных параметров — вольты.
SR3: При нажатии на кнопку 1, на индикаторе должен показываться экран со следующей измеряемой переменной,
SR4: При нажатии на кнопку 1 Светодиод 1 должен изменять свое состояние
SR5: При нажатии на кнопку 2, на индикаторе должен поменяться режим отображения переменных с постоянного показывания переменной на последовательное (менять экраны раз в 1.5 секунды) при следующем нажатии с последовательного на постоянное,
SR6: При нажатии на кнопку 2 светодиод 2 должен менять свое состояние.
SR7: Светодиод 3 должен моргать раз в 1 секунду.

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

Разработка: начало работы

И так требования готовы, можно приступать. Начнем с инфраструктуры. Для начала я создал проект в IAR для C++, ничего нового тут нет. Вот эта статья все описывает Создание проекта на C++ в IAR для STM32. Здесь останавливаться не будем.

Разработка: обертка для FreeRTOS

Любой объект, который хочет быть задачей (активным) должен наследовать этот интерфейс и реализовывать метод run(). Указатель на этот объект передается в функцию run() обертки в качестве параметра.
На бумаге это выглядело это так:
image
Задачу реализации этой картины я поручил одаренному молодому специалисту, который успешно справился с ней и через пару дней выдал вот такой пустой рабочий проект в IAR 6.50
Пустой проект с оберткой FreeRTOS в IAR 6.50
Улучшенную версию обертки вы можете взять из этой статьи С++ обертка для «всех» Операционных Систем Реального Времени для Cortex M4

Разработка: Общая архитектура

Пока молодой специалист делал обертку, я прикидывал архитектуру ПО. Для себя я выделил 3 пакета:
AHardware — пакет содержащий классы для работы и управления аппаратурой (Светодиоды, Индикатор, АЦП и так далее)
Application — пакет содержащий классы прикладного уровня и по задумке ничего не знающий про железо, поэтому может быть портрирован на любой микроконтроллер без изменения, ну при условии, что обертка разработана не человеком снежинкой. А в данном случае это не так:)
image
FreeRTOS — пакет с портированной операционкой и оберткой для неё.
Немного порисовав получилась следующая картина:
image

Разработка: Моргание светодиодом

Ну что же железо настроено и я опять сел за рисование, на этот раз — класса управления светодидами. Через пол часа получилось вот это:
image
И тут же реализация сего чуда:

Теперь снова нужно немного порисовать, чтобы сделать класс управления логикой работы светодиодов — класса cLedsDirector. Это будет активный класс, т.е. его функция run(), будет запускаться в задаче. Как я уже писал выше, все мои активные классы должны наследовать интерфейс iActiveObject. Поэтому рисунок выглядит тоже не сложно.
image
Опять же реализация тоже простая и по силам человеке снежинке, вроде меня:

Запускаем на плате, и о чудо — работает с первого раза. Значит все сделал правильно. Есть реализация первого требования. Будем идти дальше. А пока сохраним проект
Проект морганием светодиодом в IAR 6.50

Разработка: Кнопки

Светодиоды работают, теперь можно реализовать требования SR6: При нажатии на кнопку 2 светодиод 2 должен менять свое состояние и SR4: При нажатии на кнопку 1 светодиод 1 должен менять свое состояние.
Кнопки я решил делать без прерываний, да и вообще прерывания в этом проекте использовать не буду, хотя ничто не запрещает использовать прерывания, но просто я так решил.
Согласно процедуре рисуем класс cButtonsDriver.
image
Тут надо сказать одну вещь, что с первого раза у меня кнопки не заработали, точнее заработала только одна. Вторая не работала. Разобравшись, я понял, что одна кнопка подтянута к нулю, а вторая к единице. Поэтому нажатие определяется у них по разному. Для определения нажатия я и ввел дополнительный атрибут -buttonsTrigger (которого изначально в архитектуре не было). Он показывает по какому значению считается нажата кнопка по 0 или по 1. И вот после этого, все стало работать как часы.
Реализация очень простая.

Ну что же с драйвером покончено, теперь надо подумать про задачу, которая будет переодически опрашивать кнопки.
Картинка выглядит очень похожей на активный класс для светодиодов, оно и верно, чего изобретать велосипед:
image

Нажатие на кнопку должно каким-то образом оповещать те задачи, которым нужно об этом знать. Можно сделать это несколькими способами: Через события, либо использовать очередь, либо воспользоваться последним достоянием freeRTOS нотификацией (Notify).
Проанализируем требования. По нажатию кнопки у нас должно осуществляться две вещи: Во-первых, менять свои состояния светодиоды 1 и 2, а во-вторых, на индикаторе должен меняться режим вывода и экраны. Проблема заключается в том, что очереди и события могут быть приняты только одной задачей, после чего вторая об этом не узнает, и тогда архитектура будет усложнена. Придется переадресовывать события, скажем что задача Светодиодов должна будет переадресовывать событие или очередь от кнопок задаче Индикатора. Что-то в этом меня не устроило, и я решил сделать нотификацию конктретной задаче. Т.е наша задача опроса кнопок после определения нажатия будет нотифицировать только те задачи, которым эти события нужны.
Для этого и был заведен массив указателей pTaskToNotify на задачи, которые необходимо оповещать.
Теперь реализация:

Запускаем, проверяем и все работает — да просто удивительно 🙂 Вот что значит вначале порисовать.
Как обычно сохраняем проект:
Проект Кнопки и светодиоды для IAR 6.50
Улучшенный проект с примером новой обертки можно взять здесь:
Проект на IAR 8.30
А проект стал выглдятеть вот так:
image

Итак, за каких-то три дня я реализовал три требования — не плохая скорость, учитывая, что всего у меня их 7. И решив, что за оставшиеся 2.5 недели я то точно все сделаю, отправился на два дня на озеро.
Как потом оказалось, зря я так оптимистически смотрел на вещи, но об этом уже во второй части.


Источник: habr.com