Удобная настройка GPIO STM32F030. Мой велосипед.

Долгое время я настраивал порты различных микроконтроллеров STM32 при помощи Standart Peripheral Library.

GPIO_InitTypeDef gpio_struct;
gpio_struct.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_8;
gpio_struct.GPIO_Mode = GPIO_Mode_Out_PP;
gpio_struct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOC , &gpio_struct);


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

GPIOA->MODER |= GPIO_MODER_MODER15_0;	// LCD_RS PA15
GPIOA->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR15;
GPIOB->MODER |= GPIO_MODER_MODER3_0;	// LCD_CS PB3
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR3;
GPIOB->MODER |= GPIO_MODER_MODER4_0;	// LCD_WR PB4
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR4;
GPIOB->MODER |= GPIO_MODER_MODER5_0;	// LCD_RST PB5
GPIOB->OSPEEDR |= GPIO_OSPEEDR_OSPEEDR5;


Но в один прекрасный момент мне надоели эти непонятные записи. Магические, ни о чём не говорящие числа… Код становится нечитаемым, а если надо изменить настройки одной ноги, то надо открывать даташит… Погуглил, наткнулся на тему, в которой описывалась моя проблема. Но предложенные там реализации кода мне не очень понравились.
Например, строчка которая ввела меня в ступор:

#define GPIO_MODE_INPUT_PULL_UP_DOWN


Или эти магические числа:

#define GPIO_MODE_OUTPUT2_ALT_OPEN_DRAIN
#define GPIO_MODE_OUTPUT10_OPEN_DRAIN
#define GPIO_MODE_OUTPUT50_PUSH_PULL


А уж не говоря про это, непонятно зачем вставленное

#define GPIO_MODE_RESERVED


Вобщем я перечитал тему со всеми комментариями, и решил изобрести свой велосипед, на основе предложенных там примеров. Так скажем, с каждого примера взял чуть по чуть. Может кому-то понравится мой вариант — буду допиливать. На данный момент можно настраивать только пины по отдельности, но могу допилить настройку пинов по маске.
Итак, мой велосипед:

// Режим работы
#define GPIO_MODE_INPUT      0UL
#define GPIO_MODE_OUTPUT     1UL
#define GPIO_MODE_ALTFUNC    2UL
#define GPIO_MODE_ANALOG     3UL

// Тип выхода
#define GPIO_OUTTYPE_NONE    0UL   // заглушка
#define GPIO_OUTTYPE_PP      0UL
#define GPIO_OUTTYPE_OD      1UL

// Подтяжка
#define GPIO_PULL_NONE       0UL
#define GPIO_PULL_UP         1UL
#define GPIO_PULL_DOWN       2UL

// Скорость
#define GPIO_SPEED_LOW       0UL
#define GPIO_SPEED_MED       1UL
#define GPIO_SPEED_HI        3UL

// Альтернативные функции
#define GPIO_ALTFUNC_NONE    0UL   // заглушка

// Макрос инициализации
#define GPIO_INIT(GPIO_PORT,GPIO_PIN,GPIO_MODE,GPIO_OUTTYPE,GPIO_PULL,GPIO_SPEED,GPIO_ALTFUNC)\
{\
	GPIO_PORT->MODER = (GPIO_PORT->MODER & ~(3UL << (GPIO_PIN * 2UL)))\
	| (GPIO_MODE << (GPIO_PIN * 2UL));\
	GPIO_PORT->OTYPER = (GPIO_PORT->OTYPER & ~(1UL << (GPIO_PIN)))\
	| (GPIO_OUTTYPE << (GPIO_PIN));\
	GPIO_PORT->OSPEEDR = (GPIO_PORT->OSPEEDR & ~(3UL << (GPIO_PIN * 2UL)))\
	| (GPIO_SPEED << (GPIO_PIN * 2UL));\
	GPIO_PORT->PUPDR = (GPIO_PORT->PUPDR & ~(3UL << (GPIO_PIN * 2UL)))\
	| (GPIO_PULL << (GPIO_PIN * 2UL));\
	GPIO_PORT->AFR[GPIO_PIN / 8] = (GPIO_PORT->AFR[GPIO_PIN / 8] & ~(15UL << ((GPIO_PIN & 0x7) * 4)))\
	| ((GPIO_ALTFUNC) << (GPIO_PIN & 0x7) * 4);\
}

// Макросы для работы с ножкой порта
#define GPIO_SET(GPIO_PORT,GPIO_PIN) (GPIO_PORT->BSRR = (1 << GPIO_PIN))
#define GPIO_RESET(GPIO_PORT,GPIO_PIN) (GPIO_PORT->BRR = (1 << GPIO_PIN))
#define GPIO_TOGGLE(GPIO_PORT,GPIO_PIN) (GPIO_PORT->BSRR = (1 << (GPIO_PIN+ ((GPIO_PORT->ODR & (1 << GPIO_PIN))?16:0)
#define GPIO_ISSET(GPIO_PORT,GPIO_PIN) (GPIO_PORT->IDR & (1 << GPIO_PIN))
#define GPIO_ISRESET(GPIO_PORT,GPIO_PIN) (!(GPIO_PORT->IDR & (1 << GPIO_PIN)))

// Макросы для работы с портом
#define GPIOS_SET(GPIO_PORT,GPIO_MASK) (GPIO_PORT->BSRR = GPIO_MASK)
#define GPIOS_RESET(GPIO_PORT,GPIO_MASK) (GPIO_PORT->BRR = GPIO_MASK)
#define GPIOS_TOGGLE(GPIO_PORT,GPIO_MASK) (GPIO_PORT->ODR ^= GPIO_MASK)

// Примеры инициализации
// GPIO_INIT(GPIOA, 0, GPIO_MODE_OUTPUT, GPIO_OUTTYPE_PP, GPIO_PULL_NONE, GPIO_SPEED_HI, GPIO_ALTFUNC_NONE);
// GPIO_INIT(GPIOA, 1, GPIO_MODE_INPUT, GPIO_OUTTYPE_NONE, GPIO_PULL_UP, GPIO_SPEED_HI, GPIO_ALTFUNC_NONE);


Делалось под STM32F030, для других МК возможно понадобятся исправления.
Замечания пишите в комментариях. Приветствуется любая адекватная критика.
  • +5
  • 05 апреля 2015, 22:13
  • Zlodey

Комментарии (125)

RSS свернуть / развернуть
Эмм, может быть чего-то не понимаю, но:

