Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Библиотеки для STM32
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2, 3, 4, 5, 6, 7, 8
Forger
Цитата(Reflector @ Apr 19 2017, 10:01) *
Эээ. Вообще-то это был ответ AHTOXЕ, твоего полного кода у меня то нет sm.gif

Гы, заработался smile3046.gif
juvf
Цитата(Forger @ Apr 19 2017, 11:06) *
Вы ошиблись темой. Здесь собрался народ с совершенно иными взглядами на мир. Нам не по пути. Проходите мимо.

Это вы хамите.

Цитата
Просто осторожно нужно goto использовать
+1

Цитата
если goto и метка лежат не дальше, чем в пределах 1-2 страниц текста, то этот код читать удобно, если дальше — могут возникнуть проблемы.
эээээ...... 1-2 страницы!!!.... ЕСЛИ ДАЛЬШЕ!?....
Опять же возвращаюсь к стилю программирования.... гудстаил является таким, чтобы функция влезла в монитор. Функция должна быть как на ладони... она должна быть полностью охвачена взглядом. Если что-то скрывается за скролом - это уже черевато багой.

Цитата
В общем так, проинитил я 5 пинов, получил 144 байта, против 188 у меня.
кстати.... эти ваши плюсы.... вот на порту РА нужно проинитить пины 0, 1, 6, 7 как выход... на Led или на CS.... не важно. с SPL или си-стаил инит не такой красивый, но там инитятся порты все хором за раз (в stm32). Теперь допустим надо 0 и 6 выствить в "0", а 1 и 7 выставить в 1. через регистр BRRS это делается за 1 команду си/с++ (вроде как в асме выходит 3 команды). AHTOXА, Forger, Reflector - в ваших юСтайлах сколько строк дизасемблера занимает инит 4-х портов, и сколько переключение? если инит одного порта будет вызван 4 раза.... то в итоге... код будет вызываться один и тот же 4 раза. Но допустим, функция - зашли в неё и проинитили 4 порта, потом выставили на них значение - сколько машинных тактов будет крутиться код при ините и при переключении? А если не 4 пина, а 13 пинов и все на РА?
Reflector
Цитата(Эдди @ Apr 19 2017, 08:16) *
Очередной подход к калокубу.
Хотя, возможно, шаблоны С++ и разворачиваются в static inline, ХЗ. Но мне как-то неприятно видеть цепепешный код применительно к микроконтроллерам, а также оформление инициализации периферии в виде общих функций (как в SPL или калокубе). Либо делать каждый раз явно, либо оформлять как макросы (на худой конец — static inline функции).

Как-то попалась мне на другом форуме тема в духе "помогите, код не влазит в 1Кб tiny13". Я к тому времени для avr уже практически не писал, а когда писал, то делал это еще на С, потому ради интереса взял этот код и просто поменял расширение на cpp. Gcc добавил 20 байт, не знаю точно почему, для STM32 обычно не добавляет ничего. Затем я переписал все на С++, переделав в том числе и работу с портами через шаблонный класс. Добавилось еще 4 байта. Итого +24 байта, но то ли из-за того, что у меня была другая версия компилятора, то ли другие ключи, хотя в теме вроде их подбирали под минимальный размер, размер похудел примерно байт на 150 и в 1Кб уже влазило. Даже оптимизировать ничего не пришлось, хотя основной выигрыш должен быть именно там. А теперь сравни tiny13 и самый простой STM32...
Forger
Цитата(juvf @ Apr 19 2017, 10:21) *
AHTOXА, Forger, Reflector - в ваших юСтайлах сколько строк дизасемблера занимает инит 4-х портов, и сколько переключение? если инит одного порта будет вызван 4 раза.... то в итоге... код будет вызываться один и тот же 4 раза. Но допустим, функция - зашли в неё и проинитили 4 порта, потом выставили на них значение - сколько машинных тактов будет крутиться код при ините и при переключении? А если не 4 пина, а 13 пинов и все на РА?

Если делать выбор между:
1) читаемость кода, его сопровждаемость и переносимостью,
2) экономия FLASH вплоть до байтов,
то я всегда буду выбирать 1-е.
Но если проект упирается в стоимость контроллера (тысячные партии изделия, при низкой стоимости самого изделия) и мне лично это будет выгодно (банально, бонусы или доля продаж),
то я конечно же буду ставить дешевый контроллер (STM8) и мучаться с его кривой средой и втаптыванием в него кода. Но в подобных проектах код обычно предельно простой - написал и забыл.

Однако, я всегда старался избегать подобных проектов. © "Не хочу, не буду" )))
Поэтому предпочитаю брать подходящий контроллер с достаточным объемом flash и озу, а код писать так, как мне удобно, поскольку сопровождать этот код мне же и придется.
А экономить чужие деньги уже давно разучился, точнее, отучили более опытные товарищи )))
ViKo
А у меня самый тонкий и короткий код. biggrin.gif
CODE

/*! GPIO Mode Type */
typedef enum {
MD_IN, //!< Input *
MD_GO, //!< General purpose output
MD_AF, //!< Alternate function
MD_AN //!< Analog
} GPIO_MODE_t;

/*! GPIO Output Type */
typedef enum {
OT_PP, //!< Output push-pull*
OT_OD //!< Output open-drain
} GPIO_OTYPE_t;

/*! GPIO Output Speed Type */
typedef enum {
SP_LO, //!< 2 MHz Low speed *
SP_ME, //!< 10 MHz Medium speed
// 2 MHz
SP_HI = 3 //!< 50 MHz High speed
} GPIO_OSPEED_t;

/*! GPIO Pull-Up_Pull-Down Type */
typedef enum {
PL_NP, //!< No pull-up, pull-down *
PL_PU, //!< Pull-up
PL_PD //!< Pull-down
// Reserved
} GPIO_PUPD_t;

/*! GPIO Alternate Functions (4 bits)
The specific alternate function assignments for each pin are detailed in
the device datasheet */
typedef enum {
AF_00, //!< AF0 *
AF_01, //!< AF1
AF_02, //!< AF2
AF_03, //!< AF3
AF_04, //!< AF4
AF_05, //!< AF5
AF_06, //!< AF6
AF_07, //!< AF7
AF_08, //!< AF8
AF_09, //!< AF9
AF_10, //!< AF10
AF_11, //!< AF11
AF_12, //!< AF12
AF_13, //!< AF13
AF_14, //!< AF14
AF_15 //!< AF15
} GPIO_AFLH_t;

/*!****************************************************************************
@brief Port configuration
@details Конфигурация портов целиком
@param PORT - имя порта (A..G)
@param MDx, OTx, SPx, PLx, AFx - режимы x-битов порта
@note Default: MD_IN, OT_PP, SP_02, PL_NP, AF_00
*/
#define GPIO_CONF(PORT, \
MD00, OT00, SP00, PL00, AF00, \
MD01, OT01, SP01, PL01, AF01, \
MD02, OT02, SP02, PL02, AF02, \
MD03, OT03, SP03, PL03, AF03, \
MD04, OT04, SP04, PL04, AF04, \
MD05, OT05, SP05, PL05, AF05, \
MD06, OT06, SP06, PL06, AF06, \
MD07, OT07, SP07, PL07, AF07, \
MD08, OT08, SP08, PL08, AF08, \
MD09, OT09, SP09, PL09, AF09, \
MD10, OT10, SP10, PL10, AF10, \
MD11, OT11, SP11, PL11, AF11, \
MD12, OT12, SP12, PL12, AF12, \
MD13, OT13, SP13, PL13, AF13, \
MD14, OT14, SP14, PL14, AF14, \
MD15, OT15, SP15, PL15, AF15); \
GPIO##PORT->MODER = ( \
MD00 << 0 | MD01 << 2 | MD02 << 4 | MD03 << 6 | \
MD04 << 8 | MD05 << 10 | MD06 << 12 | MD07 << 14 | \
MD08 << 16 | MD09 << 18 | MD10 << 20 | MD11 << 22 | \
MD12 << 24 | MD13 << 26 | MD14 << 28 | (uint32_t)MD15 << 30); \
GPIO##PORT->OTYPER = ( \
OT00 << 0 | OT01 << 1 | OT02 << 2 | OT03 << 3 | \
OT04 << 4 | OT05 << 5 | OT06 << 6 | OT07 << 7 | \
OT08 << 8 | OT09 << 9 | OT10 << 10 | OT11 << 11 | \
OT12 << 12 | OT13 << 13 | OT14 << 14 | (uint32_t)OT15 << 15); \
GPIO##PORT->OSPEEDR = ( \
SP00 << 0 | SP01 << 2 | SP02 << 4 | SP03 << 6 | \
SP04 << 8 | SP05 << 10 | SP06 << 12 | SP07 << 14 | \
SP08 << 16 | SP09 << 18 | SP10 << 20 | SP11 << 22 | \
SP12 << 24 | SP13 << 26 | SP14 << 28 | (uint32_t)SP15 << 30); \
GPIO##PORT->PUPDR = ( \
PL00 << 0 | PL01 << 2 | PL02 << 4 | PL03 << 6 | \
PL04 << 8 | PL05 << 10 | PL06 << 12 | PL07 << 14 | \
PL08 << 16 | PL09 << 18 | PL10 << 20 | PL11 << 22 | \
PL12 << 24 | PL13 << 26 | PL14 << 28 | (uint32_t)PL15 << 30); \
GPIO##PORT->AFR[0] = ( \
AF00 << 0 | AF01 << 4 | AF02 << 8 | AF03 << 12 | \
AF04 << 16 | AF05 << 20 | AF06 << 24 | (uint32_t)AF07 << 28); \
GPIO##PORT->AFR[1] = ( \
AF08 << 0 | AF09 << 4 | AF10 << 8 | AF11 << 12 | \
AF12 << 16 | AF13 << 20 | AF14 << 24 | (uint32_t)AF15 << 28);

/*!****************************************************************************
@brief Port configuration lock
@details Зафиксировать конфигурацию портов
@param PORT - имя порта (A..G)
@param BITS - биты порта, которые нужно зафиксировать (0xffff - все)
@return
@note
*/
#define GPIO_LOCK(PORT, BITS); \
GPIO##PORT->LCKR = 0x00010000 | BITS; \
GPIO##PORT->LCKR = 0x00000000 | BITS; \
GPIO##PORT->LCKR = 0x00010000 | BITS; \
GPIO##PORT->LCKR;

Пример использования.
CODE

/*!****************************************************************************
@brief GPIO initialize
@note
*/
inline void GPIO_init(void)
{
GPIO_CONF(A,
MD_AN, OT_PP, SP_LO, PL_NP, AF_00, // 0: Wake-Up Button
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 1
MD_AN, OT_PP, SP_LO, PL_NP, AF_00, // 2: ADC1_IN3
MD_AF, OT_PP, SP_ME, PL_NP, AF_01, // 3: TIM2_CH4
MD_AN, OT_PP, SP_LO, PL_NP, AF_00, // 4: DAC1_OUT1
MD_AN, OT_PP, SP_LO, PL_NP, AF_00, // 5: DAC1_OUT2
MD_AN, OT_PP, SP_LO, PL_NP, AF_00, // 6: ADC2_IN3
MD_IN, OT_PP, SP_ME, PL_NP, AF_02, // 7:
MD_AF, OT_PP, SP_ME, PL_NP, AF_00, // 8: MCO
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 9
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 10
MD_AF, OT_PP, SP_HI, PL_NP, AF_14, // 11: USB_DM
MD_AF, OT_PP, SP_HI, PL_NP, AF_14, // 12: USB_DP
MD_AF, OT_PP, SP_ME, PL_NP, AF_00, // 13: SWDIO
MD_AF, OT_PP, SP_ME, PL_NP, AF_00, // 14: SWCLK
MD_IN, OT_PP, SP_LO, PL_NP, AF_00); // 15

// PB3 - SWO после сброса
GPIO_CONF(B,
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 0
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 1
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 2
MD_IN, OT_PP, SP_ME, PL_NP, AF_00, // 3: SWO
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 4
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 5
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 6:
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 7:
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 8
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 9
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 10
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 11
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 12
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 13
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 14
MD_AF, OT_PP, SP_ME, PL_NP, AF_01); // 15: TIM15_CH2

GPIO_CONF(C,
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 0
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 1
MD_AF, OT_PP, SP_HI, PL_NP, AF_03, // 2: COMP7_OUT
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 3
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 4
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 5
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 6
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 7
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 8
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 9
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 10
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 11
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 12
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 13
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 14
MD_IN, OT_PP, SP_LO, PL_NP, AF_00); // 15

GPIOE->ODR = 0x0000;
GPIO_CONF(E,
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 0
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 1
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 2
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 3
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 4
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 5
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 6
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 7
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 8: NW
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 9: NN
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 10: NE
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 11: EE
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 12: SE
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 13: SS
MD_GO, OT_PP, SP_ME, PL_NP, AF_00, // 14: SW
MD_GO, OT_PP, SP_ME, PL_NP, AF_00); // 15: WW

GPIO_CONF(F,
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 0
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 1
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 2
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 3
MD_AF, OT_PP, SP_HI, PL_NP, AF_02, // 4: COMP1_OUT
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 5
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 6
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 7
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 8
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 9
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 10
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 11
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 12
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 13
MD_IN, OT_PP, SP_LO, PL_NP, AF_00, // 14
MD_IN, OT_PP, SP_LO, PL_NP, AF_00); // 15

/* Заблокировать конфигурацию для всех битов портов */
GPIO_LOCK(A, 0xFFFF);
GPIO_LOCK(B, 0xFFFF);
GPIO_LOCK(C, 0xFFFF);
GPIO_LOCK(E, 0xFFFF);
GPIO_LOCK(F, 0xFFFF);
}
Forger
Цитата(ViKo @ Apr 19 2017, 10:46) *
А у меня самый тонкий и короткий код. biggrin.gif

Цитата
Пример использования.

ЖЕСТЬ wacko.gif
Reflector
Цитата(SasaVitebsk @ Apr 19 2017, 09:16) *
И что?
Ещё раз повторяю. Совершенно бессмысленно.
Приведу пример. Только что делал модификацию прибора. Изменилось количество каналов, полностью переразведена плата, на первой версии стояла отдельный МК который управлял клавой, светодиодами пищалкой и подключен был по I2C. В новой версии применили 595/165 SPI и отдельный таймер на пищалку.
Отладку нового проекта производил на старой плате. При переносе старый файл local.h переименовал в local_v1.h. Добавил файл local_v2.h с данными новой платы, и написал local.h где переключатель хидеров. В main.h ввёл макрос выбора версии платы. Написал один файл обработки новой клавы и внёс некоторые изменения в инициализацию - прерывания (так как поменялись таймеры каналы spi и прочее). Думаю всего изменения коснулись строчек 10. У меня в main.h указываются дефайны типа spi_dataflash/ spi_adc. И всё! Проект компилится для двух версий плат.
Чем поможет конструкция "PA5::On()"? По всему проекту её ловить? И что такое On? Я сейчас использую двухцветные светодиоды. Очень удобно Конструкция проще, информативность выше. И что даст ко всему этому PA5::On()? То есть нужен ещё слой, чтобы PA5::On превратился в удобоваримое powerled(red) или adc_cs_on. Я сейчас не о стилистике написания, поймите. Пусть стилистика будет ваша. Но применять по тексту основной программы обезличенные данные аппаратных ресурсов - тоже, что писать reg = 5. Это также недопустимо! Ни одной константы по тексту программы.
Итак над этой хренью появляется ещё один файл абстракций. Итого 2. А у меня он 1. Сразу аппаратура в абстракцию.

Зачем искать PA5::On() по всему проекту? И почему PA5? Дай нормальное имя:
Код
Pin<'A', 5> powerled;
...
powerled.On();

Если у тебя двухцветные светодиод, то с парой пинов ты все равно же как-то работаешь. Покажи как, тогда сравним.
ViKo
Цитата(Forger @ Apr 19 2017, 10:53) *
ЖЕСТЬ wacko.gif

Необъективно судите, впрочем, не в первый раз.
Минимальный размер кода. Меньше просто некуда.
Forger
Цитата(ViKo @ Apr 19 2017, 10:59) *
Необъективно судите...

Извините за прямоту, но, чтобы понять ваш код, пришлось его несколько раз пролистать сверху донизу.
После некоторого перерыва заново понять, что там и как делается, придется делать то же самое - листать и ломать голову, что тут и как работает.
Я считаю очень объективными первые впечатления, которые создал процесс изучения нового кода.

Цитата(ViKo @ Apr 19 2017, 10:59) *
Минимальный размер кода. Меньше просто некуда.

Может я отстал от поезда, но объясните: какой смысл такой "экономии на спичках"?

ViKo
Цитата(Forger @ Apr 19 2017, 11:06) *
Может я отстал от поезда, но объясните: какой смысл такой "экономии на спичках"?

Мы тут о крутизне спорим, да? Так в чем смысл городить классы-шмассы, тратить время, напрягать мозг, если результат хуже, чем стандартное применение дефайнов? В чем сила, брат? С++? Явно не для этого места. Как обучающий пример, сгодится.

Цитата(Forger @ Apr 19 2017, 11:06) *
Необъективно? Чтобы понять ваш код, пришлось его несколько раз пролистать сверху донизу.
Чтобы после некоторого перерыва заново понять, что там и как делается, придется делать то же самое - листать и ломать голову, что тут и как работает.

А ваш пример я и листать не стал. Все равно не осилю. biggrin.gif Да и зачем?
У меня в разных файлах определение и применение. Открываю в двух окнах в Notepad++. Все вижу сразу.
juvf
Цитата(Forger @ Apr 19 2017, 13:06) *
Может я отстал от поезда, но объясните: какой смысл такой "экономии на спичках"?

есть программисты, которым подавай boost, stl, и т.п..... которые передают по каналам связи джейсон... 1 бит состояния хранят как "Масляный выключатель Включен" и "Масляный выключатель Выключен". те ни когда не опустятся ниже прикладного уровня Linux/Windows. Есть которые опускаются ниже, но не любят тесноты.... им подавай запас под их "красивый и легко читаемый код".... А есть проекты, где нужно сделать хороший функционал на дешевом процессоре. Тут спорить бессмысленно.
Цитата
"Не хочу, не буду"
Прикладной программист для ПК не возмется за программирование stm32f100. Я не возьмусь за программирование СУБД.... Каждому своё. wink.gif
Forger
Цитата(ViKo @ Apr 19 2017, 11:11) *
Мы тут о крутизне спорим, да?

О чем вы?

Цитата
если результат хуже, чем стандартное применение дефайнов?

Не хочу с вами спорить, т. к. я прошел через оба варианта, начинал с вашего исполнения на дефайнах.
Но остался на том, что сейчас.

Вот простой пример работы со светодиодом, никакой дополнительной инициализации и никакого дополнительного кода:

Код
#include "Pin.hpp"

main()
{
   LedOpenDrain<RB0> ledRed;
....
    ledRed.on();
....
    ledRed.off();
....
}


Цитата(juvf @ Apr 19 2017, 11:29) *
есть программисты, которым подавай boost, stl, и т.п.....

(С) "Я ему про Фому, а он мне про Ерёму"
juvf
Цитата(Forger @ Apr 19 2017, 13:37) *
Вот простой пример работы со светодиодом, никакой дополнительной инициализации и никакого дополнительного кода:
на дефайнах этот код не менее прост и понятен.

Код
#include "MyInclude.h"

main()
{
   initRedLed();
....
    ledRedOn();
....
    ledRedOff();
....
}


а что вашего кода касательно.... с ходу.... не очень понятно что такое RB0? Не загялнув в инклуде не понятно, хотя может у вашего процессора есть порт RB0? У стм обычно PB0.
и второе.... вижу ledRed - это объект класса LedOpenDrain. Т.е. ledRed в будещем нельзя изменить и сделать как OutPullUp?

Kabdim
Цитата(Forger @ Apr 19 2017, 10:43) *
тысячные партии изделия, при низкой стоимости самого изделия

Ну вот всё и встало на места. Разные условия - разные подходы.
ЗЫ Тысячи это совсем мало. Размер партии слегка кастомного решения для средне размерного клиента.
Шаманъ
Цитата(Эдди @ Apr 18 2017, 18:57) *
К сожалению, gcc их не умеет. А было бы удобно, конечно. Правда, в случае 32-битных регистров удобство уже сомнительно, но с 8/16 все ОК.

Умеет. Да и чего бы ему не иметь, если это одно из его обычных расширений https://gcc.gnu.org/onlinedocs/gcc/Binary-c...inary-constants . Кстати умеет в чистом С. Только не забудьте ему ключик указать, чтобы он расширения включил.

P.S. Позабавили меня рассуждения последних нескольких страниц - столько внимания пину, можно подумать контроллер помощнее выбирался, чтобы ногами дрыгать через С++ классы sm.gif
Forger
Цитата(juvf @ Apr 19 2017, 11:49) *
на дефайнах этот код не менее прост и понятен.

Код
#include "MyInclude.h"

main()
{
   initRedLed();
....
    ledRedOn();
....
    ledRedOff();
....
}


Где ж он короче?
А содержимое initRedLed, ledRedOff, ledRedOn кто за вас писать будет? Вася Пупкин?
Иначе прога не соберется.

У меня Pin.hpp не зависит от проекта, он вообще вынесен в отдельный каталог к соотв. либе для соотв. семейства. Общий для всех проектов.
В него лазить и тем более править нет нужды, отладил и забыл.

Цитата
а что вашего кода касательно.... с ходу.... не очень понятно что такое RB0?
Описка, должно быть PB0.
Вообще, это самый простой пример, который собирается именно в таком виде без всяких доп. функций и т.п.
А в реальном коде этот PB0 "забит" дефайном в одном месте в файле настроек проекта (StaticSettings.hpp):

Код
// LEDS
#define PIN_LED_GREEN                        (PB6)
#define PIN_LED_RED                            (PB5)


Цитата
и второе.... вижу ledRed - это объект класса LedOpenDrain.
Т.е. ledRed в будещем нельзя изменить и сделать как OutPullUp?

Почему же нельзя? Конечно, можно, но это будет неправильно - нарушается логичность кода (какой смысл в runtime делать такие странные вещи - изменять схему подключения пина),
лучше использовать для этого другой класс со своим функционалом: DigitalOutputPin
Или вообще, просто Pin, но тогда придется вызывать вручную несколько методов, которые настроят пин на нужные параметры (подтяжка, направление, альтернативные функции и т. п.).

Вообще, LedOpenDrain у меня выглядит так (файл Pin.hpp):

Код
template <PIN pin>
class LedOpenDrain : public PinBase
{
public:
    LedOpenDrain(void) : PinBase(pin)
    {
        setAsDigitalOutput();
        setAsOpenDrain();
        setOutputSpeed2MHz();
        off();
    }

    void on(void) { setToLow(); }
    void off(void) { setToHigh(); }
};


Цитата(Шаманъ @ Apr 19 2017, 11:52) *
подумать контроллер помощнее выбирался, чтобы ногами дрыгать через С++ классы sm.gif

Подумать можно все что угодно, но в действительности все как раз наоборот - C++ используется потому что, проекте толстый и компилятор нормальный.
Ногодрыг на классах - лишь удобный довесок к куче других С++ полезностей, а вовсе не наоборот.

зы. Вот щас посматриваю в сторону С++ исключений, но пока просто изучаю матчасть, смотрю, что это дает, а что отбирает в МК ...
По этой же причине пока не использую кучу или что-то, что ее заменяет. Осторожничаю )))
SasaVitebsk
Цитата(Forger @ Apr 19 2017, 09:56) *
На момент создания всего этого у меня было две задачи: убрать кучу кода инициализации пинов, спратав это внутрь одного файла (Pin.hpp), сохранить скорость работы ногодрыга.
Оптимизация - это вторично. Главное - user-код изолирован от реализации Pin, который впоследствии можно безболезненно переделать.

Какую кучу инициализации?
Вы что бредите?
Я пишу свой файл local где детально описываю все ноги, включая неиспользуемые. Описываю оборудование применяемое на ногах.
Но не потому, что нет другого выхода. Потому что это способ сразу видеть схему, глазами программиста. Даже через несколько лет, я смогу восстановить схему, с точки зрения программиста. Это очень удобно, в том числе при отладке.
То есть этот подход ничего общего не имеет к стилю написания, а имеет прямое отношение к стилю проектирования.
Порты инициализирую сразу, а не попиново, но инициализация всё равно имеет мнемонику пинов, что не ухудшает переносимость.
"Куча кода инициализации портов" получается 5/6 регистров на порт. Плюс не более 20 макросов управления ногодрыгом. И это на 144 ножный корпус.
Причём макросы это не обязательно ногодрыг. Например подключен АЦП на SPI. DIN/DOUT(RDY). Я передаю, потом переключаю линию разрешаю прерывания по готовности и так далее. То есть это уже к класу ногодрыга не совсем относится, но по логике работы общая.
Я не ратую за макросы. Сейчас это не рекомендуется. Можно оформить процедурами либо классами. Но зачем увязывать это с портами?

