Skip to main content

Конференции

Просмотр конференции fido7.pushkin.rss.easyelectronics.ru:

Предыдущее Следующее

Дата: 29 Oct 2019, 00:00:06
От: RSS robot @ 2:5020/2140.2140
Кому: All
Тема: Удобная работа с GPIO на STM32


http://feedproxy.google.com/~r/easyelectronics/~3/THFdv0_IHps/udobnaya-rabota-s-gpio-na-stm32.html

Долгое время работая с STM32 все никак не мог определиться как же удобней мне обращаться с портами GPIO. Всякие SPL методы мне категорически не прикалывают, как то громоздко, хотя и читаемо. Опять же
помнить как там параметры эти зовутся. Вручную порты тасовать можно, но опять же громоздко выходит. В дефайны оборачивать… тоже чет не то. Пока в одном из проектов не увидел годную реализацию,
немного ее усовершенствовал и теперь с удовольствием использую. Главное достоинство ее в том, что теперь неимоверно просто менять порты с ноги на ногу. Тасовать как угодно. Что особенно приятно когда
пилишь пилишь девайс, хотелки постепенно растут, а потом не влезаешь в корпус и надо ног побольше. Берешь и пересаживаешься в корпус пожирней, а матрицу выводов переписываешь за пять минут и все
взлетает правильно с первой же компиляции. В общем, суть такова: Библиотечка IO.h состоит из хидера с именами и сишника, с парой функций. В хидере кроме прототипов есть еще и enum с именами: Вроде
такого, скопипащен из реального проекта: typedef enum { io_Blink = 0, io_DIR1 = 1, io_EN1, io_STEP1, io_DIR2, io_EN2, io_STEP2, io_DIR3, io_EN3, io_STEP3, io_DIR4, io_EN4, io_STEP4, io_DIR5, io_EN5,
io_STEP5, io_EndstopX, io_EndstopY, io_EndstopB, io_EndstopF, io_LEDX, io_LEDY, io_RX1, io_TX1, io_RX2, io_TX2, io_RX3, io_TX3, io_RX4, io_TX4, io_RS_DIR4 } tIOLine; А также есть enum с состояними,
чисто для удобства и наглядности. typedef enum { OFF = 0, ON = 1, LOW = 0, HIGH =1 } tIOState; Там же находятся битовые маски определения состояний в которых может быть вывод порта: #define IN (0x00)
#define OUT_10MHz (0x01) #define OUT_2MHz (0x02) #define OUT_50MHz (0x03) #define OUT_PP (0x00) #define OUT_OD (0x04) #define OUT_APP (0x08) #define OUT_AOD (0x0C) #define IN_ADC (0x00) #define
IN_HIZ (0x04) #define IN_PULL (0x08) Они взяты просто из таблицы: И фактически кодируют вот этот четырехбитный блок. Т.е. если нам надо вывод перевести в состояние входа с подтяжкой, то нам нужна
битмаска IN_PULL. 0x08 это b1000, что развернется в CNF=10, MODE=00. Если же надо, например, выход с Open Drain и частотой в 2Мгц, то берем сумму двух масок: OUT_2MHz+OUT_OD = b0110. Остается только
это битмаску задвинуть на нужное место. Ну,а обозначения простые IN и OUT это вход и выход соответственно, PP — PushPull, OD — OpenDrain, APP — Альтернативная функция PushPull, AOD
— альтернативная функция OpenDrain. Ну и ADC — вход АЦП, HIZ — выскоимпендансный вход. PULL — вход с подтяжкой. Направление подтяжки задается битом в ODR. Теперь эту всю
красоту надо как то упорядочить, чтобы было удобно пользоваться. В IO.с у меня живет вот такая структура: typedef struct { GPIO_TypeDef* GPIOx; // Имя порта uint16_t GPIO_Pin; // Номер порта uint8_t
MODE; // Режим uint8_t DefState; // Стартовое значение (уйдет в бит ODR при инициализации) } tGPIO_Line; Ну и типом этой структуры мы создаем массивчик: const tGPIO_Line IOs[] = {{ GPIOD, 11,
OUT_10MHz + OUT_PP, HIGH}, // Blink 1 * { GPIOB, 9, OUT_10MHz + OUT_PP, HIGH}, // Dir1 XR 2 * { GPIOE, 1, OUT_10MHz + OUT_PP, HIGH}, // En1 3 * { GPIOE, 0, OUT_10MHz + OUT_PP, HIGH}, // Step1 4 * {
GPIOB, 6, OUT_10MHz + OUT_PP, HIGH}, // Dir2 YL 5 * { GPIOB, 8, OUT_10MHz + OUT_PP, HIGH}, // En2 6 * { GPIOB, 7, OUT_10MHz + OUT_PP, HIGH}, // Step2 7 * { GPIOD, 6, OUT_10MHz + OUT_PP, HIGH}, //
Dir3 F 8 * { GPIOB, 5, OUT_10MHz + OUT_PP, LOW}, // En3 9 * { GPIOD, 7, OUT_10MHz + OUT_PP, HIGH}, // Step3 10 * { GPIOD, 3, OUT_10MHz + OUT_PP, HIGH}, // Dir4 B 11 * { GPIOD, 5, OUT_10MHz + OUT_PP,
LOW}, // En4 12 * { GPIOD, 4, OUT_10MHz + OUT_PP, HIGH}, // Step4 13 * { GPIOA, 8, OUT_10MHz + OUT_PP, HIGH}, // Dir5 S 14 * { GPIOA, 12, OUT_10MHz + OUT_PP, LOW}, // En5 15 * { GPIOA, 11, OUT_10MHz
+ OUT_PP, HIGH}, // Step5 16 * { GPIOD, 13, IN + IN_PULL, HIGH}, // Endstop x 17 * { GPIOD, 12, IN + IN_PULL, HIGH}, // Endstop y 18 * { GPIOC, 6, IN + IN_PULL, HIGH}, // Endstop B 19 * { GPIOD, 15,
IN + IN_PULL, HIGH}, // Endstop F 20 * { GPIOA, 5, OUT_10MHz + OUT_PP, LOW}, // LedX 21 * DEBUG { GPIOA, 6, OUT_10MHz + OUT_PP, LOW}, // LedY 22 * DEBUG { GPIOA, 10, IN + IN_PULL, HIGH}, // Rx1 23 *
{ GPIOA, 9, OUT_10MHz + OUT_APP, HIGH}, // Tx1 24 * { GPIOA, 3, IN + IN_PULL, HIGH}, // Rx2 25 * { GPIOA, 2, OUT_10MHz + OUT_APP, HIGH}, // Tx2 26 * { GPIOB, 11, IN + IN_PULL, HIGH}, // Rx3 27 * {
GPIOB, 10, OUT_10MHz + OUT_APP, HIGH}, // Tx3 28 * { GPIOC, 11, IN + IN_PULL, HIGH}, // Rx4 27 * { GPIOC, 10, OUT_10MHz + OUT_APP, HIGH}, // Tx4 28 * { GPIOD, 0, OUT_10MHz + OUT_PP, LOW} // RS DIR4
}; const uint32_t cIO_COUNT = sizeof(IOs)/sizeof(tGPIO_Line); Где по каждому значению, записями вида GPIOB, 9, OUT_10MHz + OUT_PP, HIGH определяем нога какого порта, под каким номером в каком режиме
работает. Ну и дефолтное значение при старте. Высокое или низкое. Подтяжка вверх или вниз (для входа с подтяжкой). В результате у нас будет обращение к нужному порту GPIO просто по номеру его в
массиве, а номера обозваны через enum. Самое главное, чтобы порядок расположения строк в массиве точно следовал порядку имен в Enum. За этим надо следить ручками, если знаете более красивое решение,
то набросьте. Да, использования (+) для склейки битмасок является дурным тоном, т.к. в случае ошибки перехлеста равностоящих битов вы получите лажу. Лучше применять ИЛИ (|). Но мне так норм,
выглядит красивее. :) Осталось теперь только сделать обработчики нашего массива, который позволит дрыгать ногами и настраивать. Первым делом идет функция инициализации. Она просто пробежит по массиву
и все настроит как задано: Функция конфигурации линии, тут все просто: void IO_ConfigLine(tIOLine Line, uint8_t Mode, uint8_t State) { if(IOs[Line].GPIO_Pin CRL &= ~(0x0F CRL |= ModeAPB2ENR
|= RCC_APB2ENR_IOPEEN; //RCC->APB2ENR |= RCC_APB2ENR_IOPFEN; //RCC->APB2ENR |= RCC_APB2ENR_IOPGEN; RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // А дальше в цикле пробегаемся по нашему
массиву и конфигурируем. for (int Line = 0; Line BSRR = 1 BRR = 1 ODR ^= 1 IDR) & (1

--- rss2mail.pl
Origin: RSS robot (2:5020/2140.2140)

Предыдущее Следующее

К списку сообщений
К списку конференций