Современный C++ для микроконтроллеров
Тема дорожной карты · Embedded / Системный программист
Миф о том, что C++ «тяжёлый» и не годится для контроллера с 32 КБ флеша, давно опровергнут: компилятор один и тот же, а большинство возможностей языка стоят ноль байт в рантайме. Современный C++ на микроконтроллере — это не классы с виртуальными функциями повсюду, а constexpr, строгие перечисления, шаблоны для доступа к регистрам и статическое выделение памяти. Такой код читается лучше макросного C и ловит больше ошибок ещё до прошивки платы.
Как это работает
Флаги -fno-exceptions -fno-rtti отключают две подсистемы, которые действительно раздувают бинарник: таблицы раскрутки стека и информацию о типах. Всё остальное остаётся. constexpr-функции вычисляют делители тактирования, таблицы CRC и коэффициенты фильтров на этапе компиляции, и в прошивку попадают готовые константы. enum class не даёт случайно передать номер пина туда, где ожидался режим таймера, а static_assert проверяет размеры структур, наложенных на регистры.
Шаблонный класс Register<Address, Mask> с методами set(), clear(), read() после инлайнинга превращается в те же три инструкции, что и ручная запись через указатель, но исключает опечатку в адресе. std::array заменяет сырые массивы и знает свой размер, std::span передаёт буфер вместе с длиной без копирования. Динамическая память запрещена или ограничена: объекты создаются статически, а если нужен пул, применяют placement new в заранее выделенном буфере. Библиотека ETL (Embedded Template Library) предоставляет контейнеры с фиксированной ёмкостью — etl::vector<T, N>, etl::string<N>, etl::queue — которые повторяют интерфейс STL, но никогда не обращаются к куче.
Когда применять
C++ оправдан на любом 32-битном контроллере — Cortex-M, RISC-V, ESP32 — где есть современный GCC или Clang и хотя бы десятки килобайт флеша. На 8-битных AVR он тоже работает, но выигрыш меньше из-за ограничений компилятора. Переход особенно окупается в проектах с несколькими вариантами платы: шаблоны и constexpr-конфигурация заменяют лес #ifdef. Если проект должен проходить сертификацию по MISRA или требует вендорского C-компилятора, C++ приходится согласовывать с заказчиком заранее.
Типичные ошибки
Первая ошибка — тянуть <iostream> или std::string в прошивку: одна такая строка добавляет десятки килобайт и скрытые вызовы malloc. Вторая — использовать std::vector и std::map, полагая, что «памяти хватит»; фрагментация кучи проявится через недели работы устройства. Третья — забыть, что глобальные объекты с конструкторами инициализируются startup-кодом до main(), и обращаться к периферии, которая ещё не тактируется. Четвёртая — виртуальные функции в обработчиках прерываний: сами по себе они дёшевы, но мешают компилятору инлайнить критичный код. Наконец, включив -fno-exceptions, нужно предусмотреть, как ETL и собственный код сообщают об ошибках: через коды возврата, etl::expected или пользовательский обработчик.
Связанные понятия
Полезные ресурсы
🔤 Английские термины в этой теме
Слова из этой темы, которые русскоязычные часто произносят неверно. Кнопка 🔊 произносит голосом браузера.
- interrupt /ˌɪntəˈrʌpt/ ударение на последний слог разбор: Фразы митингов: перебить вежливо, уточнить, подвести итог
- register /ˈrɛdʒɪstər/ стилистический регистр разбор: Письменная коммуникация
- interface /ˈɪntərfeɪs/ ударение на первый слог разбор: Немые буквы и ударение: cache, queue, suite, height