И главное, мне не ответили в чём выигрыш? Если класс получается непереносимый, и его нельзя заимствовать? Зачем? Вот главный вопрос.
Reflector
Цитата(juvf @ Apr 19 2017, 10:21) *
кстати.... эти ваши плюсы.... вот на порту РА нужно проинитить пины 0, 1, 6, 7 как выход... на Led или на CS.... не важно. с SPL или си-стаил инит не такой красивый, но там инитятся порты все хором за раз (в stm32). Теперь допустим надо 0 и 6 выствить в "0", а 1 и 7 выставить в 1. через регистр BRRS это делается за 1 команду си/с++ (вроде как в асме выходит 3 команды). AHTOXА, Forger, Reflector - в ваших юСтайлах сколько строк дизасемблера занимает инит 4-х портов, и сколько переключение? если инит одного порта будет вызван 4 раза.... то в итоге... код будет вызываться один и тот же 4 раза. Но допустим, функция - зашли в неё и проинитили 4 порта, потом выставили на них значение - сколько машинных тактов будет крутиться код при ините и при переключении? А если не 4 пина, а 13 пинов и все на РА?

Если с парой светодиодов работать именно как с пинами, то нельзя их выставить в 0 одной командой. Будет две записи в BSRR, разница лишь в том, что для второго светодиода базовый адрес уже будет загружен и вместо 3 команд на ассме получим 2. Теоретически можно сделать такой метод, который бы принимал разные пины и инитил за раз, но особой необходимости в этом не вижу... Если же мы изначально выбираем работу с группой пинов, то при установке значений все равно будет одна запись в BSRR и получим практически или такой-же по размеру код, как и для одного пина. То же самое будет происходить если переключать группу пинов с входа на выход и т.п., т.е. частичную переинициализацию можно выполнять достаточно эффективно. При полной инициализации уже вызывается функция инициализации, для группы пинов она достаточно тяжелая, там в цикле считается двойная маска, я просто выбрал вариант с меньшим размером кода. Начальная инициализация выполняется один раз и скорость там не важна.

Цитата(juvf @ Apr 19 2017, 11:49) *
и второе.... вижу ledRed - это объект класса LedOpenDrain. Т.е. ledRed в будещем нельзя изменить и сделать как OutPullUp?

У меня был такой-же вопрос. Скорее всего можно переключать и в другие состояния тоже, но выглядит это действительно странно.
Forger
Цитата(SasaVitebsk @ Apr 19 2017, 12:12) *
Я пишу свой файл local где детально описываю все ноги, включая неиспользуемые. Описываю оборудование применяемое на ногах.

А я не пишу никаких local файлов. Пример выше собирается без доп. файлов. Нужно лишь указать путь к Pin.hpp в настройках проекта.

Цитата
Но не потому, что нет другого выхода. Потому что это способ сразу видеть схему, глазами программиста.

Именно поэтому всю привязку пинов я выношу в отдельный файл, просто ряд дефайнов, чтобы уйти от PA7, PB3 и т.п. на человеческие имена, соотв. схеме.

Цитата
И главное, мне не ответили в чём выигрыш?

В переносимости и читаемости кода не в момент написания, а вообще, в любое время.

Я настраиваю пины именно в тех модулях, которым они принадлежат. У меня проект построен по модульному принципу. Модули имею логическое разделение, и делится не по периферии/портам и т.п., а по смыслу.
Некоторые модули стартуют с задержкой и нельзя инициализировать их пины раньше инициализации железа, на котором они висят.
Мне важно контроллировать порядок и инициализации железа.
Для отладки удобно вообще отключать некоторые модули (два символа //)
Раньше все было свалено в кучу и создавала кучу проблем,
теперь все разбито на логически независимые модули, которые можно переносить из проекта в проект, при этом править приходит только то, что относится к этим модулям.
Мне сложно вам это объяснить, я до этого дорастал долго и мучительно, но обратно уже не вернусь. Тем более на груду макросов/дефайнов, раскиданных по всему коду ))
Покажу примером (Application.cpp):

Код
#include "Kernel.hpp"
#include "Application.hpp"

void Kernel::initializeModules()
{
    WatchDog::getInstance().initialize();
    Communication::getInstance().initialize();
....
    MotionControl::getInstance().initialize();
    Leds::getInstance().initialize();
    Settings::getInstance().initialize();
}

void Kernel::runModules()
{
    WatchDog::getInstance().run();
    Communication::getInstance().run();
.....
    MotionControl::getInstance().run();
    Leds::getInstance().run();
    Settings::getInstance().run();
}

void Kernel::initializeHardware(void)
{
... настраивается максимум тактирование ядра и шин
}


В каждом таком модуле может быть несколько задач (потоков) или вообще не быть.
Пины и периферия "принадлежат" модулям, там же инициализируются и настраиваются.
Прерывания тоже сделаны в виде классов (долго мудохался, но добился своего), никаких extern "C" irq и т.п.

Функция main скрыта в библиотеки rtos, ее не нужно реализовывать, достаточно лишь подключить соотв. lib файл и Kernel.hpp

Никаких глобальных объектов, все обращение модулей друг другу ТОЛЬКО через соотв. открытые методы.

Цитата
Если класс получается непереносимый, и его нельзя заимствовать?

Откуда такие выводы? Все как раз наоброт!
Reflector
Цитата(Forger @ Apr 19 2017, 12:12) *
Почему же нельзя? Конечно, можно, но это будет неправильно - нарушается логичность кода (какой смысл в runtime делать такие странные вещи - изменять схему подключения пина),
лучше использовать для этого другой класс со своим функционалом: DigitalOutputPin

Этот DigitalOutputPin, его же можно заставить работать и на вход? sm.gif
Forger
Цитата(Reflector @ Apr 19 2017, 12:31) *
Этот DigitalOutputPin, его же можно заставить работать и на вход? sm.gif

Да, можно. Но это можно сделать лишь нарочно, т. е. сознательно ставить палки в колеса. Мазохизм в чистом виде sm.gif
Если бы этот код уходил в виде библиотеки для имбицилов-программеров, то он выглядел бы совсем иначе. Где подобные "фокусы" не работали бы... Но это - уход от темы.
Reflector
Цитата(Forger @ Apr 19 2017, 12:35) *
Да, можно. Но это можно сделать лишь нарочно, т. е. сознательно ставить палки в колеса. Мазохизм в чистом виде sm.gif
Если бы этот код уходил в виде библиотеки для имбицилов-программеров, то он выглядел бы совсем иначе. Где подобные "фокусы" не работали бы... Но это - уход от темы.

Есть у меня один экранчик, там ноги тача совмещены с данными, соответственно они могут работать как цифровые входы/выходы или как аналоговый вход. Какой класс пина нужно выбрать?
juvf
Цитата(Forger @ Apr 19 2017, 14:12) *
Где ж он короче?
я не говорю, что он короче. в данном контексте я сказал что он также понятен и прост. Не залазия в хидер всё понятно.

Код
А содержимое initRedLed, ledRedOff, ledRedOn кто за вас писать будет? Вася Пупкин?
тот же кто напишет Pin.hpp

Цитата
какой смысл в runtime делать такие странные вещи - изменять схему подключения пина
Есть 1Wire, програмный i2c, can.....

Цитата
Или вообще, просто Pin, но тогда придется вызывать вручную несколько методов, которые настроят пин на нужные параметры (подтяжка, направление, альтернативные функции и т. п.).
я менял один бит в соответствующем регистре.

например.... динамическая индикация 4-х 7-ми сегментников с точкой. в прерывании от таймера нужно 8 портов выкл/откл, плюс общие катоды (4 шт). можно конечно вашими классами это сделать, проскочить по всем пинам и дернуть у всех On() или Off(). сколько вы просидите в прерывании? И если на остальное времени не хватит... то вы "не будете втаптывать код".... попросите вагон спичек, ну чтоб не экономить.
а это можно сделать одной командой
Код
GPIOA->BSRR = indicator[++curentNumber&3]; //где uint32_t indicator[4] - выставляется код каждого символа в основном потоке



да я согласен, что с++ рулит на мк. но не всегда и не везде. И ваш Pin.hpp не имеет отношения к использованию b. оно может жить и вместе и порознь. вкус и цвет....

Forger
Цитата(Reflector @ Apr 19 2017, 12:44) *
Есть у меня один экранчик, там ноги тача совмещены с данными, соответственно они могут работать как цифровые входы/выходы или как аналоговый вход. Какой класс пина нужно выбрать?