...
REG=0xDEADBEEF;    //магическое число, хрен знает что происходит при его записи
...
#define GPIO_MODE_OUTPUT50_PUSH_PULL    0xB00BBABE
REG=GPIO_MODE_OUTPUT50_PUSH_PULL;        //запись ясно дающая понять, что мы переводим портВВ 
                                        //в режим выхода пуш-пулл с максимальным быстродействием 50МГц
0
К чему относится цифра 50 не понятно. К частоте 50 кГц? или 50 Гц? Может быть это порт под номером 50?
0
К тому же у разных МК градации скорости разные. Поэтому конструкции вида

#define GPIO_SPEED_LOW       0UL
#define GPIO_SPEED_MED       1UL
#define GPIO_SPEED_HI        3UL


делают код более универсальным
0
Ну то что определение имеет неудачное название, не делает его магическим числом ведь. Ну а если речь про универсальность, тогда соглашусь, что такие конструкции корректнее.
0
ну если дело только в этом, то можно и допилить
GPIO_MODE_OUTPUT_50MHZ_PUSH_PULL
0
Получается писанина, равносильная моей. Отличие в том, что у меня все параметры настраиваются раздельно, а у вас кучей. Ну и ещё у вас скорость привязана к конкретному камню.
0
по поводу скорости: Мне гораздо важнее, какую скорость нога выдает, чем ее абстрактное описание LOW,MEDIUM,HIGH. К примеру, мне нужно чтобы порт был со скоростью 10МГц. (вообще вся эта штука придумана исключительно с целью экономии питания). И что мне писать? LOW? MEDIUM? HIGH?.. а в STM32F4xx там есть еще и FAST. Насколько это разумно?
0
к примеру для STM32f4xx — это 50МГц — это FAST, для STM32F10x — это HIGH
Как говорится,
вам шашечки или ехать?
0
Именно в этом и дело, эту фишку мало кто использует! Поэтому абстрактное значение MAX или HI гораздо проще запомнить, чем вспоминать сколько там мегагерц выдаст…
Я в своих проектах всегда ставлю скорость на максимум, а сколько там мегагерц — не имеет значения. Бесполезные цифры, так скажем.
0
Тело макроса GPIO_INIT_PIN лучше заключить в do{...}while(0), а не {...}.
+1
  • avatar
  • Vga
  • 05 апреля 2015, 22:37
Уже не в первый раз слышу такую рекомендацию. Для какой цели так делают?
И почему нельзя совсем без скобок написать?
0
Чтобы компилятор однозначно воспринимал ее как блок. Иначе сунув ее в логическую структуру получишь трешак если у тебя приоритеты на что то перетянутся.
0
Не совсем понимаю как и где можно получить трешак?
0
При обрамлении только в фигурные скобки при подстановке тела макроса в некоторые места будет получаться некорректный код. ЕМНИП, в конструкции
if(...)
  GPIO_INIT_PIN(...);
else
  ...
0
А, речь о таких местах. Я просто всегда ставлю фигурные скобки, даже если используется одно выражение. Поэтому и сначала не понял в чем соль.
0
Макросы из одного выражения обычно в круглые скобки берутся. Этого достаточно для корректного разворачивания в любом окружении. А вот макросы в фигурных скобках корректно разворачиваются не везде.
0
Здесь указана ссылка на мою статью, поэтому попробую взять реванш). По мне, так писанина
GPIOA, 1, GPIO_MODE_INPUT, GPIO_OUTTYPE_NONE, GPIO_PULL_UP, GPIO_SPEED_HI, GPIO_ALTFUNC_NONE
гораздо запутаннее. Толи дело: ввел одну константу начинающуюся на GPIO_MODE… и забыл.
Второе: Данный макрос был написан для серии stm32f10x, там настройки совершенно по иному выглядят, поэтому целесообразней их делать так, как у меня.
Третье:
GPIO_PORT->MODER &= ~(3UL << (GPIO_PIN * 2UL));\
GPIO_PORT->MODER |= (GPIO_MODE << (GPIO_PIN * 2UL));\
Почему бы не записать в одну операцию Чтение/модификация/запись?
GPIO_PORT->MODER = GPIO_PORT->MODER & ~(3UL << (GPIO_PIN * 2UL)) | (GPIO_MODE << (GPIO_PIN * 2UL));
Неужели не ясно, что порт настраивается в промежуточные положения в момент настройки? В каком режиме он окажется, когда отработает первая настройка?
0
Насчёт одной операции Ч/М/З — спасибо за важное замечание. Позволяет уменьшить объём кода, и избежать промежуточных режимов работы порта. Вечером исправлю.

А вот писанина как раз и нужна для того, чтобы чётко, ясно, и отдельно видеть все настройки. Мне например удобнее настраивать каждый параметр отдельно, а не всё в кучу. Меняется только визуальное восприятие, на размер кода не влияет, поэтому не страшно.
0
да, главное, только не перепутать эти настройки в описании, а то мало ли чего выйдет?))
И все же, обратите внимание, что у вас много регистров настройки, один отвечает за скорость, другой за режим, третий альтернативные функции и так далее… А в stm32f10x на все про все одна пара регистров. Там все упаковано максимально возможно. Отсюда и логика: Один регистр настройки — одно GPIO_MODE… описание.
0
STM32F10x не использую, т.к. не нравится как там реализованы некоторые узлы. Например, RTC реализовано через попу. Настройка GPIO тоже сделана не очень удобно. Вобщем древние чипы.
0
Ещё регистр GPIOx_BSRR на STM32F10x реализован через попу.
0
работал долго с этой серией, никаких проблем с регистром BSRR не испытывал.
0
С регистром BSRR я немного попутал, а вот RTC действительно реализован через попу
0
Мне, к примеру, от RTS нужен только uint32_t time_t, который считает количество секунд. Без него реально не удобно.
0
Не атомарно это:
#define GPIO_TOGGLE(PORT,PIN) (PORT->ODR ^= (1 << PIN))

замени на
#define GPIO_TOGGLE(PORT,PIN) (PORT->BSRR = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)

Это атомарно.
Работает так: мы присваевем регистру BSRR 1 с сдвигом на пин + 16, если соответствующий пин выставлен в ODR, или на пин, если
не выставлен.
0
ваш вариант гораздо лучше, тоже хотел указать на это, однако атомарным его нельзя называть, поскольку используется тот же принцип ЧМЗ. Весомый плюс — не затрагивает другие выводы порта, кроме изменяемого.
0
Для изменяемого порта — не атомарно, для регистра — атомарно.
Обычно, под ЧМЗ подразумевают работу с регистром. Тут нету модификации — точлько чтение из одного регистра и запись в другой.
0
если запись в другой регистр влияет на источник, то такую конструкцию приемлемо приравнять к ЧМЗ. По сути, это и происходит.
+1
вообще надо взять за правило — не пользоваться ODR! Все можно сделать с помощью регистра BSRR (в STM32F4xx.h почему-то он описан как два 16-ти битных BSRRL и BSRRH. Тупо.)
например, выставить значения надо в 8-ми битную шину GPIOA.0 — GPIOA.7:
GPIOA->BSRR = (data & 0xFF) | (((~data)&0xFF) << 16);
0
ODR нужен для чтения. Запись в него — это да, порочно.
0
ну да, про запись и имелось ввиду))
0
Через ODR будет на 6 тактов быстрее. Если это критично, то всё-таки он.
0
Спасибо за важные замечания!
Эта конструкция
#define GPIO_TOGGLE(PORT,PIN) (PORT->BSRR = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)

не заработает на STM32F030, там регистры битбэнда реализованы по другому
0
возможно, там имеется та же проблема: BSRR расшит на два BSRRH и BSRRL
В этом случае поможет несложный хак:
#define GPIO_TOGGLE(PORT,PIN) ((*(uint32_t*)&PORT->BSRRL) = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)
0
#define GPIO_TOGGLE(PORT,PIN) ((*(volatile uint32_t*)&PORT->BSRRL) = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)

так правильнее
0
Тьфу, я вру. С регистром BSRR будет работать. Почему-то думал, что регистр BSRR устанавливает биты, а регистр BRR сбрасывает. Оказалось что BSRR умеет и устанавливать и сбрасывать, так что всё ОК!
0
Это только в дефайнах. В датащите всё по-старому.
0
да. Именно поэтому и использую этот хак без зазрения совести))
0
нет. Только что прочитал мануал.
Вот в 40х всё описано действительнь по другому. И там надо условие в lvalue перенести
((PORT->ODR & (1 << PIN))?PORT->BRR:PORT->BSR) = (1 << PIN)
Но там есть нормальный Bit Banding, и можно просто воспользоваться им.
0
И там надо условие в lvalue перенести
Не нужно. Описанный мною выше хак прекрасно работает. Нет никаких препятствий в том, чтобы записать в регистр BSR 32-х битное значение по образу и подобию BSRR в stm32f10x. Видимо, кто-то решил «упростить» несложные операции по выставлению и сбросу битов. Если нужно одновременно установить и сбросить нужные биты — ваш способ окажется не оправданным
0
с битбандом красота! Однако, у всего есть свои минусы…
0
GPIO_INIT — исправил на «чтение/модификация/запись»
GPIO_TOGGLE — исправил на «битбэнд»

Ещё добавил:
GPIOS_SET
GPIOS_RESET
GPIOS_TOGGLE
GPIOS_ISSET
0
GPIO_INIT — исправил на «чтение/модификация/запись»
исправили некорректно.
всюду &= |=, а их в принципе не должно быть))
0
туплю, поправил
0
GPIOS_TOGGLE почему бы не сделать по тому же принципу, что и GPIO_TOGGLE?
__STATIC_INLINE GPIOS_TOGGLE(GPIO_TypeDef *GPIO, uint16_t MASK)
{
    uint32_t GPIO_ODR = GPIO->ODR;
    GPIO->BSRR = (~GPIO_ODR & MASK) | ((GPIO_ODR & MASK) << 16);
}
0
в этом случае незадействованные биты свободны от ЧМЗ
0
сомнительна необходимость макроса GPIOS_ISSET
0
Согласен, но неиспользуемые макросы на размер программы не влияют.
0
как и надпись
#define GPIO_MODE_RESERVED

1:1
0
Все же, коллеги, почему макросы а не энамы и инлайн фикции?
Единственный довод в пользу макросов – существуют древние компиляторы, которые эти сами энамы и инлайны не поддерживают. Но разве это актуально (для данной, STM32, платформы)?
+3
согласен отчасти. Иногда эти ну слишком умные компиляторы не хотят инлайнить функции по своему, неведомо никому усмотрению. А это не по феншую, когда выходит как в рекламе билайна:
— А я не это загадывал!
— А сбылось это!!!
+1
Иногда эти ну слишком умные компиляторы не хотят инлайнить функции по своему, неведомо никому усмотрению.

С макросами может произойти обратное, сначала препроцессор сделает подстановку макросов, потом «слишком умный компилятор» увидит повторяющийся однотипный фрагмент кода и вынесет его в «подпрограмму»

Макросы, хоть и похожи по синтаксису на функции, ими не являются, стоит об этом забыть, и невинный макрос
#define MIN(a,b) ((a>b)?b:a)

при определенных условиях становится жутким и трудно выявляемым багом
int I = MIN(ReadADC(), 100);


Не говоря о других проблемах, типа синтаксиса и типобезопасности.
+1
При использовании макросов покачто на грабли не натыкался, а вот с темой «слишком умного компилятора» уже знаком))) Такчто я за дефайны.
0
Ну, Вам решать.

Мой опыт диаметрально противоположен (и пример с MIN(ReadADC(), 100)
я привел из своего опыта :)

а вот с темой «слишком умного компилятора» уже знаком
С чем именно вы столкнулись? При включенной оптимизации компилятор не инлайнил функцию но инлайнил аналогичный макрос?
0
инлайновая функция это вообще болезнь всех умных компиляторов. Я считаю, одна единственная уважительная причина игнорировать это требование — рекурсивный вызов.
А вот вынос однотипного кода в подпрограмму — с таким не встречался. возможно и такое имеется, не спорю.
0
инлайновая функция это вообще болезнь всех умных компиляторов. Я считаю, одна единственная уважительная причина игнорировать это требование — рекурсивный вызов.

Но совсем понял. В чем проблема с инлайн функцией и как это связанно с рекурсией?
0
я имел ввиду, если прогер пишет inline — значит inline. Понятное дело, что если эта функция вызывает сама себя — то инлайнить ее нельзя.
0
я имел ввиду, если прогер пишет inline — значит inline.
Забавней то, что есть аттрибут forceinline — и компилятор его так же может проигнорировать)
+1
а есть еще аттрибут always_inline Как с этим дело обстоит?
0
Да, по сути, те же яйца только в профиль, компилятор должен инлайнить но нет 100% гарантии.

Как по мне, проблема с тем, что компилятор может не заинлайтить функцию – сильно преувеличена. Как я мы знаем, компилятор может делать достаточно много вещей для оптимизации кода, единственное он должен – соответствовать стандарту. В случае с макросами – стандарт гарантирует только то, что препроцессор выполнит замену…

