Arlleex 190 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба Можно ли какими-то средствами задать тип-подмножество целых, только не обзывать эти подмножества именами? Т.е. я хочу что-то типа такого enum class ePin : u32 { 0, 1, 2, 3, 4, 5, 6, 7 }; ... void selectPin(ePin pin); ... void main() { ... selectPin(ePin::7); // ok selectPin(ePin::8); // compilation error } Т.е. не создавая всяких _0 = 0, _1, _2... внутри enum-а (дублирование нижних подчеркиваний просто замусоривает одни и те же смысловые конструкции). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 13 minutes ago, Arlleex said: только не обзывать эти подмножества именами? А почему нельзя обзывать именами? Почему принципиально цифры? Ведь в том же даташите пины называются как раз именами: PA7, PB3. Так использую у себя, очень удобно и главное - наглядно. К примеру, вот как у себя делаю: Spoiler #include <Pin.hpp> ... stm32f4::PortCAN1 port; stm32f4::Pin<PB9> pinCAN_TX; stm32f4::Pin<PB8> pinCAN_RX; .... port.initialize(); port.setPinTx(pinCAN_TX); port.setPinRx(pinCAN_RX); ... port.run(); ..... Pin.hpp: using PinName = enum { PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15, // 0 PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15, // 1 PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15, // 2 ..... }; Нет привязки к порту, все пины легком меняются на другие (конечно, в рамках аппаратной привязки). Все разовые "расчеты" спрятаны внутри Pin.cpp. Да, есть некоторый оверхед по ОЗУ, но мне на это плевать - его всегда с избытком ) 13 minutes ago, Arlleex said: нижние подчеркивания просто выглядят они контекстно ужасно Это нарушение нужно вписать в КОАпп, как злостное )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба У меня возникло желание при инстанцировании объекта класса UARTDMARx задавать ему все параметры, которые могут быть мультиплексированы разными способами. И поэтому проще сообщить классу отдельно номер GPIO (A, B, C...), номер пина (0, 1, 2...), номер UART (1, 2, ..., 6) и т.д. Потому что из некого PA3 не сразу очевидно как выцыганить информацию о том, каким битом и в каком регистре разрешить тактирование GPIO, в каких полях модифицировать настройки самого пина в регистрах GPIO, и какой номер альтернативной функции задается для этого пина (все они слишком переплетены в возможных конфигурациях). Я захотел, в конечном счете, создать некую таблицу PinMux, из которой по индексам указанных отдельно (при инстанцировании объекта) GPIO, Pin ... одним и тем же кодом будет обрабатываться инициализация нужной инфраструктуры периферии. P.S. Подумал, что проще в конструкторе объекта задать всего лишь 1 параметр: общую настройку. Эта общая настройка есть элемент из enum-а, который перебирает все возможные комбинации мультиплексирования. И уже в реализации конструктора использовать этот enum как индекс массива, где можно каждой настройке сопоставить список всех нужных зависимостей (типа, какой GPIO/UART/DMA активировать в RCC, и т.д.). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 17 minutes ago, Arlleex said: У меня возникло желание при инстанцировании объекта класса UARTDMARx задавать ему все параметры, которые могут быть мультиплексированы разными способами. И поэтому проще сообщить классу отдельно номер GPIO (A, B, C...), номер пина (0, 1, 2...), номер UART (1, 2, ..., 6) и т.д. А зачем "мультиплексировать"? Что это дает в юзер-коде? Quote Потому что из некого PA3 не сразу очевидно как выцыганить информацию о том, каким битом и в каком регистре разрешить тактирование GPIO, в каких полях модифицировать настройки самого пина в регистрах GPIO, и какой номер альтернативной функции задается для этого пина (все они слишком переплетены в возможных конфигурациях). Никаких проблем Предусмотрен базовый класс AbstractPin (в Pin.cpp), который имеет все базовые возможности: тактирование, сброс, управление побитовое, альтернативные возможности, инициализация режима и т.п. В конструкторе базового класса происходит вычисление номера порта и номера пина, тут же создаются битовые маски для более быстрой работы с пином в режиме ногодрыга. Повторюсь - есть овердех по ОЗУ, иначе пины будут "тормозные" ): Spoiler Pin.cpp: stm32f4::AbstractPin::AbstractPin(PinName pin) { // Undefined pin if (pin == NONE) { port = reinterpret_cast<Port*>(GPIOA_BASE); setMask = 0; resetMask = 0; return; } pinIndex = (pin % 16); setMask = (1 << pinIndex); resetMask = (1 << (pinIndex + 16)); switch (pin / 16) { case (0): port = reinterpret_cast<Port*>(GPIOA_BASE); SET_BIT(RCC->AHB1LPENR, RCC_AHB1LPENR_GPIOALPEN); SET_BIT(RCC->AHB1ENR, RCC_AHB1ENR_GPIOAEN); break; Quote Я захотел, в конечном счете, создать некую таблицу PinMux, из которой по индексам указанных отдельно (при инстанцировании объекта) GPIO, Pin ... одним и тем же кодом будет обрабатываться инициализация нужной инфраструктуры периферии. По-моему вы слишком усложняете ту часть, которая по сути в коде выполняется лишь при старте :) В конце концов, это всего-лишь пины Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 37 минут назад, Arlleex сказал: Потому что из некого PA3 не сразу очевидно как выцыганить информацию о том, каким битом и в каком регистре разрешить тактирование GPIO, в каких полях модифицировать настройки самого пина в регистрах GPIO, и какой номер альтернативной функции задается для этого пина (все они слишком переплетены в возможных конфигурациях). Я захотел, в конечном счете, создать некую таблицу PinMux, из которой по индексам указанных отдельно (при инстанцировании объекта) GPIO, Pin ... одним и тем же кодом будет обрабатываться инициализация нужной инфраструктуры периферии. Делаю всё это посредством макросов. С помощью макросов можно получить все эти данные с минимальными runtime затратами. Причём так у меня сделано для: NXP, STM32, INFINEON, TIVA, ... - везде один и тот же механизм, разные таблицы и макросы только. Без всяких классов, конструкторов и т.п. 20 минут назад, Forger сказал: По-моему вы слишком усложняете ту часть, которая по сути в коде выполняется лишь при старте :) Не надо говорить за всех. У меня например в процессе работы, различная периферия может инициализироваться/деинициализироваться и при этом соответственно перепрограммируются режимы пинов (GPIO <-> альтернативная). И так делаю всегда: если какая-то периферия может быть включена или отключена, соответственно пин переводится из режима GPIO в альтернативную функцию после инита периферии и переводится в режим GPIO перед деинитом периферии. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 34 минуты назад, Forger сказал: А зачем "мультиплексировать"? Что это дает в юзер-коде? Просто я хочу в конечном итоге с помощью лишь одного селектора уметь "перепривязываться" к разным UART-ам на разных пинах. Т.е. идеально, когда реализация сама распидаливает все эти хитрож*пые зависимости с тактированиями UART-ов, DMA и т.д. Ведь, например, в моем STM32F4 разрешение peripheral clocks разных UART-ов осуществляется в разных регистрах (APB1ENR, APB2ENR), DMA1/2 обслуживают свои наборы UART-ов (т.е. на основе номера выбранного UART-модуля класс сам должен автоматически включить тактирование DMA1/2 в RCC), и много много другого такого барахла, когда "если так, то регистр такой-то бит такой-то, а DMA такой-то, а если этак - то вот так...". Вот я и хотел сначала передавать в конструктор всю эту муть, чтобы на ее основе "напрямую" инициализировалось то, что нужно. Но вопрос, в целом, изначально был несколько в другом: я хотел ради синтаксического кайфа задавать какую-либо опцию просто номером, а не именем. Но при этом чтобы вводимый номер в compile-time по-возможности проверялся на допустимость: функциональность enum class здесь была бы просто идеальная, за исключением того, что элементы этого enum-а нужно обзывать. А обзывать числа, которые по смыслу должны быть числами (номерами) - какой толк? И синтаксически красивее было бы ePin::0 вместо ePin::PIN1 или ePin::_1. 39 минут назад, jcxz сказал: Делаю всё это посредством макросов... Угу... Макросы штука мощная, бесспорно. Но порой не шибко гибкая, ИМХО. Собственно, сейчас мысль сделать что-то типа такого // .hpp class cUARTDMARx { enum class eHW { UART1_PA10 = 0, UART2_PA3, ... }; }; // .cpp const struct { ... }HWCfgTbl[] = { [UART1_PA10] = {RCC->APB2ENR, RCC_APB2ENR_USART1, RCC_AHB1ENR_DMA1, GPIO_PIN_10, ...} }; ... UARTDMARx fifo(cUARTDMARx::eHW::UART2_PA3); // создает RX FIFO UART2, ножка RX на PA3 Грубо говоря, на основе параметра, передаваемого в конструктор, из таблицы берется нужный набор данных и участвует в инициализации всего и вся. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 36 minutes ago, Arlleex said: Т.е. идеально, когда реализация сама распидаливает все эти хитрож*пые зависимости с тактированиями UART-ов, DMA и т.д. Вы наверняка понимаете, что это - утопия, в смысле автоматом переназначает все внутри. Даже, если удасться что-то подобное реализовать, то для этого придется перекурить весь даташит на КАЖДЫЙ камень, и обязательно будет крайне монстроподобная и забагованная конструкция. Шаг влево и карточный домик рассыпется. Стоят ли такие титанические труды ради разовой инициализации, которую для каждого проекта можно реализовать за день работы с перекурами? Вопрос риторический ) Для себя я давно решил, что наглядно инициализация железа в каждом проекте гораздо полезнее для самого проекта, чем попытки все это скрыть где-то в недрах некой жирнющей самопальной либы ) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 37 минут назад, Arlleex сказал: Угу... Макросы штука мощная, бесспорно. Но порой не шибко гибкая, ИМХО. А в чём именно "негибкость"? Чего они не позволяют? 38 минут назад, Arlleex сказал: когда реализация сама распидаливает все эти хитрож*пые зависимости с тактированиями UART-ов, DMA и т.д. Ведь, например, в моем STM32F4 разрешение peripheral clocks разных UART-ов осуществляется в разных регистрах (APB1ENR, APB2ENR), DMA1/2 обслуживают свои наборы UART-ов (т.е. на основе номера выбранного UART-модуля класс сам должен автоматически включить тактирование DMA1/2 в RCC), и много много другого такого барахла, когда "если так, то регистр такой-то бит такой-то, а DMA такой-то, а если этак - то вот так..." Это макросы позволяют. 39 минут назад, Arlleex сказал: Но при этом чтобы вводимый номер в compile-time по-возможности проверялся на допустимость И на допустимость параметров - тоже макросы позволяют проверять. Если умеючи... многое позволяют. Надо только немного подумать. И совсем без какого-либо runtime кода. Чисто в compile-time. 5 минут назад, Forger сказал: Вы наверняка понимаете, что это - утопия, в смысле автоматом переназначает все внутри. Для кого то может и утопия. А у меня например так построены все исходники, что я свободно и легко меняю пины/UART-ы и т.п. изменяя всего несколько цифр в заголовочном файле определения периферии. Все необходимые DMA-каналы, тактирования, вектора прерываний и т.п. - переназначаются автоматом. И это очень удобно - за несколько секунд могу перекинуть какой-то UART, SPI, таймер и т.п. на другой. Не трогая исходников. Делаю так на разных семействах МК и ничего не "рассыпается". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба @jcxz, я и сам любитель трюков на #define-ах, причем у меня есть реализации всяко-разного с разной степенью "укуренности" Есть и макросы, на основе которых склеиваются другие макросы и автоматом там разворачивается нечто монстроидальное, но работающее корректно. Однако бывают ситуации, что к макросу нужно определить двойник-функцию, т.к. макрос, конкатенирующий символы, не применим к run-time-идентификаторам. Получается некое нагромождение дубликатов функций-макросов для compile-time и run-time. Более того, для максимально эффективного использования метапрограммирования почти всегда в месте определения макроса должны быть видны все глобальные символы, используемые в макросе и порождаемые им же (#define-ы битовых масок регистров, структуры периферийных регистров и много много чего еще). Что с трудом сказывается на будущей возможности перетащить безболезненно пару файлов .h/.cpp в другой проект, сохраняя функциональность написанного макроса (про сборку статических библиотек вообще говорить не приходится - метапрограммирование здесь не применимо почти). То, что макропрограммирование вполне позволяет решить мою задачу (поправить пару символов в .h-файле и вся цепочка аппаратурных зависимостей автоматом перестроится) - я понимаю. И понимаю, какой ценой (какими объемами писанины). Но я хочу понять, какие есть средства в C++, которые позволят максимально скрыть реализацию, эффективно работать с пространством имен и т.д. И есть ли они вообще. В данном случае из "шашечек и ехать" мне нужно рассмотреть, какие шашечки бывают. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 4 минуты назад, Arlleex сказал: @jcxzЧто с трудом сказывается на будущей возможности перетащить безболезненно пару файлов .h/.cpp в другой проект, сохраняя функциональность написанного макроса Не понимаю в чём трудность? Сам как раз так постоянно делаю: перетаскиваю из проекта в проект (на одинаковых или родственных МК) эти .h-файлы с макросами и #define-ами. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 52 minutes ago, jcxz said: Не трогая исходников. Если в этом цель, то, конечно, иначе никак ) Мне для этого все же приходится менять названия пинов или номера портов. Остальное неизменно. Впрочем, у меня в проектах периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле, как это "навязывается". В такой схеме перенос функцинально похожих модулей из в проекта в проект крайне просто и логичен. Так удается сохранить структуру проекта, без которой под плюсами получается "каша". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба Только что, Forger сказал: Впрочем, у меня в проектах периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле, как это "навязывается". Как ни странно - у меня тоже: "периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле" Только что, Forger сказал: В такой схеме перенос функцинально похожих модулей из в проекта в проект крайне просто и логичен. Именно. Есть проект на XMC4700 и другой - на XMC4500. Периферия у них почти одинаковая. Только её количество, распределение её по ногам - разное. Так не поверите: файлы, с макросами, определяющими периферию, по большей части просто копирую из проекта в проект. Меняю только в одном в файле привязку периферии к ногам и номера периферийных модулей. Ну и ещё что-то по мелочи. И обхожусь почти без средств C++. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба Just now, jcxz said: И обхожусь почти без средств C++. Если развесистые макросы - некая "плата" за избегание применения c++, то, конечно, иного выхода нет )) 1 minute ago, jcxz said: Меняю только в одном в файле привязку периферии к ногам и номера периферийных модулей. Аналогично. Но без макросов, стараюсь избегать их, заменяя где можно на constexpr. Век развесистых #define по-моему уже в прошлом, пора двигаться дальше )) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба Путем проб и ошибок буду значит сравнивать вообще самые разные способы, я все еще жду от плюсов какой-то волшебной таблетки. Потому что программировать хочется с мыслью "как хорошо и с первого взгляда понятно выглядят исходники, с этимивашими улучшенными синтаксическими и концептуальными возможностями C++", а не "как мне обозвать enum так чтобы он не пересекся именем с уже какими-нибудь другими идентификаторами проекта" (утрирую, но в чисто функциональном Си-подходе действительно порой сложно разобраться в структуре проекта). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 26 15 декабря, 2021 Опубликовано 15 декабря, 2021 · Жалоба 5 minutes ago, Arlleex said: в чисто функциональном Си-подходе действительно порой сложно разобраться в структуре проекта Это зависит от склада мозгов программиста. Наблюдал за разными - у всех своя методика, организация процесса. Тут действительно нужно найти что-то своё. Увы, не существует готового рецепта, подходящего для всех ( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться