Комментарии 38
Но если необходимо сконфигурировать несколько пинов, а тем более динамически менять конфигурацию <...> то гораздо проще обернуть всю работу с регистрами в класс, и пользоваться методами типа setPin/resetPin.Было бы не плохо продемонстрировать удобство вашего способа. Ну, скажем, вывод на семисегментник, у которого сегменты разбросаны по разным портам в произвольном порядке.
На макросах я могу сделать так:
#define LCD_44780_DATA C, 6 //PC6, PC7, PC8, PC9
#define LCD_44780_RS B, 15
#define LCD_44780_E A, 8
#include "lcd_44780.h"
Да, в библиотеке будет немного шаманства чтобы развернуть 4 байта из структуры, предназначенной для хранения одного. Но это делается один раз. А подключается она потом к произвольным выводам, как видите, просто.
Я конце 90-ых, имея под рукой IBM AT-286 с EGA-адаптером, для достижения скорострельности полез в системное программирование и стал управлять буфером кадров напрямую. Так там порядок байт бит-масок пикселей в буфере отличался от процессорного. К тому же, один байт — горизонтальная строка, длиной с ширину символьного сегмента (8x16: 80x25 для VGA и 8x14: 80x25 для EGA). У EGA-режима памяти хватало на два независимых кадра, а VGA — на 1,5 — соответственно один постоянно видимый.
Для отрисовки примитивов (это ещё никакого антиалиасинга) приходилось пересчитывать сдвиги битовых масок и накладывать их через смещения по коэффициенту. Но и то API выглядел примерно так:
graph->drowPix(x, y);
graph->drowLine(x1, y1, x2, y2);
graph->drowSquare(x1, y1, r);
И т.д.
Ни разу не оптимально, но зато удобно и достаточно быстро. А уже после идут всяческие оптимизации частных случаев, если нужна скорость.
Но то время ушло. Теперь даже жалкая AtMega тактуется выше, чем тогда процессор (8-12 МГц было). Теперь стало глупо заниматься побайтовой оптимизацией, хотя искренне жаль…
То есть внутри того же «lcd_44780.h» имеет смысл реализовать стандартные функции вывода текста (графику-то он не поддерживает). С этим никто не спорит. Но при разводке платы бывает удобно поменять порты, к которым он подключен. Не лезть же каждый раз внутрь библиотеки! И абстракции вокруг портов должны решать именно эту задачу, а вовсе не лезть во внутренности библиотек.
Вся ветка обсуждения тянется от этого поста.
опять же, как часто Вам приходилось в устройстве с МК менять настройки пинов динамически? Я даже сходу пример-то не могу такой придумать. Максимум что приходилось делать, это переводить в состояние, при котором потребление будет минимальным. Опять же, однократно перед сном. Получается что динамика в данном случае избыточна. Шаблоны тема сложная, но интересная.
Вот собственно и все. Думаю в таком виде работать с портами ввода-вывода гораздо удобнее, чем напрямую с регистрами.
Относительно. Когда говорят «пишу на регистрах» не стоит считать что по всему коду разбросы строки вида:
GPIOC->BSRR = 1 << pin;
Это все так же оборачивается в функции. Поэтому и спорное утверждение об удобстве.
Мой вариант, правда для nrf52840, но суть та же. Настройка пина. При конфигурации происходит сборка по ИЛИ допустимых настроек.
Gpio<port1, 3> enable_;
enable_.configurate(output | noPull);
enable_.set(); // разрешаем питание на драйвер светодиода
вот что порадовало, так это использование регистров «BRR» и «BSRR».
абстрагируемся от регистров CMSIS приПогодите, я правильно понял? Вы уже имеющийся слой абстракции (CMSIS) обернули еще одним слоем абстракции (вашим классом)?
Наверное не совру, что каждый, кто пишет прошивки, так же пишет небольшие «абстракции», чтобы сделать гибкую настройку некоторых пинов, их прерываний и т.д., чтобы далее, в процессе добавления в код прошивки нужных фич, не терять время на переконфигурирование пинов/таймеров/ADC/DMA и т.д. Для каждого проекта, эти «абстракции» свои, своего рода маленький «велосипед». Если этот маленький «велосипед» для текущего проекта выполняет свои задачи, то ок.
У вас «велосипед» работает — это хорошо, но, в других проектах он будет не нужен (ИМХО).
У меня тоже есть свои «велосипеды», можете посмотреть в коде на гите (не по STM).
Я к чему, таких статей можно нагенерить множество (по каждому проекту, свои «велосипеды»), и, кстати ИМХО — это будет своего рода база знаний, что можно сделать при написании кода.
Я понимаю, что выражение «велосипед» выглядит в данном контексте не очень, хотел писать «фича», но решил оставить так.
Автору: плюс в карму, ноль за статью.
Я к чему, таких статей можно нагенерить множество (по каждому проекту, свои «велосипеды»), и, кстати ИМХО — это будет своего рода база знаний, что можно сделать при написании кода.
Идея интересная. Задумался над тем, чтобы показать свой «шаблонный» велосипед над freeRTOS и стеком BLE. Но терзают определенные сомнения ибо «вроде работает, если вот и тут не трогать» )
Для каждого проекта, эти «абстракции» свои, своего рода маленький «велосипед». Если этот маленький «велосипед» для текущего проекта выполняет свои задачи, то ок.
Да, но у меня достаточно часто при переносе велосипеда из проекта в проект он обрастает новыми деталями. Потому что предусмотреть все и сразу не возможно. Обязательно что-нибудь не обычное вылезет.
Интересные статьи — про нетрадиционные методы «шоколадного обертывания», вроде тех, что пишет lamerok, либо напротив — критику этих методов (которая неоднократно была к комментариях, но так и не оформилась в виде статьи). А как в 100500-й раз обернуть что-то в класс…
Потому что обернуть можно тысячей разных способов.
Согласен, но не все одинаково удобны. у меня аллергия на динамическую работу с памятью в МК, поэтому делаю все по максимуму в статике. Счастье было, когда удалось статический полиморфизм (CRTP) реализовать у себя. Ну по крайней мере мне так кажется что удалось)
Есесна запрет на освобождение рождает много демонов — например работа со строками становится «как в Си» (что не есть плохо, просто не идиоматично). Но что поделать.
Heap Size = 0;
Main Stack Size = 2048;
Process Stack Size = 0;
Linker = SEGGER;
Ну вот например, тренировался на этом шаблоне:
class Singleton {
public:
static Singleton* instance() { return inst_; }
void run() { }
private:
Singleton() {}
static Singleton* inst_;
};
Singleton* Singleton::inst_ = new Singleton();
Расход памяти можно посмотреть тут:
То есть формально куча не создана, но указатель на нее у segger'а есть. Они относительно недавно, в этом году, меняли и линкера, и компилятора своего. Из-за этого перестали собираться проекты, созданные в ранних версиях. После выхода патча стало легче, но старые проекты восстанавливать все равно пришлось
Разве Segger виноват в том, что программист задает нулевой размер кучи а потом создает экземпляр класса при помощи new, причем там, где это совершенно не нужно? Классический ленивый Singleton может так выглядеть:
class Singleton
{
public:
static Singleton& instance()
{
static Singleton inst;
return inst;
}
...
};
Разве Segger виноват в том, что программист задает нулевой размер кучи а потом создает экземпляр класса при помощи new, причем там, где это совершенно не нужно?
проблема в другом. Программист явно указал не использовать кучу, но зачем-то решил это делать (забыл например или не знал что нельзя использовать кучу). Segger в данном случае создал кучу сам в некой области памяти. По map файлу это видно было, но нет данных о размере этой кучи.
Но если бы он дал по рукам за такое, сказав что не будет компилировать с динамической памятью при отсутствии явного выделения кучи, цены б ему не было.
Классический ленивый Singleton может так выглядеть
есть много разных вариантов, тот что в моем комментарии выше приведен для примера кода. При желании можно даже такое написать:
template <typename T>
class Singleton {
public:
static constexpr T& instance() {
return inst_;
}
const Singleton<T>& operator=(const Singleton<T>&) = delete;
private:
inline static T inst_;
};
Настройка одного пина:
GPIO_INIT_PIN(GPIOA,3,GPIO_MODE_INPUT_PULL_DOWN);
Настройка нескольких пинов:
GPIO_MULTI_INIT(GPIOA,BIN(1010101010101010),GPIO_MODE_OUTPUT50_OPEN_DRAIN_DOWN);
Каждый раз надеюсь, что тут что-то новое и каждый раз одно и то же.
Очередной джедай видит как бедные baremetal'исты ковыряются в своих регистрах и решает внести свои собственные абстракции. Тычет классами в несчастные регистры, моргает обернутым, как шаверма на вокзале, пином со светодиодом и уже готовит следующую статью с шаблонами для разных датчиков.
Писанина ради писанины.
Подробного описания периферии, принципов ее работы, эмуляцию и запуск на аппаратном модуле.
Красивого законченного устройства.
Полноценной библиотеки, которая бы действительно делала взаимодействие с контроллером более удобным.
Товарищ COKPOWEHEU прав. Мне ещё было бы интересно посмотреть на красивое решение на тех же плюсах какой-нибудь нудной проблемы(или проекта), которое добавило бы в копилку "за высокие абстракции".
Подробного описания периферии, принципов ее работы, эмуляцию и запуск на аппаратном модуле.
Могу рассказать про свой опыт работы с nrf52840. Достаточно интересная периферия с возможностью каскадирования внутри чипа через модуль PPI (Programmable peripheral interconnect — программируемое подключение периферии). Вроде занятная штука, но интересно ли будет?
STM32 абстрагируемся от регистров CMSIS при настройке GPIO