Просто Pin, от которого наследуются более узкоспециализированные (появились сравнительно недавно, так оказалось удобнее).
Reflector
Цитата(SasaVitebsk @ Apr 19 2017, 12:12) *
Порты инициализирую сразу, а не попиново, но инициализация всё равно имеет мнемонику пинов, что не ухудшает переносимость.

Код в студию sm.gif Я приводил пример инициализации портов для FSMC двумя строками, но также видел людей которые делали то же самое и код занимал страницу, при этом они гордились тем, как у них все подробно и понятно расписано sm.gif
juvf
Цитата(Reflector @ Apr 19 2017, 14:26) *
Если с парой светодиодов работать именно как с пинами, то нельзя их выставить в 0 одной командой. Будет две записи в BSRR, разница лишь в том, что для второго светодиода базовый адрес уже будет загружен и вместо 3 команд на ассме получим 2.
О чем вы? Как же нельзя?
Задача: допустим надо 0 и 6 выствить в "0", а 1 и 7 выставить в 1.
решение:
GPIOA->BSRR = GPIO_BSRR_BR0 | GPIO_BSRR_BR6 | GPIO_BSRR_BS1 | GPIO_BSRR_BS7;
Forger
Цитата(juvf @ Apr 19 2017, 12:48) *
тот же кто напишет Pin.hpp

Вы не понимаете моего подхода (( Еще раз:
Pin.hpp написан и отлажен один раз, после этого отложен в общую библиотеку, закинут в отдельную SVN-базу, поставлен аттрибут "только для чтения", его не нужно править от проекта к проекту. Вообще.
А ваши initRedLed, ledRedOff, ledRedOn зависят от проекта, в котором используются. Теперь понятно?

Цитата
Есть 1Wire, програмный i2c, can.....
В одном и том же коде на одном пине все это может висеть по очереди? Вы об этом?
Если да, тут я бы использовал только класс Pin. Хотя впервые слышу о чем-то подобном ...

Цитата
я менял один бит в соответствующем регистре.

Мне до лампочки сколько там битов меняется, на производительности никак не сказывается. Проверено. Экономия на спичках.

Цитата
например.... динамическая индикация 4-х 7-ми сегментников с точкой. в прерывании от таймера нужно 8 портов выкл/откл, плюс общие катоды (4 шт). можно конечно вашими классами это сделать, проскочить по всем пинам и дернуть у всех On() или Off(). сколько вы просидите в прерывании?

Я никогда не стану такие тяжелые вещи городить в прерывании, для этого у меня есть rtos, практически в каждом проекте.
Ногодрыг и другая активная деятельность производится в теле задачи, которая ждет неких событий. В данном случае его формирует прерывание. Остальной код не страдает и не тупит.


Цитата
И если на остальное времени не хватит... то вы "не будете втаптывать код".... попросите вагон спичек, ну чтоб не экономить.

Наш разговор переходит в разряд "глухого со слепым". Предлагаю заканчивать с этим и переключиться на что-то другое ))
Reflector
Цитата(juvf @ Apr 19 2017, 12:57) *
О чем вы? Как же нельзя?
Задача: допустим надо 0 и 6 выствить в "0", а 1 и 7 выставить в 1.
решение:
GPIOA->BSRR = GPIO_BSRR_BR0 | GPIO_BSRR_BR6 | GPIO_BSRR_BS1 | GPIO_BSRR_BS7;

Это нельзя сделать, точнее у меня это не реализовано по причине невостребованности, в классе имеющем дело с отдельными пинами. Если будет острая необходимость, то в принципе можно это сделать в таком же стиле:
Код
led1.base()->BSRR = ((led1.pinMask | led2.pinMask) << 16) | led3.pinMask | led4.pinMask;

Но один из светодиодов уезжает на другой порт и все ломается...
juvf
Цитата(Forger @ Apr 19 2017, 15:01) *
Мне до лампочки сколько там битов меняется, на производительности никак не сказывается. Экономия на спичках.
Ни фига вы не слышите... спички спички.... вам говорият - каждая спичка на счету. Вы опять "Мне до лампочки.... на производительности никак не сказывается".... тут народ считает каждый дизасемблерную строку... вы опять - мне до лампочки, это спички... У меня крутой овернинженеринг, остальный сущности .... "Не хочу, не буду".

Цитата
В одном и том же коде на одном пине все это может висеть по очереди? Вы об этом?
и такое было. Но хотя бы просто только 1-Wire, нужно менять направление пина.
Цитата
Наш разговор переходит в разряд "глухого со слепым". Предлагаю заканчивать с этим и переключиться на что-то другое ))
вот с этим я согласен

Цитата
Если будет острая необходимость, то в принципе можно это сделать в таком же стиле:
Код
led1.base()->BSRR = ((led1.pinMask | led2.pinMask) << 16) | led3.pinMask | led4.pinMask;
да, стиль такой же... но... лень скролить всю тему... на вскидку прокоментирую.. поправте, если я не прав...
led1 - это переменная, структура или класс. pinMask - это поля этой структуры. что делает ваш код
1)led1.pinMask | led2.pinMask
2)<< 16
3) | led3.pinMask
4) | led4.pinMask
5) led1.base()
6) BSRR = чему-то там
вам наверно проще/быстрее 4 раза вызвать led*.on/off();

что делает мой код
1) BSRR = чему-то там

Цитата
Но один из светодиодов уезжает на другой порт и все ломается...
есть такое.... но не всё так печально. будет так
1)GPIOA->BSRR = чему-то там
2)GPIOB->BSRR = чему-то там
всё ранво есть выгода. и при проектировании я учитываю эту особенность МК и стараюсь вешать на один порт.
Forger
Цитата(juvf @ Apr 19 2017, 14:09) *
тут народ считает каждый дизасемблерную строку..
Ну, что ж, остается лишь посочувствовать и пожелать удачи....
Видать, других дел нет, как экономить в одном месте битик, теряя сотни байт в других местах из-за криво организованного кода.
Каждый развлекается по-своему smile3046.gif

Цитата
каждая спичка на счету.
Уходите в asm, коли выбрали проц, в который не влазит код.
Чесс слово, это уже похоже на нытьё ... тока без обид laughing.gif


Цитата
и такое было. Но хотя бы просто только 1-Wire, нужно менять направление пина.

Никаких проблем: прямо в коде вызываем соотв. метод, отвечающий за направление пина. В моем случае вместо
Код
somePin.setAsDigitalOutput()

вызываем
Код
somePin.setAsDigitalInput()
juvf
Цитата(Forger @ Apr 19 2017, 16:16) *
Никаких проблем, прямо в коде вызываем соотв. метод, отвечающий за направление.
ожидал tакой ответ.... просто объявите пин LedOpenDrain pin, меняете у него выход на вход.... и он уже не выход c ОК, а вход. Конечно работать будет. Но объект класса LedOpenDrain - есть входной пин. Как то уши режет.
Forger
Цитата(juvf @ Apr 19 2017, 14:22) *
ожидал tакой ответ.... просто объявите пин LedOpenDrain pin, меняете у него выход на вход.... и он уже не выход c ОК, а вход. Конечно работать будет. Но объект класса LedOpenDrain - есть входной пин. Как то уши режет.

Я же уже сказал, что для подобной задачи есть более "базовый" класс Pin, его как раз и нужно использовать для этой задачи
или пойти чуть дальше и создать от Pin класс-наследник, который будет реализовывать функционал двунаправленного пина более красиво. Тут уже все упирается в вашу фантазию.

Теперь понятно?
juvf
Цитата(Forger @ Apr 19 2017, 16:16) *
Уходите в asm, коли выбрали проц, в который не влазит код.
Чесс слово, это уже похоже на нытьё ... тока без обид laughing.gif
давно уже современные компиляторы сравнялись (или почти) с асмом. смысла нет. И зачем? Мне вполне комфортно в с/с++. В тесноте, да не в обиде.
Forger
Цитата(juvf @ Apr 19 2017, 14:27) *
давно уже современные компиляторы сравнялись (или почти) с асмом. смысла нет. И зачем? Мне вполне комфортно в с/с++. В тесноте, да не в обиде.

Типа, съехал с темы ... Чтож, оно и к лучшему )))
Reflector
Цитата(juvf @ Apr 19 2017, 14:09) *
да, стиль такой же... но... лень скролить всю тему... на вскидку прокоментирую.. поправте, если я не прав...
led1 - это переменная, структура или класс. pinMask - это поля этой структуры. что делает ваш код
1)led1.pinMask | led2.pinMask
2)<< 16
3) | led3.pinMask
4) | led4.pinMask
5) led1.base()
6) BSRR = чему-то там

