Что является неотъемлемой частью большого количества электронных девайсов? Конечно, средства индикации и графического вывода данных. Пользователю всегда удобнее и приятнее когда результат работы можно увидеть визуально. Поэтому сегодня мы подключим к STM32 дисплей для вывода текста и цифр. Героем наших экспериментов станет довольно-таки популярный дисплей WH1602 от Winstar’а. Вот кстати в комментариях появилось важное уточнение, что методика в принципе одинакова для всех дисплеев на базе HD44780. Спасибо JekaKey за важное дополнение!
Для начала дисплей надо собственно подключить к контроллеру. Скачиваем даташит и ищем распиновку. Вот смотрите:
Как вы уже поняли, WH1602 имеет 16 выводов. Рассмотрим каждый в отдельности…
- Пины Vss, Vdd и K нужно подключать к земле и к питанию, то есть прямо так, как указано в таблице, тут без сюрпризов и даже нечего обсуждать.
- Вывод под номером 3 служит для регулировки контрастности – если подадим туда +5В, то не увидим абсолютно ничего, а если закоротим вывод на землю, то будем любоваться двумя рядами черных квадратов... Естественно, это нас не устраивает, поэтому туда надо повесить потенциометр (резистор с переменным сопротивлением) для регулировки контрастности. Самая лучшая видимость символов обеспечивается напряжением 0.5-0.7 В на этом выводе дисплея.
- Пин RS – это уже вывод, которым мы сами будем управлять при помощи микроконтроллера. Низкий уровень напряжения (0) на этом выводе означает, что сейчас последует команда, высокий уровень (1) – значит сейчас будут данные для записи в память дисплея.
- Пин R/W – тут понятно, либо мы читаем данные (флаг занятости дисплея, например), в этом случае на этом выводе 1, либо записываем команду/данные в дисплей, тогда тут у нас 0.
- DB7 – DB0 – шина данных, и этим все сказано )
- Пин E – так называемый Enable signal. Нужен он вот для чего. Чтобы работать с дисплеем - записывать данные или подавать команду – нам надо выдать на этот вывод положительный импульс. То есть, процедура будет выглядеть следующим образом:
- На пины RS, R/W, DB7 – DB0 – нужные сигналы, соответствующие нашей команде.
- Подаем высокий уровень на вывод E.
- Ждем (по даташиту – не менее 150 нс).
- Подаем на вывод E низкий уровень.
- На ножку A/Vee надо подать 4.2 В для питания подсветки дисплея.
С подключением разобрались, но прежде, чем переходить к примеру, рассмотрим, какие вообще команды понимает наш дисплей. Для этого лезем в даташит и находим интересную таблицу:
Тут описаны все команды и сигналы, которые должны быть на соответствующих выводах WH1602 для каждой конкретной команды. Вот хотим мы, например, очистить дисплей, смотрим в таблицу, и вот она нужная команда - Clear Display.
Подаем на выводы RS, R/W, DB7, DB6, DB5, DB4, DB3, DB2, DB1 нули, а на ножку DB0 - единицу. Готово, что дальше? Верно, единицу на пин E, затем ожидаем некоторое время и снова опускаем E в ноль. Все, дисплей очищен. Только перед выполнением следующей команды необходимо выдержать паузу, указанную в даташите для каждой команды. Более эффективным будет опрос флага занятости, как только он сбросился в 0 – можно работать дальше. Для чтения этого флага тоже есть специальная команда, так что и с этим все понятно. Идем дальше…
А, собственно, с теорией все, можно уже что-нибудь попробовать написать. Я для облегчения работы сделал небольшую библиотеку для дисплея WH1602 (она подходит и для других дисплеев на базе HD44780), сейчас посмотрим, как ее можно использовать. Для начала скачиваем:
Время традиционной вставки: поскольку компания STMicroelectronics прекратила поддержку библиотеки SPL, которая использовалась в этом курсе, я создал новый, посвященный работе уже с новыми инструментами, так что буду рад видеть вас там - STM32CubeMx. Кроме того, вот глобальная рубрика по STM32.
Получаем в свое распоряжение 2 файла, MT_WH1602.c и MT_WH1602.h. Отрываем второй, тут нам надо произвести выбор выводов и используемого контроллера.
Дисплей у меня, кстати, подключен так:
- RS – PC2
- R/W – PB10
- E – PB14
- DB7 – PD2
- DB6 – PC12
- DB5 – PA8
- DB4 – PA10
- DB3 – PA15
- DB2 – PD11
- DB1 – PA3
- DB0 – PA5
Открываем файл MT_WH1602.h:
/***************************************************************************************/ // Choose your platform /***************************************************************************************/ #define STM32F10x (0) #define STM32F4xx (1) #define PLATFORM (STM32F4xx)
Тут нам надо выбрать STM32F4xx или STM32F10x мы будем использовать. То есть либо пишем:
#define PLATFORM (STM32F4xx)
либо:
#define PLATFORM (STM32F10x)
Далее выбираем выводы микроконтроллера, к которым у нас подключен дисплей. Только сначала зададим, какие порты у нас задействованы. Вот при моем подключении используются GPIOA, GPIOB, GPIOC и GPIOD, пишем:
// Configure which GPIO are in use // If GPIOA is in use set this constant TRUE #define GPIOA_IS_IN_USE (TRUE) // If GPIOB is in use set this constant TRUE #define GPIOB_IS_IN_USE (TRUE) // If GPIOC is in use set this constant TRUE #define GPIOC_IS_IN_USE (TRUE) // If GPIOD is in use set this constant TRUE #define GPIOD_IS_IN_USE (TRUE) // If GPIOE is in use set this constant TRUE #define GPIOE_IS_IN_USE (FALSE)
Осталось выбрать ножки. Например, RS у меня на выводе PC2, значит:
// Select which pins are connected to the WH1602 module // Set RS port #define MT_WH1602_RS_PORT (GPIOC) // Set RS pin #define MT_WH1602_RS_PIN (GPIO_Pin_2)
Аналогично для других ножек микроконтроллера.
С настройкой покончили, продолжаем! Для вызова команд, приведенных в начале статьи в файле MT_WH1602.c содержатся следующие функции (их имена соответствуют названиям команд):
void MT_WH1602_ClearDisplay(void); void MT_WH1602_ReturnHome(void); void MT_WH1602_EntryModeSet (bool IDaddress, bool shift); void MT_WH1602_DisplayOnOff (bool Dbit, bool Cbit, bool Bbit); void MT_WH1602_CursorOrDisplayShift (bool SCbit, bool RLbit); void MT_WH1602_FunctionSet (bool DLbit, bool Nbit, bool Fbit); void MT_WH1602_SetCGRAMAddress (uint8_t address); void MT_WH1602_SetDDRAMAddress (uint8_t address); bool MT_WH1602_ReadBusy(void); void MT_WH1602_WriteData(uint8_t data);
Для некоторых команд нам нужно передать в функцию параметры, вот, например:
void MT_WH1602_DisplayOnOff (bool Dbit, bool Cbit, bool Bbit);
Смотрим в таблицу команд:
Видим, что командой Display ON/OFF не только включать/выключать дисплей, но также активировать/деактивировать курсор и мигание курсора. В даташите эти биты команды обозначены как D,C и B, их то мы и передаем в качестве параметров в функцию. Если нам нужно включить дисплей и курсор, но отключить мигание курсора, вызываем команду следующим образом:
MT_WH1602_DisplayOnOff(1, 1, 0);
В общем, все просто. Итак, создаем новый проект, добавляем библиотеку, создаем пустой .c файл и начинаем заполнять его кодом:
/***************************************************************************************/ // Подключаем файл библиотеки #include "MT_WH1602.h" /***************************************************************************************/ int main(void) { // Вызываем функцию инициализации, без этого никуда MT_WH1602_Init(); // Теперь надо произвести начальную конфигурацию дисплея, // документация рекомендует делать так MT_WH1602_FunctionSet(1, 0, 0); MT_WH1602_Delay(1000); MT_WH1602_FunctionSet(1, 0, 0); MT_WH1602_Delay(1000); MT_WH1602_FunctionSet(1, 0, 0); MT_WH1602_Delay(1000); MT_WH1602_FunctionSet(1, 1, 1); MT_WH1602_Delay(1000); MT_WH1602_DisplayOnOff(1, 0, 0); MT_WH1602_Delay(1000); MT_WH1602_ClearDisplay(); MT_WH1602_Delay(2000); // Я тут значения задержки для примера взял первые пришедшие в голову... // Вообще нужно проверять флаг занятости дисплея // Давайте теперь выведем что-нибудь на дисплей, например название нашего сайта MT_WH1602_WriteData(0x6D); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x69); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x63); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x72); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x6F); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x74); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x65); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x63); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x68); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x6E); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x69); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x63); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x73); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x2E); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x72); MT_WH1602_Delay(100); MT_WH1602_WriteData(0x75); MT_WH1602_Delay(100); while(1) { __NOP(); } } /***************************************************************************************/
Готово, проверяем:
Как видите, все работает правильно!
Кстати я как то упустил из виду вопрос о том, что же писать в дисплей, чтобы вывести тот или иной символ. Вот табличка из даташита:
Так вот, чтобы определить какое значение записать в память дисплея, нужно для конкретного символа взять числа, написанные сверху и слева в этой таблице. Например, символ "А". Смотрим - этому символу соответствует колонка 0100 (0х4) и строка 0001 (0х1). Получается, что для вывода символа "А" нужно записать в дисплей значение 0х41.
Вот теперь вроде все... Мы успешно осуществили подключение дисплея на базе HD44780, так что до скорых встреч в новых статьях )
P. S. Я при работе с библиотекой не тестировал функцию чтения флага занятости, так что, если вдруг что-то будет работать не так, как надо, пишите, будем разбираться.