Практически, я не помню случаев, чтобы (при включенной оптимизации) компилятор вел себя неадекватно и не инлайнил мелкие функции. Зато у инлайн функций есть куча плюсов: привычный синтаксис, поведение (как и в «обычных» функциях), контроль типов. Уже давно считается хорошей практикой переход от макросов к инлайн. Возьмем, для примера, Linux code style:

Enums are preferred when defining several related constants.

Generally, inline functions are preferable to macros resembling functions
0
если прогер пишет inline — значит inline
Ну, это решили отдать на откуп компилятору (хотя да, согласен, не очень логично)

Но с макросами та же проблема — единственное что мы знаем – что препроцессор выполнит замену. Что будет дальше – зависит от компилятора.
+1
нельзя … кроме случаев хвостовой рекурсии, когда можно :)

Вот только кто бы это ещё поддерживал. gcc точно нет. Или уже да?
0
Вот только кто бы это ещё поддерживал. gcc точно нет. Или уже да?

Сомневаюсь :) Оптимизация рекурсии – не самая типичная задача для структурных ЯП (в отличии от функциональных). А что у Вас за задача, которая требует рекурсивного вызова инлайн функций?
0
У меня нет такой задачи на С, особенно для µC :)
Просто, глаз зацепился и захотелось возразить по умному, простите :)

Гугление, кстати, показывает, что в случае не-инлайна gcc таки умеет. А вот совмещать — не знаю.
0
Хотя … возможно я и поспешил с ответом. Современные ЯП (ну, или по крайней пере известные мне С++ и С#) активно впитывают концепции других парадигм.

Например в С# есть ООП (это понятно), АОП (атрибуты), функциональное программирование (лямбды, замыкания) отсутствие типизации (динамические типы). В контексте появления лямбд и замыканий в С++, вполне возможно, что дело дойдет до оптимизации хвостовой рекурсии.
0
В каком-то из компиляторов видел оптимизацию хвостовой рекурсии в списке опций. Кажется, Keil, но не уверен.
0
по вашему же примеру использование макроса MIN универсально для всех типов данных, целых, вещественных числах и даже строках и структурах. А вот если делать инлайн функцию — требуется либо перегружаемые функции (если стандарт позволяет), либо бесчисленное их множество. Да, у макросов есть ограничения. По хорошему, эффективно это работает, когда в них пишутся константы или переменные, но никак не вызов функций
0
по вашему же примеру использование макроса MIN универсально для всех типов данных, целых, вещественных числах и даже строках и структурах.

Ну, насчет строк и структур Вы перегнули (если мы не говорим о С++ с перегрузкой операторов сравнения) :) И это, как раз, может стать причиной ошибки из-за отсутствия контроля типов.
0
Я считаю, что надо послушать e_mc2 и писать для MCU на C99, а не скатываться постоянно к ветхозаветному коду ANSI/C89 из-за весьма сомнительных плюсов якобы какой-то совместимости(с чем?). Тем более, что большая тройка компиляторов для MCU его(C99) поддерживает практически полностью, тем более, если речь идет об ARM.
+1
Я считаю, что надо послушать e_mc2

Странно, вы поддержали мое мнение сразу в 2-х топиках :) Это точно старый-добрый well-man2000 или у Вас аккаунт увели?
0
Это же совпадение мнений по технич.вопросам!
0
инлайн фикции
Только что заметил сей подвыподверт)) Определение довольно точное)
0
Для чего выдумывать самостийные имена?
Если бы я был параноиком, то я бы написал «графическо-табличный интерактивный макрос» типа Куба, который бы делал исходники в регистрах :)
0
Дак в названии темы ведь написано — велосипед. Поэтому и воспринимать содержимое надо соответствующим образом :)
0
Тогда уж просто это колесо без осей и рам… так поделка на раз.
И главное — в Кубе уже всё что нужно есть: имена, фамилии, явки, фотки…
C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeMX\db\mcu\IP
- <Pin Name="PA7" Position="13" Type="I/O">
  <Signal Name="ADC_IN7" /> 
  <Signal Name="SPI1_MOSI" /> 
  <Signal Name="TIM14_CH1" /> 
  <Signal Name="TIM17_CH1" /> 
  <Signal Name="TIM1_CH1N" /> 
  <Signal Name="TIM3_CH2" /> 
  <Signal IOModes="Input,Output,Analog,EVENTOUT,EXTI" Name="GPIO" /> 
  </Pin>
0
А мне зачем все эти таймеры переписывать и раздувать свой говнокод? В этом и преимущество велосипеда перед всякими SPL, HAL, Cube… Простота и ничего лишнего.
0
Для чего выдумывать самостийные имена?
Конечно же не надо. Зачем определять по своему еще раз то, что УЖЕ ОПРЕДЕЛЕНО стандартно производителем в своей мегапортянке (\Drivers\CMSIS\Device\ST\STM32F3xx\Include\stm32f30xxx.h) специально для людей и отрасли(экосистемы потребителей STM32)?

Но что вовсе не мешает определить кое-что для удобства в своей прокладке на своем личном application уровне, используя стандартные имена из этой самой мегапортянки, например:
Led1 =…
PWM_motor1 =…
Display_SPI_MOSI =…
0
Еще одно замечание по теме:
Я бы регистр MODER настраивал последним.
Сперва сконфигурировал все — а потом — бац! И сразу он молодец!
Например, если это I2C, то в момент настройки из дефайна в рабочий режим будет сперва настроено, что альтернатива, потом подтяжка, потом OPEN_DRAIN и HIGH а только потом — выход альтернативы. Красиво же, не так ли?
0
Свое всегда роднее, и это понятно. Прочитав обе «конкурирующие» темы, больше понравился способ Mihail'a: во-первых, возможностью групповой настройки выводов; во-вторых, процесс написания кода удобней/быстрей. Один раз набрал GPIO_MODE_..., выскочила подсказка, выбрал нужное. В вашем случае для инициализации одного вывода после названия функции набираем GPIO_MODE_..., GPIO_TYPE_..., GPIO_PULL_..., GPIO_SPEED_..., GPIO_ALTFUNC_… Представил процесс настройки сотни ног МК, и как-то грустно стало. Копипаст конечно поможет, но все же.
На мой взгляд, недостаток обоих способов — это работа только с портами. А если нужна другая периферия? Использовать регистры? SPL? Во втором случае велосипеды не нужны. Да и в первом можно обойтись. Для проектов на STM у меня есть сопутствующие проекты MicroXplorer (ныне куб), где визуально сразу видно, как настроена каждая нога, и это удобней, чем смотреть на портянки GPIO_INIT. Мне, например, лениво настраивать тот же UART через регистры. Подключаю SPL, функции передачи/приема данных свои, на регистрах. Соответственно, порты настраиваю через SPL. Но в программе использую BSRR. Получается, оба велосипеда как бы особо и не нужны. Но если к ним добавить инициализацию хотя бы UART, TIM, SPI, I2C, ADC, то это будет очень неплохая альтернатива SPL. По-крайней мере я бы задумался об ее использовании, т.к. чаще всего нужно вышеперечисленное.
Это все так, рассуждения. Как говорится, чисто мое мнение)
0
Лениво ему UART настраивать…

