Top.Mail.Ru

Часть 10. STM32 и C++. GPIO на классах. Изменения в библиотеке.

Давно назревала ситуация изменить немного библиотеку. Раньше я подключал 4-х, 8-ми выводные интерфейсы к строго определённым выводам. Это накладывало некоторые ограничения на написание драйверов для дисплеев. Приходилось заниматься ногодрыжеством. Решил положить этому конец и немного модернизировать библиотеку. Изменения коснулись файлов gpio_main.* и IO_Digital.*.

В чём суть изменения? Возьмём LCD1602, подключенный по 8-ми битному интерфейсу. Его можно было подключить только так:

Или только так:

Сейчас можно будет подключить начиная с любого вывода:

Единственное условие, выводы должны идти подряд. Я не стал делать выводы вразнобой и на разные порты как это делают на Arduino. У STM достаточно периферии и возможности переназначить её на нужные выводы, чтобы обращать внимание на разные мелочи.

Теперь об изменениях. В gpio_main.h описываются две функции:

// Работа с данными с разрядностью от 2-х до 16-ти.
void _DigitalWrite(GPIO_TypeDef *GPIOx_Set, uint16_t Data, uint8_t FirstPin, uint16_t PinMask);     // Запись 16-ти разрядных данных
uint16_t _DigitalRead(GPIO_TypeDef *GPIOx_Set, uint8_t FirstPin, uint16_t PinMask);                 // Чтение 16-ти разрядных данных

Первая пишет слово нужной разрядности, вторая читает слово нужной разрядности. Обоим функциям передаются:

  • порт
  • номер первого (младшего бита)
  • маска для выделения нужных бит

В _DigitalWrite() кроме этого передаются данные, а _DigitalRead() возвращает 16-ти битное значение, в разрядах которого будут готовые данные для использования в программе. Если первый пин не нулевой, то считанные данные будут сдвинуты так, что мы всё равно получим их в таком виде, будто наш интерфейс начинается с нулевого пина. Так же организована и запись в порт. Запись в порт изменит только указанные пины и не затронет другие.

Реализация в gpio_main.cpp:

void _DigitalWrite(GPIO_TypeDef *GPIOx_Set, uint16_t Data, uint8_t FirstPin, uint16_t PinMask)
{
  GPIOx_Set->ODR &= ~PinMask;                                                   // Сбрасываем выводы соответствующие пулу наших выводов
  GPIOx_Set->ODR |= ((Data << FirstPin) & PinMask);                             // Выводим
}

uint16_t _DigitalRead(GPIO_TypeDef *GPIOx_Set, uint8_t FirstPin, uint16_t PinMask)
{
  return ((GPIOx_Set->IDR & PinMask) >> FirstPin);
}

Как видно, ничего особенного в этих функциях нет. В IO_Digital.h тоже нет ничего особенного. Изменились немного конструкторы и добавились четыре функции:

IO_Digital(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, bool IO, uint8_t PULL);              // Инициализация порта
IO_Digital(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, bool Edge);                          // Инициализация порта на выход с предустановкой его в одно из состояний
IO_Digital(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t Size, bool IO, uint8_t PULL);// Инициализировать последовательность выводов порта GPIOx, начиная с GPIO_Pin, количество Size

void      digitalWriteByte(uint16_t Data);
uint16_t  digitalReadByte(void);
// Дополнительные функции, используемые в двунаправленных интерфейсах
void      setInput(void);                                                               // Переводит ранее проинициализированные выводы на вход
void      setOutput(void);                                                              // Переводит ранее проинициализированные выводы на выход

В конструкторе, инициализирующем параллельный вывод, добавилась переменная Size, в которой нужно указать сколько бит будем использовать. В GPIO_Pin указываем самый младший бит интерфейса.

Например, инициализация порта для третьего рисунка будет выглядеть следующим образом:

IO_Digital Parallel(GPIOB, GPIO_PIN_3, 8, Output, No_Pull); // Инициализируем 8 выводов с B3 по B10 на выход

А запись в него:

Parallel.digitalWriteByte(0x03); // Выводим 0х03 на параллельный выход

И чтение:

uint8_t Temp = (uint8_t) digitalReadByte();

Мы задали переменную Temp 8-ми битовой беззнаковой, так как в этом случае у нас считывается всего 8 бит.

Так как при написании интерфейсов дисплеев и другой периферии с параллельным интерфейсом у нас выводы могут быть настроены как на выход, так и на вход, введены ещё две функции setInput() и setOutput(), переинициализирующие порты. Они обращаются к известной нам функции _SetPin() из файла gpio_main.*. Конечно, повторять полную переинициализацию выводов как-то не комильфо, но это упрощает код, независимый от ядра МК. Да и скорость переинициализации довольно высока, дисплей простаивать не будет. Если посмотреть функции в IO_Digital.*, можно увидеть, что они практически ничего не делают, просто обращаются к функциям из gpio_main.*. Сделано так только для того, чтобы работа с портами была независима от периферии GPIO МК. Так как gpio_main.* зависима от типа МК, а IO_Digital.* нет.

Расписывать как и что делается не буду. Там всё просто, и можно всё понять из комментариев. И не забывайте, защиты от дурака нет. И после каждой статьи или правки библиотек, нужно скачивать данные с Яндекс Диска.

Пример работы с параллельным интерфейсом лежит в каталоге WorkDevel\Developer\Tests MK\F407F407VExx_GPIO_Multi.

Библиотеки и примеры на Яндекс Диске и архивом WorkDevel.

Тема на форуме - перейти.

Подписаться
Уведомить о
guest

2 комментариев
Старые
Новые
Межтекстовые Отзывы
Посмотреть все комментарии
Payne
2 лет назад

Спасибо за материалы!

Что-то я никак не мог собрать, оказалось сглупил - скачал только файлы проекта, без библиотек, так что надо быть внимательнее )

2
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x