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

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
Эмм, может быть чего-то не понимаю, но:
...
REG=0xDEADBEEF; //магическое число, хрен знает что происходит при его записи
...
#define GPIO_MODE_OUTPUT50_PUSH_PULL 0xB00BBABE
REG=GPIO_MODE_OUTPUT50_PUSH_PULL; //запись ясно дающая понять, что мы переводим портВВ
//в режим выхода пуш-пулл с максимальным быстродействием 50МГц
К чему относится цифра 50 не понятно. К частоте 50 кГц? или 50 Гц? Может быть это порт под номером 50?
Получается писанина, равносильная моей. Отличие в том, что у меня все параметры настраиваются раздельно, а у вас кучей. Ну и ещё у вас скорость привязана к конкретному камню.
по поводу скорости: Мне гораздо важнее, какую скорость нога выдает, чем ее абстрактное описание LOW,MEDIUM,HIGH. К примеру, мне нужно чтобы порт был со скоростью 10МГц. (вообще вся эта штука придумана исключительно с целью экономии питания). И что мне писать? LOW? MEDIUM? HIGH?.. а в STM32F4xx там есть еще и FAST. Насколько это разумно?
Уже не в первый раз слышу такую рекомендацию. Для какой цели так делают?
И почему нельзя совсем без скобок написать?
И почему нельзя совсем без скобок написать?
При обрамлении только в фигурные скобки при подстановке тела макроса в некоторые места будет получаться некорректный код. ЕМНИП, в конструкции
if(...)
GPIO_INIT_PIN(...);
else
...
Здесь указана ссылка на мою статью, поэтому попробую взять реванш). По мне, так писанина
Второе: Данный макрос был написан для серии stm32f10x, там настройки совершенно по иному выглядят, поэтому целесообразней их делать так, как у меня.
Третье:
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));
Неужели не ясно, что порт настраивается в промежуточные положения в момент настройки? В каком режиме он окажется, когда отработает первая настройка?
Насчёт одной операции Ч/М/З — спасибо за важное замечание. Позволяет уменьшить объём кода, и избежать промежуточных режимов работы порта. Вечером исправлю.
А вот писанина как раз и нужна для того, чтобы чётко, ясно, и отдельно видеть все настройки. Мне например удобнее настраивать каждый параметр отдельно, а не всё в кучу. Меняется только визуальное восприятие, на размер кода не влияет, поэтому не страшно.
А вот писанина как раз и нужна для того, чтобы чётко, ясно, и отдельно видеть все настройки. Мне например удобнее настраивать каждый параметр отдельно, а не всё в кучу. Меняется только визуальное восприятие, на размер кода не влияет, поэтому не страшно.
да, главное, только не перепутать эти настройки в описании, а то мало ли чего выйдет?))
И все же, обратите внимание, что у вас много регистров настройки, один отвечает за скорость, другой за режим, третий альтернативные функции и так далее… А в stm32f10x на все про все одна пара регистров. Там все упаковано максимально возможно. Отсюда и логика: Один регистр настройки — одно GPIO_MODE… описание.
И все же, обратите внимание, что у вас много регистров настройки, один отвечает за скорость, другой за режим, третий альтернативные функции и так далее… А в stm32f10x на все про все одна пара регистров. Там все упаковано максимально возможно. Отсюда и логика: Один регистр настройки — одно GPIO_MODE… описание.
Не атомарно это:
замени на
Это атомарно.
Работает так: мы присваевем регистру BSRR 1 с сдвигом на пин + 16, если соответствующий пин выставлен в ODR, или на пин, если
не выставлен.
#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, или на пин, если
не выставлен.
ваш вариант гораздо лучше, тоже хотел указать на это, однако атомарным его нельзя называть, поскольку используется тот же принцип ЧМЗ. Весомый плюс — не затрагивает другие выводы порта, кроме изменяемого.
вообще надо взять за правило — не пользоваться ODR! Все можно сделать с помощью регистра BSRR (в STM32F4xx.h почему-то он описан как два 16-ти битных BSRRL и BSRRH. Тупо.)
например, выставить значения надо в 8-ми битную шину GPIOA.0 — GPIOA.7:
например, выставить значения надо в 8-ми битную шину GPIOA.0 — GPIOA.7:
GPIOA->BSRR = (data & 0xFF) | (((~data)&0xFF) << 16);
Спасибо за важные замечания!
Эта конструкция
не заработает на STM32F030, там регистры битбэнда реализованы по другому
Эта конструкция
#define GPIO_TOGGLE(PORT,PIN) (PORT->BSRR = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)
не заработает на STM32F030, там регистры битбэнда реализованы по другому
возможно, там имеется та же проблема: BSRR расшит на два BSRRH и BSRRL
В этом случае поможет несложный хак:
В этом случае поможет несложный хак:
#define GPIO_TOGGLE(PORT,PIN) ((*(uint32_t*)&PORT->BSRRL) = ( 1 << (PIN + ((PORT->ODR & (1 << PIN))?16:0)
нет. Только что прочитал мануал.
Вот в 40х всё описано действительнь по другому. И там надо условие в lvalue перенести
((PORT->ODR & (1 << PIN))?PORT->BRR:PORT->BSR) = (1 << PIN)
Но там есть нормальный Bit Banding, и можно просто воспользоваться им.
Вот в 40х всё описано действительнь по другому. И там надо условие в lvalue перенести
((PORT->ODR & (1 << PIN))?PORT->BRR:PORT->BSR) = (1 << PIN)
Но там есть нормальный Bit Banding, и можно просто воспользоваться им.
И там надо условие в lvalue перенестиНе нужно. Описанный мною выше хак прекрасно работает. Нет никаких препятствий в том, чтобы записать в регистр BSR 32-х битное значение по образу и подобию BSRR в stm32f10x. Видимо, кто-то решил «упростить» несложные операции по выставлению и сбросу битов. Если нужно одновременно установить и сбросить нужные биты — ваш способ окажется не оправданным
GPIO_INIT — исправил на «чтение/модификация/запись»
GPIO_TOGGLE — исправил на «битбэнд»
Ещё добавил:
GPIOS_SET
GPIOS_RESET
GPIOS_TOGGLE
GPIOS_ISSET
GPIO_TOGGLE — исправил на «битбэнд»
Ещё добавил:
GPIOS_SET
GPIOS_RESET
GPIOS_TOGGLE
GPIOS_ISSET
Все же, коллеги, почему макросы а не энамы и инлайн фикции?
Единственный довод в пользу макросов – существуют древние компиляторы, которые эти сами энамы и инлайны не поддерживают. Но разве это актуально (для данной, STM32, платформы)?
Единственный довод в пользу макросов – существуют древние компиляторы, которые эти сами энамы и инлайны не поддерживают. Но разве это актуально (для данной, STM32, платформы)?
согласен отчасти. Иногда эти ну слишком умные компиляторы не хотят инлайнить функции по своему, неведомо никому усмотрению. А это не по феншую, когда выходит как в рекламе билайна:
— А я не это загадывал!
— А сбылось это!!!
Иногда эти ну слишком умные компиляторы не хотят инлайнить функции по своему, неведомо никому усмотрению.
С макросами может произойти обратное, сначала препроцессор сделает подстановку макросов, потом «слишком умный компилятор» увидит повторяющийся однотипный фрагмент кода и вынесет его в «подпрограмму»
Макросы, хоть и похожи по синтаксису на функции, ими не являются, стоит об этом забыть, и невинный макрос
#define MIN(a,b) ((a>b)?b:a)
при определенных условиях становится жутким и трудно выявляемым багом
int I = MIN(ReadADC(), 100);
Не говоря о других проблемах, типа синтаксиса и типобезопасности.
При использовании макросов покачто на грабли не натыкался, а вот с темой «слишком умного компилятора» уже знаком))) Такчто я за дефайны.
Ну, Вам решать.
Мой опыт диаметрально противоположен (и пример с MIN(ReadADC(), 100)
я привел из своего опыта :)
Мой опыт диаметрально противоположен (и пример с MIN(ReadADC(), 100)
я привел из своего опыта :)
а вот с темой «слишком умного компилятора» уже знакомС чем именно вы столкнулись? При включенной оптимизации компилятор не инлайнил функцию но инлайнил аналогичный макрос?
инлайновая функция это вообще болезнь всех умных компиляторов. Я считаю, одна единственная уважительная причина игнорировать это требование — рекурсивный вызов.
А вот вынос однотипного кода в подпрограмму — с таким не встречался. возможно и такое имеется, не спорю.
А вот вынос однотипного кода в подпрограмму — с таким не встречался. возможно и такое имеется, не спорю.
инлайновая функция это вообще болезнь всех умных компиляторов. Я считаю, одна единственная уважительная причина игнорировать это требование — рекурсивный вызов.
Но совсем понял. В чем проблема с инлайн функцией и как это связанно с рекурсией?
Да, по сути, те же яйца только в профиль, компилятор должен инлайнить но нет 100% гарантии.
Как по мне, проблема с тем, что компилятор может не заинлайтить функцию – сильно преувеличена. Как я мы знаем, компилятор может делать достаточно много вещей для оптимизации кода, единственное он должен – соответствовать стандарту. В случае с макросами – стандарт гарантирует только то, что препроцессор выполнит замену…
Практически, я не помню случаев, чтобы (при включенной оптимизации) компилятор вел себя неадекватно и не инлайнил мелкие функции. Зато у инлайн функций есть куча плюсов: привычный синтаксис, поведение (как и в «обычных» функциях), контроль типов. Уже давно считается хорошей практикой переход от макросов к инлайн. Возьмем, для примера, Linux code style:
Как по мне, проблема с тем, что компилятор может не заинлайтить функцию – сильно преувеличена. Как я мы знаем, компилятор может делать достаточно много вещей для оптимизации кода, единственное он должен – соответствовать стандарту. В случае с макросами – стандарт гарантирует только то, что препроцессор выполнит замену…
Практически, я не помню случаев, чтобы (при включенной оптимизации) компилятор вел себя неадекватно и не инлайнил мелкие функции. Зато у инлайн функций есть куча плюсов: привычный синтаксис, поведение (как и в «обычных» функциях), контроль типов. Уже давно считается хорошей практикой переход от макросов к инлайн. Возьмем, для примера, Linux code style:
Enums are preferred when defining several related constants.
Generally, inline functions are preferable to macros resembling functions
Хотя … возможно я и поспешил с ответом. Современные ЯП (ну, или по крайней пере известные мне С++ и С#) активно впитывают концепции других парадигм.
Например в С# есть ООП (это понятно), АОП (атрибуты), функциональное программирование (лямбды, замыкания) отсутствие типизации (динамические типы). В контексте появления лямбд и замыканий в С++, вполне возможно, что дело дойдет до оптимизации хвостовой рекурсии.
Например в С# есть ООП (это понятно), АОП (атрибуты), функциональное программирование (лямбды, замыкания) отсутствие типизации (динамические типы). В контексте появления лямбд и замыканий в С++, вполне возможно, что дело дойдет до оптимизации хвостовой рекурсии.
по вашему же примеру использование макроса MIN универсально для всех типов данных, целых, вещественных числах и даже строках и структурах. А вот если делать инлайн функцию — требуется либо перегружаемые функции (если стандарт позволяет), либо бесчисленное их множество. Да, у макросов есть ограничения. По хорошему, эффективно это работает, когда в них пишутся константы или переменные, но никак не вызов функций
по вашему же примеру использование макроса MIN универсально для всех типов данных, целых, вещественных числах и даже строках и структурах.
Ну, насчет строк и структур Вы перегнули (если мы не говорим о С++ с перегрузкой операторов сравнения) :) И это, как раз, может стать причиной ошибки из-за отсутствия контроля типов.
Я считаю, что надо послушать e_mc2 и писать для MCU на C99, а не скатываться постоянно к ветхозаветному коду ANSI/C89 из-за весьма сомнительных плюсов якобы какой-то совместимости(с чем?). Тем более, что большая тройка компиляторов для MCU его(C99) поддерживает практически полностью, тем более, если речь идет об ARM.
- well-man2000
- 06 апреля 2015, 21:34
- ↑
- ↓
Я считаю, что надо послушать e_mc2
Странно, вы поддержали мое мнение сразу в 2-х топиках :) Это точно старый-добрый well-man2000 или у Вас аккаунт увели?
Для чего выдумывать самостийные имена?
Если бы я был параноиком, то я бы написал «графическо-табличный интерактивный макрос» типа Куба, который бы делал исходники в регистрах :)
Если бы я был параноиком, то я бы написал «графическо-табличный интерактивный макрос» типа Куба, который бы делал исходники в регистрах :)