#define USART1_PORT GPIOA
#define USART1_TX 2
#define USART1_RX 3
#define USART1_BAUD 9600UL
#define USART1_CLOCK 48000000UL
USART1->BRR = (u32)(USART1_CLOCK / USART1_BAUD);
USART1->CR1 |=
(USART_CR1_UE       * 1)|	// USART enable
(USART_CR1_TE       * 1)|	// Transmitter enable
(USART_CR1_RE       * 1);	// Receiver enable


Ну а вообще — каждый выбирает своё. Кому-то достаточно DS+RM, кому-то Нужно уже DS+RM+SPL+Cube

:-)
0
USART_InitTypeDef usart;
usart.USART_BaudRate            = 9600;
usart.USART_Mode                = USART_Mode_Rx | USART_Mode_Tx;
usart.USART_WordLength          = USART_WordLength_8b;
usart.USART_Parity              = USART_Parity_No ;
usart.USART_StopBits            = USART_StopBits_1;
usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_Init(USART1, &usart);
USART_Cmd(USART1, ENABLE); 
Я не против регистров, пожалуй даже за, но не в инициализации периферии. Наглядная инициализация сильно уменьшает количество проблем в будущем.
0
а я вот так упростил
user.c

#define GainMute B, 6, OUT_PP
#define GainOn B, 6, IN_A

...

GIP(GainOn);
GPIO_SET_(LED_Din);

GIP(GainMute);

gpio_init_f0xx.h
....
#define OUT_PP 0x101
#define OUT_OD 0x201
#define OUT_OD_PU 0x202
#define IN_A 0x303
#define IN_F 0x000
#define IN_PU 0x04
#define IN_PD 0x05
#define AF 0x06
#define AF_PP 0x07
#define AF_OD 0x08
...
// Макросы для работы с ножкой порта
#define GPIO_SET(GPIO_PORT,GPIO_PIN) (GPIO_PORT->BSRR = (1 << GPIO_PIN))

#define _GPIO_SET(GPIO_PORT, GPIO_PIN, NONE) (GPIO##GPIO_PORT->BSRR = (1 << GPIO_PIN))
#define GPIO_SET_(GPIO_SET) _GPIO_SET(GPIO_SET)

#define _GPIO_RESET(GPIO_PORT, GPIO_PIN, NONE) (GPIO##GPIO_PORT->BRR = (1 << GPIO_PIN))
#define GPIO_RESET_(GPIO_SET) _GPIO_RESET(GPIO_SET)

#define _GPIO_Read(GPIO_PORT, GPIO_PIN, NONE) ((GPIO##GPIO_PORT->IDR) & (1<<(GPIO_PIN)))>0? 1: 0
#define GPIO_Read(GPIO_SET) _GPIO_Read(GPIO_SET)