что делает мой код
1) BSRR = чему-то там

Нет, все совсем не так. Как я ранее говорил, в моем классе нет переменных, они есть у Forger. Сам классс выглядит следующим образом:
Код
template<uint32_t gpio, uint32_t pin_, uint32_t af_>
struct PinT
{
    static auto base() { return (GPIO_TypeDef*)gpio; }
    const static uint32_t af = af_;
    const static uint32_t pin = pin_;
    const static uint32_t pinMask = 1 << pin;
......
};

Так что pinMask - это обычная константа и генерируемый код будет в точности как у тебя.

Цитата
есть такое.... но не всё так печально. будет так
1)GPIOA->BSRR = чему-то там
2)GPIOB->BSRR = чему-то там
всё ранво есть выгода. и при проектировании я учитываю эту особенность МК и стараюсь вешать на один порт.

Эта выгода микроскопическая и думать над этим нужно только если уже все, флеш полностью закончился. Причем очевидно, что если пишешь достаточно компактный код и такое произошло, то скорее всего просто неверно выбран мк. На таких операциях много не сэкономишь, основная экономия лежит в области алгоритмов...
juvf
Цитата(Forger @ Apr 19 2017, 16:32) *
Типа, съехал с темы ... Чтож, оно и к лучшему )))

c какой темы? я уже всё сказал. сам же говоришь
Цитата
Наш разговор переходит в разряд "глухого со слепым". Предлагаю заканчивать с этим и переключиться на что-то другое ))
зачем мне асм? я вижу в твоем коде сплошной оверинжененринг. я это уже говорил. то, что можно сделать бистро и просто - у тебя разрослось в классы и шаблоны... в сущности... ты считаешь - что это хорошо- твоё право. я тоже пользую ООП в МК. но использование ООП - не значит отказ от 0b00001000. Ты, в ущерб производительности и компактности пишешь классы, Да пожалуйста, пиши. Но ты при этом говоришь "Чо экономить на спичках, хватит ныть и идите в асм". Что-то мне про ООП доказываешь. что тут доказывать - там где надо я сделаю и классы, и шаблоны и паттрены. Но в некоторых случаях делаю прямую запись в регистры, в некоторых в какие-то булевые операции в которых удобно литералы b использовать.

Пока что в твоём подходе использовать ООП всюду, отвергая литералы, - я не вижу смысла и удобства. У тебя в майне 3 строчки - у меня в майне 3 строчки. У тебя всё в майне понятно и прозрачно, у меня в майне всё понятно и прозрачно. Я перенесу на другую платформу - поправлю дефайны, майн не буду трогать. ты перенесешь на др платформу - так же будешь писать новый Pin.hpp, майн не изменишь. Если платформа та же... ноги другие.. ты в дефайнах исправишь пин и порт, я в дефайне изменю пин и порт. О чем спор? В удобстве стиля? не вижу преимуществ. а вот в производительности - тут можно поспорить. Но для тебя это экономия на спичках... так что дальше можно не дискутировать wink.gif

Цитата(Reflector @ Apr 19 2017, 16:40) *
Эта выгода микроскопическая и думать над этим нужно только если уже все, флеш полностью закончился.
не всегда так. в обработчиках прерывания это очень актуально, таже динамическая индикация....
Reflector
Цитата(juvf @ Apr 19 2017, 15:00) *
не всегда так. в обработчиках прерывания это очень актуально, таже динамическая индикация....

Ну что ж, еще раз sm.gif Есть два класса: PinX и GpioX. Первый имеет дело с одним пином, второй с портом в целом, но пишет/читает только в область ограниченную маской. Теперь подумай сколько ног используется при динамической индикации и какой класс больше для этих целей подходит sm.gif
Forger
Цитата(juvf @ Apr 19 2017, 15:00) *
В удобстве стиля? не вижу преимуществ.

Я раньше писал код для работы с пинами (да и не только) по практически точно такому же принципу, что и вы, но ушел на нынешнее решение.
Поэтому я отлично вижу преимущества, скорее всего, потому, что я пробовал и так и сяк...

Короче. Я поделился своим опытом, показал примеры. Буду рад, если кому-то пригодится wink.gif
AHTOXA
Цитата(Reflector @ Apr 19 2017, 11:45) *
В общем так, проинитил я 5 пинов, получил 144 байта, против 188 у меня. Проинитил 10 пинов, получил 252 vs 216, уже в мою пользу.

Интересно, как это так? У тебя же вроде тоже нет общего базового класса.
Цитата(Reflector @ Apr 19 2017, 11:45) *
Но при этом твой код изначально не работает на F1, также там используется bitBand, значит он не работает и на F0, но главное это его размер в дебаге... У меня он вырос в 2.4 раза, у тебя в 110 sm.gif Ты принципиально не пользуешься отладкой?

А посмотреть на уровень выше? sm.gif
Там есть pin.h, который включает нужный pin_xxx.h, в зависимости от типа контроллера. Для F1 другой вариант, для F0 третий.

Цитата(SasaVitebsk @ Apr 19 2017, 11:16) *
И что?
Ещё раз повторяю. Совершенно бессмысленно.
Приведу пример. Только что делал модификацию прибора. Изменилось количество каналов, полностью переразведена плата, на первой версии стояла отдельный МК который управлял клавой, светодиодами пищалкой и подключен был по I2C. В новой версии применили 595/165 SPI и отдельный таймер на пищалку.

Это всё замечательно, но как это относится к классу управления ножкой микроконтроллера?

Цитата(SasaVitebsk @ Apr 19 2017, 11:16) *
Чем поможет конструкция "PA5::On()"?

Ну вот, например. Есть класс-АЦП на SPI. Он оформлен в виде шаблона, принимающего параметр - ножку CS. (Ссылка на SPI передаётся в конструктор). Очень удобно.
Я пишу в файле описания железа:

Код
using AdcCsPin = Pin<'A', 2, 'L'>;
using AdcSpi = SPI<3, DIV_2>;
using AdcType = Ad7714<AdcCsPin>;


И потом использую. Разве не удобно?

Цитата(SasaVitebsk @ Apr 19 2017, 11:16) *
То есть нужен ещё слой, чтобы PA5::On превратился в
удобоваримое powerled(red) или adc_cs_on. Я сейчас не о стилистике написания, поймите. Пусть стилистика будет ваша. Но применять по тексту основной программы обезличенные данные аппаратных ресурсов - тоже, что писать reg = 5. Это также недопустимо! Ни одной константы по тексту программы.
Итак над этой хренью появляется ещё один файл абстракций. Итого 2. А у меня он 1. Сразу аппаратура в абстракцию.


Никакого ещё одного слоя, как вы видите из примера выше. Вместо вашего макроса adc_cs_on у меня в коде класса Adc будет CS:On();

Цитата(SasaVitebsk @ Apr 19 2017, 11:16) *
Ну хорошо рассмотрим переносимость данного класса и его применимость в другом проекте.
Ну и какая она? Громко заявлена, что она pin_stm32F4xx.h. То есть сразу же радуемся, что при использовании этой чудо хрени в stm32f0 мы в пролёте. Более того, при использовании её в f1 там придётся переписать половину.
Но и это ещё не всё. Я утверждаю, что эта хрень не заработает даже на всех камнях F4. Почему? Поищите сами. Или наступите на грабли.
При переносе на lpc это полностью переписать. И код использующий данный класс - тоже будет полностью переписан.
Вот у меня теперь вопрос. В ЧЁМ ВЫИГРЫШ БРАТ?!!!

Я уже ответил выше, но повторюсь. У меня включается файл pin.h, который в свою очередь включает нужный файл в зависимости от типа контроллера. Я дал ссылку на конкретную реализацию, чтобы люди не заблудились в репозитории. Но получил обратный эффект.

Что касаемо переносимости между контроллерами, то она тоже имеется (хотя и не полная). Для всех контроллеров, которые я использовал, я писал свой вариант pin.h.

А, и про это:
Цитата(SasaVitebsk @ Apr 19 2017, 11:16) *
Я утверждаю, что эта хрень не заработает даже на всех камнях F4. Почему? Поищите сами.

Насчёт хрени - я вас чем-то обидел? Откуда такие бурные эмоции?
А насчёт "не заработает даже на всех камнях F4" - буду благодарен за информацию о косяке.
Reflector
Цитата(AHTOXA @ Apr 19 2017, 15:46) *
Интересно, как это так? У тебя же вроде тоже нет общего базового класса.

Нет, у меня всего один класс и отдельно от него функция инициализации.