Дак в названии темы ведь написано — велосипед. Поэтому и воспринимать содержимое надо соответствующим образом :)
Тогда уж просто это колесо без осей и рам… так поделка на раз.
И главное — в Кубе уже всё что нужно есть: имена, фамилии, явки, фотки…
C:\Program Files\STMicroelectronics\STM32Cube\STM32CubeMX\db\mcu\IP
И главное — в Кубе уже всё что нужно есть: имена, фамилии, явки, фотки…
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>
Для чего выдумывать самостийные имена?Конечно же не надо. Зачем определять по своему еще раз то, что УЖЕ ОПРЕДЕЛЕНО стандартно производителем в своей мегапортянке (\Drivers\CMSIS\Device\ST\STM32F3xx\Include\stm32f30xxx.h) специально для людей и отрасли(экосистемы потребителей STM32)?
Но что вовсе не мешает определить кое-что для удобства в своей прокладке на своем личном application уровне, используя стандартные имена из этой самой мегапортянки, например:
Led1 =…
PWM_motor1 =…
Display_SPI_MOSI =…
- well-man2000
- 18 апреля 2015, 05:43
- ↑
- ↓
Еще одно замечание по теме:
Я бы регистр MODER настраивал последним.
Сперва сконфигурировал все — а потом — бац! И сразу он молодец!
Например, если это I2C, то в момент настройки из дефайна в рабочий режим будет сперва настроено, что альтернатива, потом подтяжка, потом OPEN_DRAIN и HIGH а только потом — выход альтернативы. Красиво же, не так ли?
Я бы регистр MODER настраивал последним.
Сперва сконфигурировал все — а потом — бац! И сразу он молодец!
Например, если это I2C, то в момент настройки из дефайна в рабочий режим будет сперва настроено, что альтернатива, потом подтяжка, потом OPEN_DRAIN и HIGH а только потом — выход альтернативы. Красиво же, не так ли?
Свое всегда роднее, и это понятно. Прочитав обе «конкурирующие» темы, больше понравился способ 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. По-крайней мере я бы задумался об ее использовании, т.к. чаще всего нужно вышеперечисленное.
Это все так, рассуждения. Как говорится, чисто мое мнение)
На мой взгляд, недостаток обоих способов — это работа только с портами. А если нужна другая периферия? Использовать регистры? SPL? Во втором случае велосипеды не нужны. Да и в первом можно обойтись. Для проектов на STM у меня есть сопутствующие проекты MicroXplorer (ныне куб), где визуально сразу видно, как настроена каждая нога, и это удобней, чем смотреть на портянки GPIO_INIT. Мне, например, лениво настраивать тот же UART через регистры. Подключаю SPL, функции передачи/приема данных свои, на регистрах. Соответственно, порты настраиваю через SPL. Но в программе использую BSRR. Получается, оба велосипеда как бы особо и не нужны. Но если к ним добавить инициализацию хотя бы UART, TIM, SPI, I2C, ADC, то это будет очень неплохая альтернатива SPL. По-крайней мере я бы задумался об ее использовании, т.к. чаще всего нужно вышеперечисленное.
Это все так, рассуждения. Как говорится, чисто мое мнение)
Лениво ему UART настраивать…
Ну а вообще — каждый выбирает своё. Кому-то достаточно DS+RM, кому-то Нужно уже DS+RM+SPL+Cube
:-)
#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
:-)
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);
Я не против регистров, пожалуй даже за, но не в инициализации периферии. Наглядная инициализация сильно уменьшает количество проблем в будущем.
а я вот так упростил
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)
По крайне мере работать стало удобней и на много быстрей
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)
По крайне мере работать стало удобней и на много быстрей
- Vitaliy_81
- 13 июля 2015, 14:04
- ↓
а я вот так упростил
user.c
gpio_init_f0xx.h
По крайне мере работать стало удобней и на много быстрей
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)
По крайне мере работать стало удобней и на много быстрей
- Vitaliy_81
- 13 июля 2015, 16:21
- ↓
Вот эта простыня:
будет полностью присутствовать в коде при каждом упоминании GPIO_CONFs или GIP. Думаю, стоит заменить if-ы в этом коде на препроцессорные, тогда на каждый вызов будет вставляться ровно одна строка кода вместо всей простыни.
#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-ы в этом коде на препроцессорные, тогда на каждый вызов будет вставляться ровно одна строка кода вместо всей простыни.
А не пофиг ли? Оптимизатор же выпилит. Правда, если таких макросов будет много, это отразится на скорости компиляции.
чет не могу найти пример который поможет мне исспользовать препроцесорные ифки, хоть один пример как его сдесь применить плииз
- Vitaliy_81
- 16 июля 2015, 01:13
- ↑
- ↓
Спасибо за совет мне он помог лучше понимать процес, ну и полезно знать что оптимизатор может взять на себя процес оптимизации пока я учусь.
- Vitaliy_81
- 15 июля 2015, 18:30
- ↓
#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
обычный тролль-мудач@ок… пока не видно ни одного аргумента, или слабо предъявить свой мега-эффектичный код?
Аргументы… Видите ли, Ваш код несколько мутноват (имхо конечно).
Какие-то непонятно, зачем нужные, макросы (типа _bs, _nbs и прочих _rmxx), типы данных навроде u32, неужели стандартных uint32_t (и тому подобных) не хватает.
И самое главное непонятно где тут удобство настройки? Мозг вывихнешь пока поймёшь.
Какие-то непонятно, зачем нужные, макросы (типа _bs, _nbs и прочих _rmxx), типы данных навроде u32, неужели стандартных uint32_t (и тому подобных) не хватает.
И самое главное непонятно где тут удобство настройки? Мозг вывихнешь пока поймёшь.
если ничего непонятно — зачем неаргуметрированное набрасывание г@вна?
без сравнения ASM-листингов, количества тактов и т.д.?
лично Вам неудобно и не нужно — просто проходите мимо…
без сравнения ASM-листингов, количества тактов и т.д.?
лично Вам неудобно и не нужно — просто проходите мимо…
если ничего непонятно — зачем неаргуметрированное набрасывание г@вна?Код (по возможности) надо бы делать как можно более понятным, иначе замучаешься его поддерживать. Это в большинстве случаев сейчас важнее скорости исполнения и эффективности.
P.S. Имхо лучше делать через template — код и эффективный и понятный. Типа такого
во-1х, откуда в С template?В Си нету. А что для ARM плюсов нет? Полно компиляторов и даже бесплатных.
во-2х — попробуй кодом по ссылке настроить например 4 вывода на выход, и посмотри на сгенерированный листинг…Думаю всё нормально будет сгенерировано… Сейчас проверить не могу — на этом компе для ARM ничего не установлено.
вот например настройка режима порта (любого количества пинов):
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]
комментарий был удален
комментарий был удален
Нет, в стандарте uint32_t
Просто их стандартизировали «относительно недавно» в С99, до этого придумывали свои названия, которые закрепились в legacy code. В линуксовом ядре ЕМНИП до сих пор сосуществуют u32 и u_int32_t и uint32_t из стандарта. В Win32 – DWORD и т.д. Плюс дело привычки, я долго не мог заставить себя писать uint8_t вместо BYTE … :)
Просто их стандартизировали «относительно недавно» в С99, до этого придумывали свои названия, которые закрепились в legacy code. В линуксовом ядре ЕМНИП до сих пор сосуществуют u32 и u_int32_t и uint32_t из стандарта. В Win32 – DWORD и т.д. Плюс дело привычки, я долго не мог заставить себя писать uint8_t вместо BYTE … :)
Сами типы данных прошиты в компиляторах Си и определяются через ключевые слова: «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, а-ля:
Собственно, такое обозначение типов (а-ля 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 — это стандарт!
Чтобы разрулить этот беспредел и не изменять компиляторы (оставить набор уже всем известных ключевых слов, описывающих типы), т.е. оставить совместимость с уже написанным кодом, в стандарте 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 и т.п.
Чел, наваявший LwIp TCP/IP стек, тоже захреначил свою обертку со своими u32_t/s32_t. Короче: кто в лес, кто по дрова. Но не надо видимо придавать всем этим обозначениям а-ля U32/S32 такого уж важного значения, пропитанного подобострастным фанатизмом или наоборот ненавистью.
Но! MCU-мэн, помни о C99 и stdint.h — это стандарт!
- well-man2000
- 12 августа 2015, 01:10
- ↑
- ↓
reptile написал неплохой код для GPIO (т.е. конкретной периферии, конкретного произв.). Который, я, наверное, подло спизжу скопипащу :D
Но вот ты и спроси у него: почему он ввел туда u32? Я могу только предположить, что код можно использовать на stm32 и stm8 (процы, кои reptile всегда юзал). stm8 я не изучал(из принципа и из-за общей ограниченности объема черепа, т.к. он у меня не бездонный — череп не жмет :) — поэтому ничего не могу сказать.
Но вот ты и спроси у него: почему он ввел туда u32? Я могу только предположить, что код можно использовать на stm32 и stm8 (процы, кои reptile всегда юзал). stm8 я не изучал(из принципа и из-за общей ограниченности объема черепа, т.к. он у меня не бездонный — череп не жмет :) — поэтому ничего не могу сказать.
- well-man2000
- 12 августа 2015, 02:03
- ↑
- ↓
Офигеть. Я вот изобрел примерно то же самое. :) Только сейчас увидел эту статью. :)
В моем варианте настройка выглядит как-то так:
Соответственно, макросы определены как-то так:
Ну и так далее. LEDn_Px и прочее определено цифирками, соответствующими номерам пинов. В конце добавляю _Px, просто чтобы не забыть, на каком порту соответствующий пин.
В моем варианте настройка выглядит как-то так:
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, просто чтобы не забыть, на каком порту соответствующий пин.
Комментарии (125)
RSS свернуть / развернуть