Перейти к содержанию
    

Плавный переход C -> C++ под МК

Можно ли какими-то средствами задать тип-подмножество целых, только не обзывать эти подмножества именами?

Т.е. я хочу что-то типа такого

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-а (дублирование нижних подчеркиваний просто замусоривает одни и те же смысловые конструкции).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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:

нижние подчеркивания просто выглядят они контекстно ужасно

Это нарушение нужно вписать в КОАпп, как злостное ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У меня возникло желание при инстанцировании объекта класса 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, и т.д.).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 minutes ago, Arlleex said:

У меня возникло желание при инстанцировании объекта класса UARTDMARx задавать ему все параметры, которые могут быть мультиплексированы разными способами. И поэтому проще сообщить классу отдельно номер GPIO (A, B, C...), номер пина (0, 1, 2...), номер UART (1, 2, ..., 6) и т.д.

А зачем "мультиплексировать"? Что это дает в юзер-коде?

 

Quote

Потому что из некого PA3 не сразу очевидно как выцыганить информацию о том, каким битом и в каком регистре разрешить тактирование GPIO, в каких полях модифицировать настройки самого пина в регистрах GPIO, и какой номер альтернативной функции задается для этого пина (все они слишком переплетены в возможных конфигурациях).

Никаких проблем :dirol:

Предусмотрен базовый класс 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 ... одним и тем же кодом будет обрабатываться инициализация нужной инфраструктуры периферии.

По-моему вы слишком усложняете ту часть, которая по сути в коде выполняется лишь при старте :)

В конце концов, это всего-лишь пины :don-t_mention:

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

37 минут назад, Arlleex сказал:

Потому что из некого PA3 не сразу очевидно как выцыганить информацию о том, каким битом и в каком регистре разрешить тактирование GPIO, в каких полях модифицировать настройки самого пина в регистрах GPIO, и какой номер альтернативной функции задается для этого пина (все они слишком переплетены в возможных конфигурациях). Я захотел, в конечном счете, создать некую таблицу PinMux, из которой по индексам указанных отдельно (при инстанцировании объекта) GPIO, Pin ... одним и тем же кодом будет обрабатываться инициализация нужной инфраструктуры периферии.

Делаю всё это посредством макросов. С помощью макросов можно получить все эти данные с минимальными runtime затратами.

Причём так у меня сделано для: NXP, STM32, INFINEON, TIVA, ... - везде один и тот же механизм, разные таблицы и макросы только. Без всяких классов, конструкторов и т.п.

 

20 минут назад, Forger сказал:

По-моему вы слишком усложняете ту часть, которая по сути в коде выполняется лишь при старте :)

Не надо говорить за всех. У меня например в процессе работы, различная периферия может инициализироваться/деинициализироваться и при этом соответственно перепрограммируются режимы пинов (GPIO <-> альтернативная). И так делаю всегда: если какая-то периферия может быть включена или отключена, соответственно пин переводится из режима GPIO в альтернативную функцию после инита периферии и переводится в режим GPIO перед деинитом периферии.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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


Грубо говоря, на основе параметра, передаваемого в конструктор, из таблицы берется нужный набор данных и участвует в инициализации всего и вся.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

36 minutes ago, Arlleex said:

Т.е. идеально, когда реализация сама распидаливает все эти хитрож*пые зависимости с тактированиями UART-ов, DMA и т.д.

Вы наверняка понимаете, что это - утопия, в смысле автоматом переназначает все внутри.

Даже, если удасться что-то подобное реализовать, то для этого придется перекурить весь даташит на КАЖДЫЙ камень, и обязательно будет крайне монстроподобная и забагованная конструкция. Шаг влево и карточный домик рассыпется.

Стоят ли такие титанические труды ради разовой инициализации, которую для каждого проекта можно реализовать за день работы с перекурами? Вопрос риторический )

 

Для себя я давно решил, что наглядно инициализация железа в каждом проекте гораздо полезнее для самого проекта, чем попытки все это скрыть где-то в недрах некой жирнющей самопальной либы )

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

37 минут назад, Arlleex сказал:

Угу... Макросы штука мощная, бесспорно. Но порой не шибко гибкая, ИМХО.

А в чём именно "негибкость"? Чего они не позволяют?

38 минут назад, Arlleex сказал:

когда реализация сама распидаливает все эти хитрож*пые зависимости с тактированиями UART-ов, DMA и т.д. Ведь, например, в моем STM32F4 разрешение peripheral clocks разных UART-ов осуществляется в разных регистрах (APB1ENR, APB2ENR), DMA1/2 обслуживают свои наборы UART-ов (т.е. на основе номера выбранного UART-модуля класс сам должен автоматически включить тактирование DMA1/2 в RCC), и много много другого такого барахла, когда "если так, то регистр такой-то бит такой-то, а DMA такой-то, а если этак - то вот так..."

Это макросы позволяют.

39 минут назад, Arlleex сказал:

Но при этом чтобы вводимый номер в compile-time по-возможности проверялся на допустимость

И на допустимость параметров - тоже макросы позволяют проверять. Если умеючи... :victory:  многое позволяют. Надо только немного подумать.  :umnik2:

