Микроконтроллеры

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Микроконтроллеры » Архив » STM32F4xx: GPIO (мой вариант работы)


STM32F4xx: GPIO (мой вариант работы)

Сообщений 1 страница 21 из 21

1

Предлагаю свой вариант работы с портами. В основном писалось ради удобства конфигурирования, управление использую только если нужно динамически менять пин. Для хранения информации о пине хватило 1 байта: младшие 4 бита - номер пина, старшие 4 - номер порта, 0 - пин не присвоен. Для конфигурации создал энумератор и там перечислены все настройки.

Код:
typedef enum
{
  gpInputMode = 0x0000,
  gpGeneralMode = 0x0001,
  gpAlternateMode = 0x0002,
  gpAnalogMode = 0x0003,

  gpPushPull = 0x0000,
  gpOpenDrain = 0x0004,

  gpSpeedLow = 0x0000,
  gpSpeedMedium = 0x0008,
  gpSpeedHigh = 0x10,
  gpSpeedVeryHigh = 0x18,

  gpNoPull = 0x0000,
  gpPullUp = 0x0020,
  gpPullDown = 0x0040,

  gpODR_Low = 0x0000,
  gpODR_High = 0x0080,

  gpAF0 = 0x0000,
  gpAF1 = 0x0100,
  gpAF2 = 0x0200,
  gpAF3 = 0x0300,
  gpAF4 = 0x0400,
  gpAF5 = 0x0500,
  gpAF6 = 0x0600,
  gpAF7 = 0x0700,
  gpAF8 = 0x0800,
  gpAF9 = 0x0900,
  gpAF10 = 0x0a00,
  gpAF11 = 0x0b00,
  gpAF12 = 0x0c00,
  gpAF13 = 0x0d00,
  gpAF14 = 0x0e00,
  gpAF15 = 0x0f00

/*
 0:1    MODER
 2      OTYPER
 3:4    OSPEEDR
 5:6    PUPDR
 7:     ODR
 8:15   AFx
*/
} Typ_CtrlPort;

перечисление пинов:

Код:
typedef enum
{
  P_NC = 0,
  PA0 = 0x10,
  PA1 = 0x11,
  PA2 = 0x12,
  PA3 = 0x13,
  PA4 = 0x14,
  PA5 = 0x15,
  PA6 = 0x16,
  PA7 = 0x17,
  PA8 = 0x18,
  PA9 = 0x19,
  PA10 = 0x1a,
  PA11 = 0x1b,
  PA12 = 0x1c,
  PA13 = 0x1d,
  PA14 = 0x1e,
  PA15 = 0x1f,

  PB0 = 0x20,
  PB1 = 0x21,
  PB2 = 0x22,
  PB3 = 0x23,
  PB4 = 0x24,
  PB5 = 0x25,
  PB6 = 0x26,
  PB7 = 0x27,
  PB8 = 0x28,
  PB9 = 0x29,
  PB10 = 0x2a,
  PB11 = 0x2b,
  PB12 = 0x2c,
  PB13 = 0x2d,
  PB14 = 0x2e,
  PB15 = 0x2f,

  PC0 = 0x30,
  PC1 = 0x31,
  PC2 = 0x32,
  PC3 = 0x33,
  PC4 = 0x34,
  PC5 = 0x35,
  PC6 = 0x36,
  PC7 = 0x37,
  PC8 = 0x38,
  PC9 = 0x39,
  PC10 = 0x3a,
  PC11 = 0x3b,
  PC12 = 0x3c,
  PC13 = 0x3d,
  PC14 = 0x3e,
  PC15 = 0x3f,

  PD0 = 0x40,
  PD1 = 0x41,
  PD2 = 0x42,
  PD3 = 0x43,
  PD4 = 0x44,
  PD5 = 0x45,
  PD6 = 0x46,
  PD7 = 0x47,
  PD8 = 0x48,
  PD9 = 0x49,
  PD10 = 0x4a,
  PD11 = 0x4b,
  PD12 = 0x4c,
  PD13 = 0x4d,
  PD14 = 0x4e,
  PD15 = 0x4f,

  PE0 = 0x50,
  PE1 = 0x51,
  PE2 = 0x52,
  PE3 = 0x53,
  PE4 = 0x54,
  PE5 = 0x55,
  PE6 = 0x56,
  PE7 = 0x57,
  PE8 = 0x58,
  PE9 = 0x59,
  PE10 = 0x5a,
  PE11 = 0x5b,
  PE12 = 0x5c,
  PE13 = 0x5d,
  PE14 = 0x5e,
  PE15 = 0x5f,

  PF0 = 0x60,
  PF1 = 0x61,
  PF2 = 0x62,
  PF3 = 0x63,
  PF4 = 0x64,
  PF5 = 0x65,
  PF6 = 0x66,
  PF7 = 0x67,
  PF8 = 0x68,
  PF9 = 0x69,
  PF10 = 0x6a,
  PF11 = 0x6b,
  PF12 = 0x6c,
  PF13 = 0x6d,
  PF14 = 0x6e,
  PF15 = 0x6f,

  PG0 = 0x70,
  PG1 = 0x71,
  PG2 = 0x72,
  PG3 = 0x73,
  PG4 = 0x74,
  PG5 = 0x75,
  PG6 = 0x76,
  PG7 = 0x77,
  PG8 = 0x78,
  PG9 = 0x79,
  PG10 = 0x7a,
  PG11 = 0x7b,
  PG12 = 0x7c,
  PG13 = 0x7d,
  PG14 = 0x7e,
  PG15 = 0x7f,

  PH0 = 0x80,
  PH1 = 0x81,
  PH2 = 0x82,
  PH3 = 0x83,
  PH4 = 0x84,
  PH5 = 0x85,
  PH6 = 0x86,
  PH7 = 0x87,
  PH8 = 0x88,
  PH9 = 0x89,
  PH10 = 0x8a,
  PH11 = 0x8b,
  PH12 = 0x8c,
  PH13 = 0x8d,
  PH14 = 0x8e,
  PH15 = 0x8f,

  PI0 = 0x90,
  PI1 = 0x91,
  PI2 = 0x92,
  PI3 = 0x93,
  PI4 = 0x94,
  PI5 = 0x95,
  PI6 = 0x96,
  PI7 = 0x97,
  PI8 = 0x98,
  PI9 = 0x99,
  PI10 = 0x9a,
  PI11 = 0x9b,
  PI12 = 0x9c,
  PI13 = 0x9d,
  PI14 = 0x9e,
  PI15 = 0x9f

// младшие 4 бита - номер пина 0 - 15
// старшие 4 бита - номер порта 1 - 4, 0 - не назначен
} Typ_Port;

Отредактировано MasterElectric (2018-03-31 18:59:29)

2

вспомогательные функци - модификация части регистра:

Код:
void Modify1Msk(volatile uint32_t *tReg, uint32_t tData, uint32_t tPos)
{
uint32_t  tIndx;

  tIndx = tPos / 32;
  tPos = (tPos % 32);

  tReg[tIndx] = (tReg[tIndx] & ((uint32_t) ~(0x01 << tPos))) | ((tData & 0x01) << tPos);
}

void Modify2Msk(volatile uint32_t *tReg, uint32_t tData, uint32_t tPos)
{
uint32_t  tIndx;

  tIndx = tPos / 16;
  tPos = (tPos % 16) * 2;

  tReg[tIndx] = (tReg[tIndx] & ((uint32_t) ~(0x03 << tPos))) | ((tData & 0x03) << tPos);
}

void Modify4Msk(volatile uint32_t *tReg, uint32_t tData, uint32_t tPos)
{
uint32_t  tIndx;

  tIndx = tPos / 8;
  tPos = (tPos % 8) * 4;

  tReg[tIndx] = (tReg[tIndx] & ((uint32_t) ~(0x0f << tPos))) | ((tData & 0x0f) << tPos);
}

т.к. можно применять с любыми данными расположил отдельно в базовом модуле.

3

и сама функция конфигурирования:

Код:
*.h

#include <main.h>

void GPIO_SetMode(Typ_Port const tPin, uint32_t tCtrlPort);

*.c

GPIO_TypeDef *const GPIO_List[] = {
  NULL, GPIOA, GPIOB, GPIOC, GPIOD, GPIOE, GPIOF, GPIOG, GPIOH, GPIOI, GPIOJ, GPIOK
};

void GPIO_SetMode(Typ_Port const tPin, uint32_t tCtrlPort)
{
register uint32_t tPinPos = 0x0f & (uint8_t)tPin;
GPIO_TypeDef *tGPIO;

  if(tPin & 0xf0)
  {
   tGPIO = (GPIO_TypeDef*) GPIO_List[(0xf0 & (uint8_t)tPin) >> 4];

   Modify2Msk(&tGPIO->MODER, tCtrlPort, tPinPos);
   Modify1Msk(&tGPIO->OTYPER, tCtrlPort >> 2, tPinPos);
   Modify2Msk(&tGPIO->OSPEEDR, tCtrlPort >> 3, tPinPos);
   Modify2Msk(&tGPIO->PUPDR, tCtrlPort >> 5, tPinPos);
   Modify1Msk(&tGPIO->ODR, tCtrlPort >> 7, tPinPos);
   Modify4Msk(tGPIO->AFR, tCtrlPort >> 8, tPinPos);
  }
}

для применения к списку пинов (массиву):

Код:
void GPIO_SetModeList(Typ_Port const *tPins, uint32_t tCtrlPort);

void GPIO_SetModeList(Typ_Port const *tPins, uint32_t tCtrlPort)
{
  while(*tPins != P_NC) GPIO_SetMode(*tPins++, tCtrlPort);
}

Отредактировано MasterElectric (2018-03-30 20:40:54)