#define GPIO_CONFs(PORT, pin, mode)\
{\
if(mode==OUT_PP) GPIO_INIT(GPIO##PORT, pin, OUT, PP, NONE, HI, NONE) \
if(mode==OUT_OD) GPIO_INIT(GPIO##PORT, pin, OUT, OD, NONE, HI, NONE) \
if(mode==OUT_OD_PU) GPIO_INIT(GPIO##PORT, pin, OUT, OD, UP, HI, NONE) \
if(mode==IN_F) GPIO_INIT(GPIO##PORT, pin, IN, NONE, NONE, HI, NONE) \
if(mode==IN_PU) GPIO_INIT(GPIO##PORT, pin, IN, NONE, UP, HI, NONE) \
if(mode==IN_PD) GPIO_INIT(GPIO##PORT, pin, IN, NONE, DOWN, HI, NONE) \
if(mode==IN_A) GPIO_INIT(GPIO##PORT, pin, AN, NONE, NONE, HI, NONE) \
}
#define GIP(PORT_CONF) GPIO_CONFs(PORT_CONF)
По крайне мере работать стало удобней и на много быстрей
0
Для кода есть специальный тег <code>, тогда код не бьется и не сливается с текстом комментария.
0
конешно спешыл не оптимизировал, надо было быстро, потом допилю
0
спасибо попробую)))
0
а я вот так упростил
user.c

#define GainMute B, 6, OUT_PP
#define GainOn B, 6, IN_A

...

GIP(GainOn);
GPIO_SET_(LED_Din);
…
GIP(GainMute);


gpio_init_f0xx.h

//... остальной код
#define OUT_PP 0x101
#define OUT_OD 0x201
#define OUT_OD_PU 0x202
#define IN_A 0x303
#define IN_F 0x000
#define IN_PU 0x04
#define IN_PD 0x05
#define AF 0x06
#define AF_PP 0x07
#define AF_OD 0x08

//... 

// Макросы для работы с ножкой порта
#define GPIO_SET(GPIO_PORT,GPIO_PIN) (GPIO_PORT->BSRR = (1 << GPIO_PIN))

#define _GPIO_SET(GPIO_PORT, GPIO_PIN, NONE) (GPIO##GPIO_PORT->BSRR = (1 << GPIO_PIN))
#define GPIO_SET_(GPIO_SET) _GPIO_SET(GPIO_SET) 

#define _GPIO_RESET(GPIO_PORT, GPIO_PIN, NONE) (GPIO##GPIO_PORT->BRR = (1 << GPIO_PIN))
#define GPIO_RESET_(GPIO_SET) _GPIO_RESET(GPIO_SET) 

#define _GPIO_Read(GPIO_PORT, GPIO_PIN, NONE) ((GPIO##GPIO_PORT->IDR) & (1<<(GPIO_PIN)))>0? 1: 0
#define GPIO_Read(GPIO_SET) _GPIO_Read(GPIO_SET)

//…
#define GPIO_CONFs(PORT, pin, mode)\
{\
if(mode==OUT_PP) GPIO_INIT(GPIO##PORT, pin, OUT, PP, NONE, HI, NONE) \
if(mode==OUT_OD) GPIO_INIT(GPIO##PORT, pin, OUT, OD, NONE, HI, NONE) \
if(mode==OUT_OD_PU) GPIO_INIT(GPIO##PORT, pin, OUT, OD, UP, HI, NONE) \
if(mode==IN_F) GPIO_INIT(GPIO##PORT, pin, IN, NONE, NONE, HI, NONE) \
if(mode==IN_PU) GPIO_INIT(GPIO##PORT, pin, IN, NONE, UP, HI, NONE) \
if(mode==IN_PD) GPIO_INIT(GPIO##PORT, pin, IN, NONE, DOWN, HI, NONE) \
if(mode==IN_A) GPIO_INIT(GPIO##PORT, pin, AN, NONE, NONE, HI, NONE) \
} 
#define GIP(PORT_CONF) GPIO_CONFs(PORT_CONF)


По крайне мере работать стало удобней и на много быстрей
0
Вот эта простыня:
#define GPIO_CONFs(PORT, pin, mode)\
{\
if(mode==OUT_PP) GPIO_INIT(GPIO##PORT, pin, OUT, PP, NONE, HI, NONE) \
if(mode==OUT_OD) GPIO_INIT(GPIO##PORT, pin, OUT, OD, NONE, HI, NONE) \
if(mode==OUT_OD_PU) GPIO_INIT(GPIO##PORT, pin, OUT, OD, UP, HI, NONE) \
if(mode==IN_F) GPIO_INIT(GPIO##PORT, pin, IN, NONE, NONE, HI, NONE) \
if(mode==IN_PU) GPIO_INIT(GPIO##PORT, pin, IN, NONE, UP, HI, NONE) \
if(mode==IN_PD) GPIO_INIT(GPIO##PORT, pin, IN, NONE, DOWN, HI, NONE) \
if(mode==IN_A) GPIO_INIT(GPIO##PORT, pin, AN, NONE, NONE, HI, NONE) \
} 

будет полностью присутствовать в коде при каждом упоминании GPIO_CONFs или GIP. Думаю, стоит заменить if-ы в этом коде на препроцессорные, тогда на каждый вызов будет вставляться ровно одна строка кода вместо всей простыни.
0
А не пофиг ли? Оптимизатор же выпилит. Правда, если таких макросов будет много, это отразится на скорости компиляции.
+1
Если выключить оптимизацию (например для отладки), то не выпилит и код может не влезть в чип. А в 030, особенно в мелких корпусах, памяти совсем не много. Так что лучше, все-таки, поправить. Хотя, конечно, хозяин барин…
0
То есть ты предлагаешь программисту выполнять работу оптимизатора? Я не ослышался?
-1
чет не могу найти пример который поможет мне исспользовать препроцесорные ифки, хоть один пример как его сдесь применить плииз
0
Спасибо за совет мне он помог лучше понимать процес, ну и полезно знать что оптимизатор может взять на себя процес оптимизации пока я учусь.
0
#define _bs(bits,n,m)  ((u32)(bits) << ((n) << ((m)-1)))
#define _nbs(bits,n,m) ((u32)~(_bs((bits),(n),(m))))

#define _rm11(r,b,n) (R) = ((R) & _nbs(1,n,1)) | _bs(b,n,1)
#define _rm12(r, b, n1,n2) (R) = ((R) & (_nbs(1,n1,1) & _nbs(1,n2,1))) | (_bs(b,n1,1) | _bs(b,n2,1))
#define _rm13(r, b, n1,n2,n3) (R) = ((R) & (_nbs(1,n1,1) & _nbs(1,n2,1) & _nbs(1,n3,1))) | (_bs(b,n1,1) | _bs(b,n2,1) | _bs(b,n3,1))
#define _rm14(r, b, n1,n2,n3,n4) (R) = ((R) & (_nbs(1,n1,1) & _nbs(1,n2,1) & _nbs(1,n3,1) & _nbs(1,n4,1))) | (_bs(b,n1,1) | _bs(b,n2,1) | _bs(b,n3,1) | _bs(b,n4,1))
#define _rm15(r, b, n1,n2,n3,n4,n5) (R) = ((R) & (_nbs(1,n1,1) & _nbs(1,n2,1) & _nbs(1,n3,1) & _nbs(1,n4,1) & _nbs(1,n5,1))) | (_bs(b,n1,1) | _bs(b,n2,1) | _bs(b,n3,1) | _bs(b,n4,1) | _bs(b,n5,1))

#define _rm21(r,b,n) (R) = ((R) & _nbs(3,n,2)) | _bs(b,n,2)
#define _rm22(r, b, n1,n2) (R) = ((R) & (_nbs(3,n1,2) & _nbs(3,n2,2))) | (_bs(b,n1,2) | _bs(b,n2,2))
#define _rm23(r, b, n1,n2,n3) (R) = ((R) & (_nbs(3,n1,2) & _nbs(3,n2,2) & _nbs(3,n3,2))) | (_bs(b,n1,2) | _bs(b,n2,2) | _bs(b,n3,2))
#define _rm24(r, b, n1,n2,n3,n4) (R) = ((R) & (_nbs(3,n1,2) & _nbs(3,n2,2) & _nbs(3,n3,2) & _nbs(3,n4,2))) | (_bs(b,n1,2) | _bs(b,n2,2) | _bs(b,n3,2) | _bs(b,n4,2))
#define _rm25(r, b, n1,n2,n3,n4,n5) (R) = ((R) & (_nbs(3,n1,2) & _nbs(3,n2,2) & _nbs(3,n3,2) & _nbs(3,n4,2) & _nbs(3,n5,2))) | (_bs(b,n1,2) | _bs(b,n2,2) | _bs(b,n3,2) | _bs(b,n4,2) | _bs(b,n5,2))

#ifdef __STM32F0

	inline void gpio_mode1(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1){ _rm21(GPIOx->MODER, mode, pin1);	}
	inline void gpio_mode2(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2){ _rm22(GPIOx->MODER, mode, pin1,pin2);	}
	inline void gpio_mode3(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3){ _rm23(GPIOx->MODER, mode, pin1,pin2,pin3);	}
	inline void gpio_mode4(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4){ _rm24(GPIOx->MODER, mode, pin1,pin2,pin3,pin4);	}
	inline void gpio_mode5(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4, u32 pin5){ _rm25(GPIOx->MODER, mode, pin1,pin2,pin3,pin4,pin5);	}

	inline void gpio_otype1(GPIO_TypeDef* GPIOx, GPIOOType_TypeDef mode, u32 pin1){ _rm11(GPIOx->OTYPER, mode, pin1); }
	inline void gpio_otype2(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2){ _rm12(GPIOx->OTYPER, mode, pin1,pin2);	}
	inline void gpio_otype3(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3){ _rm13(GPIOx->OTYPER, mode, pin1,pin2,pin3);	}
	inline void gpio_otype4(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4){ _rm14(GPIOx->OTYPER, mode, pin1,pin2,pin3,pin4);	}
	inline void gpio_otype5(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4, u32 pin5){ _rm15(GPIOx->OTYPER, mode, pin1,pin2,pin3,pin4,pin5);	}

	inline void gpio_ospeed1(GPIO_TypeDef* GPIOx, GPIOSpeed_TypeDef mode, u32 pin1){ _rm21(GPIOx->OSPEEDR, mode, pin1); }
	inline void gpio_ospeed2(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2){ _rm22(GPIOx->OSPEEDR, mode, pin1,pin2);	}
	inline void gpio_ospeed3(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3){ _rm23(GPIOx->OSPEEDR, mode, pin1,pin2,pin3);	}
	inline void gpio_ospeed4(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4){ _rm24(GPIOx->OSPEEDR, mode, pin1,pin2,pin3,pin4);	}
	inline void gpio_ospeed5(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4, u32 pin5){ _rm25(GPIOx->OSPEEDR, mode, pin1,pin2,pin3,pin4,pin5);	}

	inline void gpio_pupd1(GPIO_TypeDef* GPIOx, GPIOPuPd_TypeDef mode, u32 pin1){ _rm21(GPIOx->PUPDR, mode, pin1); }
	inline void gpio_pupd2(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2){ _rm22(GPIOx->PUPDR, mode, pin1,pin2);	}
	inline void gpio_pupd3(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3){ _rm23(GPIOx->PUPDR, mode, pin1,pin2,pin3);	}
	inline void gpio_pupd4(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4){ _rm24(GPIOx->PUPDR, mode, pin1,pin2,pin3,pin4);	}
	inline void gpio_pupd5(GPIO_TypeDef* GPIOx, GPIOMode_TypeDef mode, u32 pin1, u32 pin2, u32 pin3, u32 pin4, u32 pin5){ _rm25(GPIOx->PUPDR, mode, pin1,pin2,pin3,pin4,pin5);	}

	inline u32  gpio_get(GPIO_TypeDef* GPIOx, u32 pin){ return GPIOx->IDR & (1uL << pin); }
	inline void gpio_set(GPIO_TypeDef* GPIOx, u32 pin, u32 state){ if (state) GPIOx->BSRR = 1 << pin; else GPIOx->BRR = 1 << pin; }

#endif
0
Это пиздец.
0
опять вылез? лезь обратно.
-1
Ну где-то ATAMAH прав…
-1
обычный тролль-мудач@ок… пока не видно ни одного аргумента, или слабо предъявить свой мега-эффектичный код?
0
Аргументы… Видите ли, Ваш код несколько мутноват (имхо конечно).
Какие-то непонятно, зачем нужные, макросы (типа _bs, _nbs и прочих _rmxx), типы данных навроде u32, неужели стандартных uint32_t (и тому подобных) не хватает.
И самое главное непонятно где тут удобство настройки? Мозг вывихнешь пока поймёшь.
0
если ничего непонятно — зачем неаргуметрированное набрасывание г@вна?
без сравнения ASM-листингов, количества тактов и т.д.?
лично Вам неудобно и не нужно — просто проходите мимо…
0
если ничего непонятно — зачем неаргуметрированное набрасывание г@вна?
Код (по возможности) надо бы делать как можно более понятным, иначе замучаешься его поддерживать. Это в большинстве случаев сейчас важнее скорости исполнения и эффективности.
P.S. Имхо лучше делать через template — код и эффективный и понятный. Типа такого
+2
во-1х, откуда в С template?
во-2х — попробуй кодом по ссылке настроить например 4 вывода на выход, и посмотри на сгенерированный листинг…
0
во-1х, откуда в С template?
В Си нету. А что для ARM плюсов нет? Полно компиляторов и даже бесплатных.

во-2х — попробуй кодом по ссылке настроить например 4 вывода на выход, и посмотри на сгенерированный листинг…
Думаю всё нормально будет сгенерировано… Сейчас проверить не могу — на этом компе для ARM ничего не установлено.
+1
1. мне они не нужны, как и много кому еще…
2. вот тут сомневаюсь… даже не заглядывания в листинг — вангую что для каждого пина будет вызываться метод настройки.
по удобству — тоже не факт:
PA5::Mode(OUTPUT);
PA7::Mode(OUTPUT);
PA8::Mode(OUTPUT);


vs

gpio_mode3(GPIOA, GPIO_Mode_OUT, 5,7,8);
0
1. мне они не нужны, как и много кому еще…
Дело ваше. Но плюсы всёж удобнее.
вангую что для каждого пина будет вызываться метод настройки.
Как раз врядли — вызовов наверняка вообще не будет — все функции приинлайнятся.
+1
Попробуй это.

Впрочем, результат паршивый. Хотя оно и инлайнит, каждый пин выставляется отдельно. Можно было бы и получше сделать, у neiver'а все это оптимизировалось.
С другой стороны — согласен, читаемость на ARM'ах важнее в большинстве случаев.
0
Хотя оно и инлайнит, каждый пин выставляется отдельно.
Ну это естественно — регистры порта же volatile… Не может компилятор эти записи объединить.
С другой стороны — согласен, читаемость на ARM'ах важнее в большинстве случаев.
Ну да, зато понятно написано.
0
вот например настройка режима порта (любого количества пинов):

80025e6:	2290      	movs	r2, #144	; 0x90
 80025e8:	05d2      	lsls	r2, r2, #23
 80025ea:	6813      	ldr	r3, [r2, #0]
 80025ec:	4819      	ldr	r0, [pc, #100]	; (8002654 <ind_setup+0x70>)
 80025ee:	4d1a      	ldr	r5, [pc, #104]	; (8002658 <ind_setup+0x74>)
 80025f0:	4003      	ands	r3, r0
 80025f2:	432b      	orrs	r3, r5
 80025f4:	6013      	str	r3, [r2, #0]
0
комментарий был удален
комментарий был удален
(uint32_t) только лошки используют, давно пора переходить на (u32)
Вы бы говном не срали, а посмотрели как выглядит код:


uint32_t Sum;
u32 Sum;


Запись почти в 2 раза короче получается!
-1
(uint32_t) только лошки используют, давно пора переходить на (u32)
Вы так шутите?
Запись почти в 2 раза короче получается!
Не смешно.
P.S. Когда uint32_t пишешь, все буквы писать не надо (достаточно u3) — дальше эклипса допишет.
0
Когда u32 пишешь, ничего и дописывать не надо. Достаточно нажать:

u8,s8 - две клавиши
u32,s32 - три клавиши

При этом чётко ясно, что:

u - Unsigned
s - Signed

А ваши эти int32_t — прошлый век и не наглядно
-1
Когда u32 пишешь
Клавишь конечно меньше придётся нажать. Ну и что?
Зачем свои типы придумывать, если есть стандартные (к тому же более функциональные)?
+1
Тогда аргументируйте чем они функциональнее?
0
А s8 это тип знакового 8 бит, или это переменная для строки в 8й колонке? Все чётко.
-1
ну пиши uint32, если нравится, никто никому не навязывает. Вообще непонятно, к чему этот срач на ровном месте…
0
Запись почти в 2 раза короче получается!
Что, впрочем, не имеет абсолютно никакого значения.

Эти u32 в стандарте хоть?
-1
Нет, в стандарте uint32_t

Просто их стандартизировали «относительно недавно» в С99, до этого придумывали свои названия, которые закрепились в legacy code. В линуксовом ядре ЕМНИП до сих пор сосуществуют u32 и u_int32_t и uint32_t из стандарта. В Win32 – DWORD и т.д. Плюс дело привычки, я долго не мог заставить себя писать uint8_t вместо BYTE … :)
0
Сами типы данных прошиты в компиляторах Си и определяются через ключевые слова: «char», «short», «int», «long», «double», «float», «unsigned», «signed», или их допустимые комбинации (например, «unsigned long int»). Но что конкретно подразумевается под этим — зависит от компилятора и CPU для которого он был написан, например: int может быть 16-битным (AVR, PDP-11, i8080/8086), но чаще 32-битным (ARM, i32).

Чтобы разрулить этот беспредел и не изменять компиляторы (оставить набор уже всем известных ключевых слов, описывающих типы), т.е. оставить совместимость с уже написанным кодом, в стандарте C99 сделали очень просто: дешевый трюк через уже имеющийся препроцессор — включили в стандарт обязательный для самих компиляторов набор h-файлов, вошедших в стандартную библиотеку, важнейший из которых (с точки зрения определения типов) — это stdint.h.

Там в имена целых типов (чтобы не мудрствовали лукаво) вбита их ДЛИНА ЦИФРОЙ и впереди добавляется u, если он БЕЗЗНАКОВЫЙ (unsigned). Все это, если говорить слишком упрощенно, просто продефайнено через типы на основе стандартных ключевых слов языка Си в соответствии с их длиной в конкретном компиляторе для конкретного CPU, а-ля:
typedef signed char int8_t
typedef unsigned char uint8_t
// В этом компиляторе 
// для этого процессора 
// int имеет длину 32 бита:
typedef signed int int32_t
typedef unsigned int uint32_t
typedef signed char int8_t
typedef unsigned char uint8_t
// А в этом компиляторе 
// для другого процессора 
// int имеет длину 16 бит:
typedef signed int int16_t
typedef unsigned int uint16_t

Собственно, такое обозначение типов (а-ля C99) — это ныне некий понятный стандарт для MCU-мэнов, а тем паче для ARM-мэнов.

Мега-MCU-мэны (в т.ч. reptile :), пишут мега-портируемый код, который можно портировать куда угодно. Например, на проц, у которого нет C99 компилятора, а есть только поддерживающий старый стандарт ANSI/C89. Или даже под какую ОС (с Linux не должно быть особых проблем, т.к. его ядро пишется на GCC, который С99) и даже под венду(!!!)

Обычно они делают для своего поделия обертку в виде h-файла для своих собственных типов. Видимо отсюда и лезут всяческие U32/S32 и U_32/S_32 и т.п. локальная хуйня. Хотя, ВЕЛИКИЙ Elm Chan в этом случае в своей библиотеке FatFS (которая портируется и под венду) просто лег под Мелкософт, т.е. не мудрствуя лукаво переопределил имена а-ля православный C99 stdint.h в презренные имена, прописанные в windef.h (загружаемого из windows.h). Но видимо так было проще, хотя… х.з.

Чел, наваявший LwIp TCP/IP стек, тоже захреначил свою обертку со своими u32_t/s32_t. Короче: кто в лес, кто по дрова. Но не надо видимо придавать всем этим обозначениям а-ля U32/S32 такого уж важного значения, пропитанного подобострастным фанатизмом или наоборот ненавистью.

Но! MCU-мэн, помни о C99 и stdint.h — это стандарт!
+1
Например, на проц, у которого нет C99 компилятора, а есть только поддерживающий старый стандарт ANSI/C89.
Не проблема портировать/написать под такой компилятор stdint.h, вместо изобретения своих типов. Иной раз какой-нить uint32 встречается в одном и том же модуле под десятком разных имен…
-1
Ты меня в этом обвиняешь?
0
reptile написал неплохой код для GPIO (т.е. конкретной периферии, конкретного произв.). Который, я, наверное, подло спизжу скопипащу :D

Но вот ты и спроси у него: почему он ввел туда u32? Я могу только предположить, что код можно использовать на stm32 и stm8 (процы, кои reptile всегда юзал). stm8 я не изучал(из принципа и из-за общей ограниченности объема черепа, т.к. он у меня не бездонный — череп не жмет :) — поэтому ничего не могу сказать.
+1
Тоже хорош.
0
Так, без оскорблений. Оба. А то выпилю вас на пару.
0
Офигеть. Я вот изобрел примерно то же самое. :) Только сейчас увидел эту статью. :)

В моем варианте настройка выглядит как-то так:


    GPIOC->MODER=PIN_OUT(LED1_PC) | PIN_OUT(LED2_PC) | PIN_OUT(LED3_PC) | PIN_AF(WIFI_UART_RX_PC) | PIN_AF(UART_TX_PC);
    GPIOC->OTYPER=0;
    GPIOC->OSPEEDR=PIN_LSPEED(LED1_PC) | PIN_LSPEED(LED2_PC) | PIN_LSPEED(LED3_PC) | PIN_HSPEED(UART_TX_PC);
    GPIOC->PUPDR=0;
    GPIOC->ODR=0;
    GPIOC->AFR[1]=AF_AFR1(7,WIFI_UART_TX_PC);

    ...

    GPIOC->ODR|=PIN_MASK(LED1_PC);


Соответственно, макросы определены как-то так:


#define PIN_OUT(pin_no)          (1<<(2*(pin_no)))
#define PIN_AF(pin_no)           (2<<(2*(pin_no)))
#define PIN_ANALOG(pin_no)       (3<<(2*(pin_no)))

#define PIN_MASK(pin_no)         (1<<(pin_no))


Ну и так далее. LEDn_Px и прочее определено цифирками, соответствующими номерам пинов. В конце добавляю _Px, просто чтобы не забыть, на каком порту соответствующий пин.
0
  • avatar
  • _YS_
  • 30 сентября 2016, 12:37
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.