Шаблоны C++ для микроконтроллеров
Писать на хабр я не умею и боюсь, а Пикабу - это развлекательно-познавательный портал, так что начну с него:)
В 2019 году в качестве хобби решил попробовать микроконтроллеры, чтение форумов и тематических ресурсов определили, что за основу возьму STM32. Arduino за единицу ресурсов дороже, и в принципе на нее есть всё, что только можно придумать, конкретных задач и проектов у меня не было, поэтому с Arduino делать мне было, по сути, нечего.
Как и многие, первые программы создавались через CubeMX (ныне CubeIDE), светодиодами моргать научился, по UART с компьютером связался, даже USB-HID из примера сделать получилось. Но снова уперся в тот факт, что нет конкретной задачи, так что начал разбираться в вопросе непосредственно программирования: какие есть фреймворки, почему HAL так не любят и т.д. В процессе чтения статей наткнулся на очень интересный подход, связанный с применением шаблонов языка C++. Вот ссылка на оригинальную статью, очень рекомендую к прочтению http://easyelectronics.ru/rabota-s-portami-vvoda-vyvoda-mikr....
Если вкратце: автор предлагает почти объектно-ориентированный подход, только все зависимости являются не полями класса, а его шаблонными параметрами. Таким образом, нет необходимости расходовать оперативную память, передавать указатель this в методы.
Например, класс-драйвер дисплея LCD1602 требует указания ему шести ножек GPIO + ширину и длину. Тогда соответствующий шаблонный класс определяется следующим образом
Создание "объектов" (в кавычках) заменяется объявлением нового типа данных:
Все методы классов при шаблонном подходе являются статическими, соответственно вызов осуществляется через оператор разрешения контекста:
Итого, основным отличием от классического объектно-ориентированного подхода является замена создания объекта класса объявлением нового типа (путем инстанцирования некоторого шаблона). Недостатком является тот факт, что каждое такое объявление порождает новый тип данных, все методы дублируются. Однако при программировании микроконтроллеров это несущественно, поскольку вся количество таких типов ограничено количеством используемой периферии (и внешних устройств), а большая часть методов - это запись значения в регистр. Поэтому при включенной оптимизации вызов методов класса вообще сворачивается в inline, что повышает быстродействие. Также использование шаблонов дает возможность существенно ускорить инициализацию периферии путем передачи параметров в качестве шаблонных (если она известна заранее, что весьма логично). В таком случае значения регистров становятся известны еще на этапе компиляции, так что в прошивке можно обнаружить просто запись соответствующего значения в регистр.
Автор указанной статьи разработал и поддерживает библиотеку "mcucpp" (github.com/KonstantinChizhov/Mcucpp), однако ее сложно использовать "из коробки". Сам подход мне очень понравился, поэтому я решил попытаться "прибраться" в том, что сделал и делает Константин, применить нововведения стандарта c++ 17 (а их много как раз в части метапрограммирования), написать документацию и примеры, которые снизят порог вхождения. В результате медленно, но развивается проект Zhele (github.com/azhel12/Zhele), где я неторопливо собираю великолепные идеи Константина Чижова (автором перенятого кода указываю его, если вдруг у кого-то появится вопрос), пытаюсь покрыть различные МК (сейчас у меня в арсенале f030f4, f072rbt, f103c8, f401cc), написать Doxy-документацию, написать примеры и проверить их.
Если вдруг у кого-то будут силы и желание посмотреть примеры и выразить замечания и предложения, буду рад. Пока энтузиазм, к счастью, не пропал. На работе преподаю помимо прочего C++, так что развитие проекта для меня - это в первую очередь упражнения в новых фишках языка.
В планах дописать классы основных интерфейсов (хотя бы i2c, который меня пока пугает), начать реализовывать драйверы популярных устройств (возможно, некоторым студентам будет интересно попробовать). Также очень хочу разобраться в языке шаблонов CubeMX (CubeIDE), чтобы из куба генерировался проект сразу на разрабатываемой библиотеке.
UPD: нашел результаты эксперимента годичной давности (с тех пор я что-то все-таки улучшил + новые версии компиляторов, возможно, еще сильнее оптимизируют). На разных фреймворках сделал одну и ту же задачу для Stm32F103: сконфигурировать тактивание (на частоту 72 Мгц), настроить выход GPIO для управления светодиодом, настроить таймер на ежесекундное прерывание, в обработчике прерывания от таймера переключить светодиод. Допускаю, что написал не идеальные решения, но задача простая, что позволяет предполагать небольшое влияние этого факта.