Цитата
А посмотреть на уровень выше? sm.gif
Там есть pin.h, который включает нужный pin_xxx.h, в зависимости от типа контроллера. Для F1 другой вариант, для F0 третий.

Это хорошо, но с дебагом все равно нужно что-то делать sm.gif
juvf
Цитата
Эта выгода микроскопическая и думать над этим нужно только если уже все, флеш полностью закончился.

Цитата(Reflector @ Apr 19 2017, 17:28) *
Ну что ж, еще раз sm.gif Есть два класса: PinX и GpioX. Первый имеет дело с одним пином, второй с портом в целом, но пишет/читает только в область ограниченную маской. Теперь подумай сколько ног используется при динамической индикации и какой класс больше для этих целей подходит sm.gif
Эта выгода есть или нет? Вы пишете что всё таки эта выгода есть, но она макроскопическая. Я не оспариваю есть она или нет, я сразу написал - поправте меня. И всё же вы пишете что она есть, и что над этим нужно только если уже все, флеш полностью закончился. Я не согласен именно с тем пунктом, что над макроскопической выгодой нужно думать, когда флешь закончилась. флеши может быть ещё вагон целый... можно фоты хранить, но если использовать это в прерывании, то тут каждая "спичка" на счету. Зачем вы меня носом в PinX и GpioX? Если нет выгоды - вопрос снят! wink.gif
AHTOXA
Цитата(juvf @ Apr 19 2017, 16:09) *
Ни фига вы не слышите... спички спички.... вам говорият - каждая спичка на счету.

В контексте темы (Библиотеки для STM32) - думаю, очевидно, что универсальность библиотеки не даётся бесплатно. И пользователь библиотеки при необходимости может отойти от стиля, и использовать прямой доступ к портам и проч. Но это ведь не отменяет удобства использования библиотек и пользы от них?
К тому же, вместо макросов можно написать специализированный класс, который будет столь же эффективен, но гораздо более читаем.


Цитата(Reflector @ Apr 19 2017, 18:08) *
Нет, у меня всего один класс и отдельно от него функция инициализации.

Тогда я не видел этого кода. Я видел только вот это:
Код
template<uint32_t gpio, uint32_t pin, uint32_t af_>
struct PinT
{
    static auto base() { return (GPIO_TypeDef*)gpio; }

    PinT() {}
    PinT(PinMode mode) { init(mode); }
.....
    static void write(bool data) { base()->BSRR = (0x10000 << pin) | (data << pin); }
};

...
PinA<5>  redLed;  // хоть глобально, код еще не генерится
....
redLed.init(PinMode::PushPull_HighSpeed);
redLed.set();


Цитата(Reflector @ Apr 19 2017, 18:08) *
Это хорошо, но с дебагом все равно нужно что-то делать sm.gif

C -O0 оно вообще не собирается. У gcc есть отладочный ключик -Og, с ним не такой большой размер выходит.
Проект hello-led c -O2 занимает 1472 байта, а с -Og - 1680. Вполне.
juvf
Цитата(AHTOXA @ Apr 19 2017, 18:22) *
В контексте темы (Библиотеки для STM32) - думаю, очевидно, что универсальность библиотеки не даётся бесплатно. И пользователь библиотеки при необходимости может отойти от стиля, и использовать прямой доступ к портам и проч. Но это ведь не отменяет удобства использования библиотек и пользы от них?
К тому же, вместо макросов можно написать специализированный класс, который будет столь же эффективен, но гораздо более читаем.
А я не против библиотек. я использую и спл, и хал, и калокуб и прямой доступ и классы/шаблоны. Там где удобно использовать 0x82 - я использую. Там где удобно использовать 0b0101010 - я использую. Просто я пояснил, что иногда использование прямого доступа, а также литералов x и b удобнее и даже эффективнее, чем самописные библиотеки/шаблоны. Просто есть религиозные люди, которые сало не едят 0b0101010 - не приемлют ... и начинают учить правильно жить по их вкусу.

Цитата
Я поделился своим опытом, показал примеры.
спасибо большое.... только там куски... какие то... был бы совсем респект, если бы свои pin.рhh в полный рост в студию... и с примером... тогда точно кому-то пригодится, и вы будете рады wink.gif


Цитата
К тому же, вместо макросов можно написать специализированный класс, который будет столь же эффективен, но гораздо более читаем.

наверно можно гипотетический пример придумать такой... но
#define csOn() (GPIOA->BSRR = (1<<4))

написал за несколько секунд. отлаживать не надо, в использовании csOn(); Теперь какой можно написать класс... гораздо быстрее, который будет столь же эффективней и при этом будет ГОРАЗДО читаемый?
Reflector
Цитата(juvf @ Apr 19 2017, 16:21) *
Эта выгода есть или нет? Вы пишете что всё таки эта выгода есть, но она макроскопическая. Я не оспариваю есть она или нет, я сразу написал - поправте меня. И всё же вы пишете что она есть, и что над этим нужно только если уже все, флеш полностью закончился. Я не согласен именно с тем пунктом, что над макроскопической выгодой нужно думать, когда флешь закончилась. флеши может быть ещё вагон целый... можно фоты хранить, но если использовать это в прерывании, то тут каждая "спичка" на счету. Зачем вы меня носом в PinX и GpioX? Если нет выгоды - вопрос снят! wink.gif

Имелось в виду, что выгоды нет, если пишешь в GpioX сразу столько бит, сколько нужно, или когда берешь у группы PinX pinMask, объединешь их и пишешь в BSRR. Второй способ - это нечто достаточно редко используемое, но тут уже есть выгода относительно установки значения для каждого пина по отдельности. Не помню чтобы так делал, не рекомендую этим злоупотреблять, потому и не написал функции такой подход упрощающий sm.gif
Alechek
Цитата(juvf @ Apr 19 2017, 17:00) *
Пока что в твоём подходе использовать ООП всюду, отвергая литералы, - я не вижу смысла и удобства. У тебя в майне 3 строчки - у меня в майне 3 строчки. У тебя всё в майне понятно и прозрачно, у меня в майне всё понятно и прозрачно. Я перенесу на другую платформу - поправлю дефайны, майн не буду трогать. ты перенесешь на др платформу - так же будешь писать новый Pin.hpp, майн не изменишь.

Парни вдоховлены ногодрыгом?
Переход на другой камень - это помимо других ног еще и другая периферия, даже внутри одной серии. Да даже в пределах одного камня, другие ноги - другая периферия.
А тут похлеще начинается: сменилась периферия->сменились каналы DMA/таймеров->сменились имена обработчиков перывания... И вперед, переписывать все имена констант....
Играйтесь светодиодами дальше.
AHTOXA
Цитата(juvf @ Apr 19 2017, 18:39) *
Просто я пояснил, что иногда использование прямого доступа, а также литералов x и b удобнее и даже эффективнее, чем самописные библиотеки/шаблоны.

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

Цитата(juvf @ Apr 19 2017, 18:39) *
наверно можно гипотетический пример придумать такой... но
#define csOn() (GPIOA->BSRR = (1<<4))

написал за несколько секунд. отлаживать не надо, в использовании csOn(); Теперь какой можно написать класс... гораздо быстрее, который будет столь же эффективней и при этом будет ГОРАЗДО читаемый?

Насчёт быстрее - вряд ли, скорее будет паритет. Насчёт удобнее - всяко. Сравните:

Код
#define csOn() (GPIOA->BSRR = (1<<4))
#define csOff() (GPIOA->BSRR = (1<< (4 + 16)))
#define csCpl() (GPIOA->ODR ^ (1<<4))

vs
Код
using CS = Pin<'A', 4>;
CS::On();
CS::Off();
CS::Cpl();

Второй вариант на мой взгляд удобнее в сопровождении: при необходимости поменять ножку/порт нужно исправить только одно определение. С макросами тоже можно сделать так (определить отдельно порт, отдельно ножку, и использовать это во всех макросах). Но макросы нельзя передать в качестве параметра шаблона класса.
Reflector
Цитата(AHTOXA @ Apr 19 2017, 16:35) *
C -O0 оно вообще не собирается. У gcc есть отладочный ключик -Og, с ним не такой большой размер выходит.
Проект hello-led c -O2 занимает 1472 байта, а с -Og - 1680. Вполне.

С -O0 и не компилировалось пока я компилятору не помог принудительно не задав одной из функций -O2 sm.gif С -Og размер действительно меньше, только увы, отладка с ним нормально не работает. Ставишь брейкпоинт в одном месте, ее пролетает и останавливается где-то в другом.
Forger
Цитата(juvf @ Apr 19 2017, 16:39) *
спасибо большое.... только там куски... какие то... был бы совсем респект, если бы свои pin.рhh в полный рост в студию... и с примером... тогда точно кому-то пригодится, и вы будете рады wink.gif