4

пример применения:

Код:
/*
для конфигурации пинов под LCD создали 2 списка
*/

Typ_Port const Pin_LTDC_AF14_List[] = {
  PH2,  // LCD_R0
  PH3,  // LCD_R1
  PH8,  // LCD_R2
  PH9,  // LCD_R3
  PH10, // LCD_R4
  PH11,  // LCD_R5
  // PA12,  // LCD_R5
  PG6,  // LCD_R7

  PE5,  // LCD_G0
  PE6,  // LCD_G1
  PH13, // LCD_G2
  PH15, // LCD_G4
  PI0,  // LCD_G5
  PI1,  // LCD_G6
  PI2,  // LCD_G7

  PE4,  // LCD_B0
  PG12, // LCD_B1
  PD6,  // LCD_B2
  PG11, // LCD_B3
  PI4,  // LCD_B4
  PI5,  // LCD_B5
  PI6,  // LCD_B6
  PI7,  // LCD_B7

  PG7,  // LCD_CLK
  PI10, // LCD_HSYNC
  PI9,  // LCD_VSYNC
  PF10, // LCD_DE

  P_NC
};

Typ_Port const Pin_LTDC_AF9_List[] = {
  // PB0,   // LCD_R3
  PB1,    // LCD_R6
  PG10,   // LCD_G3
  // PG12,  // LCD_B4

  P_NC
};


  // применяем функции
  GPIO_SetModeList(Pin_LTDC_AF14_List, (gpAlternateMode | gpPushPull | gpSpeedHigh | gpNoPull | gpAF14));
  GPIO_SetModeList(Pin_LTDC_AF9_List, (gpAlternateMode | gpPushPull | gpSpeedHigh | gpNoPull | gpAF9));
  GPIO_SetMode(PA3, (gpGeneralMode | gpPushPull | gpSpeedHigh | gpODR_Low));

может и медленно, но вполне удобно.
ссылка на модули: https://drive.google.com/open?id=1wBGT0 … nNP9IRrDXM

Отредактировано MasterElectric (2018-03-30 20:58:38)

5

gpSpeedVeryHigh куда дел? Сейчас у тебя 3 режима для OSPEEDR, как у F0 и т.д., а у F4 их четыре.

6

может и медленно, но вполне удобно

Таких попыток "удобно" уже штук 1000500 было... а вот не прижились в итоге... ни в каком виде... однако...

7

Есть подобные макросы под stm32f0xx , нужны ?

8

Reflector Спасибо подправил.

9

CERGEI Есть, но я бы взглянул на твои.

Отредактировано MasterElectric (2018-03-31 19:02:20)

10

HHIMERA Мои у меня прижились)))

11

Попались на просторах интернета,применил пару раз,оцените

Код:
// Режим работы
#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);

12

CERGEI Как по мне слишком много передаваемых параметров.

13

Зато прозрачность, нету столько переменных, открыл сразу видно что от чего зависит.

14

И на выходе... безликий и бестолковый текст... типа...

GPIO_SET
GPIO_RESET

15

Я вот если честно не совсем понимаю причин для использования такого кода.  Я понимаю тех кто пишет исключительно на CMSIS,  понимаю тех кто использует SPL. 

Если мы делаем некую обертку для более высокого уровня абстракции, то в чем преимущество такого кода перед SPL ?

16

Сколько людей, столько и мнений :)

17

Atomic-dm написал(а):

Если мы делаем некую обертку для более высокого уровня абстракции, то в чем преимущество такого кода перед SPL ?

Допустим часть какого-то порта должна работать как на выход, так и на вход, причем желательно переключаться между этими режимами побыстрее, как ты это реализуешь в SPL? В принципе достаточно подправить только MODER, но если делать это посредством GPIO_Init(), то там внутри цикл на 16 итераций, даже если нам нужно настроить всего 1 пин. Это в десятки раз медленнее даже если отключены assert_param, которые там проверяют валидность входящих параметров. В дебаге это наверно может быть медленнее и в сотню раз...

Отредактировано Reflector (2018-04-06 12:36:55)

18

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

19

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

MasterElectric, я просто сказал что мне непонятна необходимость такой инициализации . При этом я допускаю вариант, что я просто еще не сталкивался с тем где это действительно было бы мне удобно.

20

Atomic-dm написал(а):

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

Работать с регистрами не удобно, чревато ошибками и плохо переносимо, а SPL/HAL дают медленный и раздутый код, потому народ свои велосипеды и пишет. Конкретно код представленный в этой теме, на мой взгляд, весьма далек от идеала, но в принципе ничто не мешает написать такую реализацию, которая была бы эффективнее SPL и при этом не пришлось бы напрямую иметь дело с регистрами.

21

Подобное "ничто не мешает" очень смахивает на Неуловимого Джо... типа... почему он неуловимый...


Вы здесь » Микроконтроллеры » Архив » STM32F4xx: GPIO (мой вариант работы)