Вадим Дерябкин @Vadimatorikda
Инженер-программист
Information
- Rating
- Does not participate
- Location
- Красноярск, Красноярский край, Россия
- Date of birth
- Registered
- Activity
Specialization
Software Developer, Embedded Software Engineer
Lead
From 250,000 ₽
C++
STM32
Linux
Circuitry
Python
Assembler
Programming microcontrollers
Embedded system
Software development
Object-oriented design
А если серьезно, то правильно настроив NVIC — можно получить такой код, когда такой проблемы не возникает.
Насколько я знаю, еще есть средства самого языка для атомарного доступа. Но ими я не пользовался, увы. Не было необходимости. Так что буду знать, если знатоки напомнят. std::...;
Иногда приходилось применять «костыль» с блокировкой всех прерываний и последующим возобновлением. Так как это дело пары команд ассемблера, то ничего ИМХО, страшного.
Опять же ИМХО, но для себя выбрал путь «Си для драйверов + Lua для логики». Поскольку в моей практике редко встречаются устройства с «золотой серединой». Это либо совсем уж «преобразователи протоколов» (там нет никакой Lua. Драйвера и пара функций). Либо уж совсем монстры (GUI, логика переходов сравнимая с игровыми ботами...). Но к Rust присматриваюсь чисто в академических целях. Может в будущем займусь всерьез.
По поводу реализации. Есть у меня проект-песочница. Там я делал нечто подобное. Когда только отрабатывал все эти штуки:
Нет. Не в конструкторе. А в методе init. То есть, в реальном времени. Это нужно для того, повторюсь, чтобы после всех манипуляций по ходу работы с структурой HAL-а иметь возможность восстановить ее в исходное состояние.
И вопросы «А почему он вдруг не вмещается в МК?! Я же только вывести хотел!». А то что stdout отжирает 300кб в мк с 128 кб flash — его не беспокоило.
Или snprintf внутри прерывания в буфер в 2 кб при стеке под прерывания в 1 кб…
Идея звучала интересно. Но разбилась о практику. Такие дела.
Я не соглашусь с использованием HAL-а даже. Он дико избыточен. Инициализировать UART у меня занимает 5 строк кода (запись в регистры). А вот HAL просит структуру какую-то. Ведет свои состояния. Логику и прочее. Это ведь тоже по сути объектно-ореинтированный подход. Что уже по сути своей для вывода избыточно. Причем, еще и игнорируется аппаратная часть. Чтобы записать «1» в вывод — не нужно считывать регистр, накладывать маску и записывать обратно. Нужно просто знать про бит-ендинг. И записать число по нужному адресу. Абсолютная атомарность.
Для того, чтобы не передавать в конструктор класса 8 параметров. Вместо этого указатель на структуру с этими параметрами.
Ну вот, например, объект UART хочет себе при инициализации объект класса Pin для управления CS (если не используется аппаратный по какой-то причине. Например, если в МК один SPI на кучу устройств).
Для объекта, который управляет всеми выводами разом одной командой.
Тут приведена «жизнь обычного вывода». Есть структура с описанием его конфигурации (для удобства, конфигурации описываются отдельными определениями в .h файле для каждого пина, а потом этот define используется для инициализации полей структур). Эта структура «скармливается» глобальному объекту пина в качестве параметра инициализации. Далее указатель на этот объект становится частью глобального массива на объекты используемых пинов. Этот массив является структурой инициализации для объекта GlobalProt, через который производится взаимодействие сразу со всеми выводами (инициализировать все, сбросить конфигурацию, изменить параметры порта целиком и прочее). Указатель на этот объект является частью структуры инициализации bsp контроллера (board support package), который управляет всей периферией и дает высокоуровневый интерфейс для взаимодействия с периферией (на уровне протоколов и нематериальных объектов (не таймер, работающий в режиме pwm,, а «подсветка с яркостью в диапазоне от А до Б»). После чего, для использования аппаратки из кода логики, указатель на объект bsp дается уже контроллеру приложения, в котором инкапсулирована бизнес-логика приложения.
Тогда нарушается принцип «один объект — одна область ответственности». А это уже не хорошо. Потому что никто не ждет от «функции с именем print запроса данных от пользователя». Я помню, как у меня горело, когда я обновил HAL, а там функция изменения частоты ядра (HAL_RCC..._Config) еще и инициализировала systic, что приводило к тому, что у меня прерывание срабатывало раньше, чем пройдет инициализация всех сущностей проекта и запустится планировщик FreeRTOS.