CODE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Project: Any
// Description: Pin (STM32L1xx)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once
#include "stm32l1xx_rcc.h"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

typedef enum
{
PA0, PA1, PA2, PA3, PA4, PA5, PA6, PA7, PA8, PA9, PA10, PA11, PA12, PA13, PA14, PA15,
PB0, PB1, PB2, PB3, PB4, PB5, PB6, PB7, PB8, PB9, PB10, PB11, PB12, PB13, PB14, PB15,
PC0, PC1, PC2, PC3, PC4, PC5, PC6, PC7, PC8, PC9, PC10, PC11, PC12, PC13, PC14, PC15,
PD0, PD1, PD2, PD3, PD4, PD5, PD6, PD7, PD8, PD9, PD10, PD11, PD12, PD13, PD14, PD15,
PE0, PE1, PE2, PE3, PE4, PE5, PE6, PE7, PE8, PE9, PE10, PE11, PE12, PE13, PE14, PE15,
PF0, PF1, PF2, PF3, PF4, PF5, PF6, PF7, PF8, PF9, PF10, PF11, PF12, PF13, PF14, PF15,
PH0, PH1, PH2, PH3, PH4, PH5, PH6, PH7, PH8, PH9, PH10, PH11, PH12, PH13, PH14, PH15,
} PIN;

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class PinBase
{
public:
PinBase(PIN pin)
{
portIndex = pin / 16;
pinIndex = pin % 16;
pinMask = (((UNSIGNED32)1) << pinIndex);

switch (portIndex)
{
#ifdef GPIOA_BASE
case (0): port = GPIOA; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); break;
#endif
#ifdef GPIOB_BASE
case (1): port = GPIOB; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE); break;
#endif
#ifdef GPIOC_BASE
case (2): port = GPIOC; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE); break;
#endif
#ifdef GPIOD_BASE
case (3): port = GPIOD; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); break;
#endif
#ifdef GPIOE_BASE
case (4): port = GPIOE; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOE, ENABLE); break;
#endif
#ifdef GPIOF_BASE
case (5): port = GPIOF; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE); break;
#endif
#ifdef GPIOH_BASE
case (6): port = GPIOH; RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOH, ENABLE); break;
#endif
default : break;
}

disablePullUpAndPullDown();
}

void setAsDigitalInput() // MODER = 0
{
port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2));
port->MODER |= (((UNSIGNED32)0x00) << (pinIndex * 2));
}

void setAsDigitalOutput() // MODER = 1
{
port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2));
port->MODER |= (((UNSIGNED32)0x01) << (pinIndex * 2));
}

typedef UNSIGNED8 AlternativeFunction;
void setAsAlternative(AlternativeFunction function) // MODER = 2
{
port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2));
port->MODER |= (((UNSIGNED32)0x02) << (pinIndex * 2));
if (pinIndex < 8)
{
port->AFR[0] &= ~(((UNSIGNED32)0x0F) << (pinIndex * 4));
port->AFR[0] |= (((UNSIGNED32)function & 0x0F) << (pinIndex * 4));
}
else
{
port->AFR[1] &= ~(((UNSIGNED32)0x0F) << ((pinIndex - 8) * 4));
port->AFR[1] |= (((UNSIGNED32)function & 0x0F) << ((pinIndex - 8) * 4));
}
}

void setAsAnalogInput() // MODER = 3
{
disablePullUpAndPullDown();
port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2));
port->MODER |= (((UNSIGNED32)0x03) << (pinIndex * 2));
}

void setAsAnalogOutput() // MODER = 3
{
disablePullUpAndPullDown();
port->MODER &= ~(((UNSIGNED32)0x03) << (pinIndex * 2));
port->MODER |= (((UNSIGNED32)0x03) << (pinIndex * 2));
}

void setAsPushPull() { port->OTYPER &= ~(1 << pinIndex); } // OTYPER = 0
void setAsOpenDrain() { port->OTYPER |= (1 << pinIndex); } // OTYPER = 1

void setOutputSpeed400kHz() { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x00) << (pinIndex * 2)); } // OSPEEDR = 0
void setOutputSpeed2MHz() { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x01) << (pinIndex * 2)); } // OSPEEDR = 1
void setOutputSpeed10MHz() { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x02) << (pinIndex * 2)); } // OSPEEDR = 2
void setOutputSpeed40MHz() { port->OSPEEDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->OSPEEDR |= (((UNSIGNED32)0x03) << (pinIndex * 2)); } // OSPEEDR = 3

void disablePullUpAndPullDown() { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x00) << (pinIndex * 2)); } // PUPDR = 0
void enablePullUp() { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x01) << (pinIndex * 2)); } // PUPDR = 1
void enablePullDown() { port->PUPDR &= ~(((UNSIGNED32)0x03) << (pinIndex * 2)); port->PUPDR |= (((UNSIGNED32)0x02) << (pinIndex * 2)); } // PUPDR = 2

inline void setToHigh() __attribute__((always_inline)) { port->BSRRL = pinMask; }
inline void setToLow() __attribute__((always_inline)) { port->BSRRH = pinMask; }
inline bool isHigh() __attribute__((always_inline)) { return (((port->IDR) & pinMask) != 0); }
inline bool isLow() __attribute__((always_inline)) { return (((port->IDR) & pinMask) == 0); }
inline void toggle() __attribute__((always_inline)) { port->ODR ^= pinMask; }

void lock()
{
// TODO
}

protected:
volatile GPIO_TypeDef * port;
volatile UNSIGNED8 portIndex;
volatile UNSIGNED8 pinIndex;
volatile UNSIGNED32 pinMask;
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <PIN pin>
class Pin : public PinBase
{
public:
Pin() : PinBase(pin) { }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <PIN pin>
class DigitalOutputPin : public PinBase
{
public:
DigitalOutputPin() : PinBase(pin)
{
setAsDigitalOutput();
setAsPushPull();
setOutputSpeed40MHz();
}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <PIN pin>
class LedOpenDrain : public PinBase
{
public:
LedOpenDrain() : PinBase(pin)
{
setAsDigitalOutput();
setAsOpenDrain();
setOutputSpeed10MHz();
off();
}

void on() { setToLow(); }
void off() { setToHigh(); }
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <PIN pin>
class DigitalInputPin : public PinBase
{
public:
DigitalInputPin() : PinBase(pin)
{
setAsDigitalInput();
}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <PIN pin>
class AnalogInputPin : public PinBase
{
public:
AnalogInputPin() : PinBase(pin)
{
setAsAnalogInput();
}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Пример

Некоторые моменты сыроваты (в частности сохранились куски кода для SPL), но функционал полностью соответствует моим хотелкам.
В процессе участия в этой теме у меня появились еще задумки как что исправить/улучшить/проверить )))
Reflector
Цитата(juvf @ Apr 19 2017, 16:39) *
наверно можно гипотетический пример придумать такой... но
#define csOn() (GPIOA->BSRR = (1<<4))

написал за несколько секунд. отлаживать не надо, в использовании csOn(); Теперь какой можно написать класс... гораздо быстрее, который будет столь же эффективней и при этом будет ГОРАЗДО читаемый?

Здорово, но раз есть csOn(), то как минимум должен быть и csOff(), потом захочется записать битик и дополнительно придется делать проверки... А инициализация, она ведь тоже нужна? Итого сколько всего будет таких макросов для одного пина?

Цитата(Forger @ Apr 19 2017, 17:43) *
В процессе участия в этой теме у меня появились еще задумки как что исправить/улучшить/проверить )))

В качестве одной из таких задумок рекомендую выбросить из класса включение тактирования порта при каждой инициализации пина, заодно и от SPL избавишься sm.gif
Forger
Цитата(Reflector @ Apr 19 2017, 17:59) *
В качестве одной из таких задумок рекомендую выбросить из класса включение тактирования порта при каждой инициализации пина, заодно и от SPL избавишься sm.gif

Пока что так мне удобнее - тактирование включается автоматом. Обращение к SPL заменю, как руки дойдут ))
Делается это все равно лишь на этапе вызова конструктора класса пина, т. е. один раз после подачи питания.
Если не используется ни одного пина из некого порта, то он и не тактируется.
В таком случае не приходится искать какие порты используются, а какие нет.
Раньше приходилось тактировать ВСЕ порты.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.