И совсем без какого-либо runtime кода. Чисто в compile-time.

5 минут назад, Forger сказал:

Вы наверняка понимаете, что это - утопия, в смысле автоматом переназначает все внутри.

Для кого то может и утопия. А у меня например так построены все исходники, что я свободно и легко меняю пины/UART-ы и т.п. изменяя всего несколько цифр в заголовочном файле определения периферии. Все необходимые DMA-каналы, тактирования, вектора прерываний и т.п. - переназначаются автоматом.

И это очень удобно - за несколько секунд могу перекинуть какой-то UART, SPI, таймер и т.п. на другой. Не трогая исходников.

Делаю так на разных семействах МК и ничего не "рассыпается".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

@jcxz, я и сам любитель трюков на #define-ах, причем у меня есть реализации всяко-разного с разной степенью "укуренности":biggrin: Есть и макросы, на основе которых склеиваются другие макросы и автоматом там разворачивается нечто монстроидальное, но работающее корректно. Однако бывают ситуации, что к макросу нужно определить двойник-функцию, т.к. макрос, конкатенирующий символы, не применим к run-time-идентификаторам. Получается некое нагромождение дубликатов функций-макросов для compile-time и run-time. Более того, для максимально эффективного использования метапрограммирования почти всегда в месте определения макроса должны быть видны все глобальные символы, используемые в макросе и порождаемые им же (#define-ы битовых масок регистров, структуры периферийных регистров и много много чего еще). Что с трудом сказывается на будущей возможности перетащить безболезненно пару файлов .h/.cpp в другой проект, сохраняя функциональность написанного макроса (про сборку статических библиотек вообще говорить не приходится - метапрограммирование здесь не применимо почти). То, что макропрограммирование вполне позволяет решить мою задачу (поправить пару символов в .h-файле и вся цепочка аппаратурных зависимостей автоматом перестроится) - я понимаю. И понимаю, какой ценой (какими объемами писанины). Но я хочу понять, какие есть средства в C++, которые позволят максимально скрыть реализацию, эффективно работать с пространством имен и т.д. И есть ли они вообще. В данном случае из "шашечек и ехать" мне нужно рассмотреть, какие шашечки бывают.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 минуты назад, Arlleex сказал:

@jcxzЧто с трудом сказывается на будущей возможности перетащить безболезненно пару файлов .h/.cpp в другой проект, сохраняя функциональность написанного макроса

Не понимаю в чём трудность? Сам как раз так постоянно делаю: перетаскиваю из проекта в проект (на одинаковых или родственных МК) эти .h-файлы с макросами и #define-ами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

52 minutes ago, jcxz said:

Не трогая исходников.

Если в этом цель, то, конечно, иначе никак )

Мне для этого все же приходится менять названия пинов или номера портов. Остальное неизменно.

Впрочем, у меня в проектах периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле, как это "навязывается".

В такой схеме перенос функцинально похожих модулей из в проекта в проект крайне просто и логичен. Так удается сохранить структуру проекта, без которой под плюсами получается "каша".

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Только что, Forger сказал:

Впрочем, у меня в проектах периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле, как это "навязывается".

Как ни странно - у меня тоже: "периферия иницилизируется в тех модулях, кому она "принадлежит" и соотв. где применяется, а не в неком общем startup файле"  :unknw:

Только что, Forger сказал:

В такой схеме перенос функцинально похожих модулей из в проекта в проект крайне просто и логичен.

Именно. Есть проект на XMC4700 и другой - на XMC4500. Периферия у них почти одинаковая. Только её количество, распределение её по ногам - разное. Так не поверите: файлы, с макросами, определяющими периферию, по большей части просто копирую из проекта в проект. Меняю только в одном в файле привязку периферии к ногам и номера периферийных модулей. Ну и ещё что-то по мелочи. И обхожусь почти без средств C++.  :wink:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Just now, jcxz said:

И обхожусь почти без средств C++.

Если развесистые макросы - некая "плата" за избегание применения c++, то, конечно, иного выхода нет ))

 

1 minute ago, jcxz said:

Меняю только в одном в файле привязку периферии к ногам и номера периферийных модулей.

Аналогично. Но без макросов, стараюсь избегать их, заменяя где можно на constexpr.

Век развесистых #define по-моему уже в прошлом, пора двигаться дальше ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Путем проб и ошибок буду значит сравнивать вообще самые разные способы, я все еще жду от плюсов какой-то волшебной таблетки.

Потому что программировать хочется с мыслью "как хорошо и с первого взгляда понятно выглядят исходники, с этимивашими улучшенными синтаксическими и концептуальными возможностями C++", а не "как мне обозвать enum так чтобы он не пересекся именем с уже какими-нибудь другими идентификаторами проекта" (утрирую, но в чисто функциональном Си-подходе действительно порой сложно разобраться в структуре проекта).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 minutes ago, Arlleex said:

в чисто функциональном Си-подходе действительно порой сложно разобраться в структуре проекта

Это зависит от склада мозгов программиста.

Наблюдал за разными - у всех своя методика, организация процесса.

Тут действительно нужно найти что-то своё. Увы, не существует готового рецепта, подходящего для всех (

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...