Урок 6
Библиотека HAL. LCD 20×4. 4-битный режим
Сегодня мы начинаем работать с подключением дисплея. Так как светодиоды — это очень красиво, но нужно ещё как-то и информацию какую-то смотреть.
Мы будем подключать дисплей на контроллере HD44780, хотя в даташите указан S6A0069, который является также аналогом первого. Дисплей отображает символьную информацию размерностью 4 строки по 20 символов. И подключать мы будем данный дисплей 4-битным образом. Так мы можем сэкономить 4 ножки порта. В данном режиме мы передаем данные по половине байта, сначала старшую часть, потом младшую.
Выглядит данный дисплей следующим образом:
Дисплей к микроконтроллеру STM32F407VG мы подключим вот таким образом (нажмите на картинку для увеличения изображения)
На схеме изображен индикаотор 20х2, но смысл тот же, просто в программе не нашлось дисплея 20х4.
По подключению дисплея и работе с ним в 4-битном режиме можно подробно посмотреть в уроке по AVR, в котором мы рассматривали дисплей 16х2.
Но всё равно я должен напомнить, что GND и VCC зачастую могут меняться местами в зависимости от типа дисплея, поэтому смотрите документацию именно на свой дисплей, иначе он неминуемо выйдет из строя.
В принципе тем самым мы разобрали уже назначение 1 и 2 ножек модуля дисплея.
Назначение следующих ножек модуля:
3 — V0 — это ножка, с помощью которой регулируется контрастность дисплея. То есть контрастность дисплея будет зависеть от поданного напряжения на данную ножку. Как правило берётся переменный резистор на 10 килоом, подключенный крайними ножками на общий провод и на питание, а с центральной ножки данного резистора провод идёт как раз на ножку V0 и посредством регулировки движка резистора мы и регулируем контрастность дисплея в модуле.
4 — RS — это такая хитрая ножка, с помощью которой контроллер дисплея будет «знать», какие именно данные нахдятся на шине данных. Если мы подадим на данную ножку логический 0, то значит будет команда, если 1 — то это данные.
5 — RW — данная ножка в зависимости от логического состояния на ней говорит контроллеру дисплея, будем мы с него читать или будем мы в него писать данные. Если будет 0 — то мы в контроллер дисплея будем писать, а если 1 — то будем читать данные из контроллера дисплея. Данная функция используется редко. Как правило мы всегда только пишем данные в дисплей. Чтение обычно требуется для того, чтобы определить, что дисплей принял наши данные, либо чтобы определить состояние. Но существуют определённые тайминги, позволяющие нам на слово «верить» котнроллеру дисплея, что он наши данные принял и обработал. Также читать мы можем из памяти дисплея, что, в принципе, незачем. Поэтому мы обычно соединяем данный контакт с общим проводом.
6 — E — это так называемая стробирующая шина, по спадающему фронту (когда 1 меняется в 0) на которой контроллер дисплея понимает, что именно сейчас наступил момент чтения данных на ножках данных D0 — D7, либо передачи данных из модуля в зависимости также от состояния ножки RW.
Ножки D0 — D7 — это параллельная восьмибитная шина данных, через которую и передаются или принимаются данные. Номера 0 — 7 соответствуют одноименным битам в байте данных. Но также есть ещё 4-битный способ передачи данных в контроллер и из контроллера дисплея, когда используются только ножки данных D4 — D7, а ножки D0 — D3 уже не используются. Как правило такой способ используется в целях экономии ножек порта и именно такой способ мы и будем сегодня использовать, так как мы теряем скорость вдвое, но у нас дисплей символьный и спешить нам некуда. В 4-битном режиме мы передаём или принимаем байт в 2 приёма по половинке, сначала старшую часть байта, затем младшую.
Ножки A и K — это анод и катод для подачи напряжения для питания светодиодной подсветки дисплея. Как правило можно питать от 5 вольт, и от 3 вольт, но желательно поставить токоограничивающий резистор на 100 ом и скорее всего тогда подсветка дисплея «проживёт» дольше. Всё это обычно указывается в технической документации на дисплей. Мы поставим на 330.
Для данных мы будем использовать порт D, а для урпавления — 8 и 9 ножки порта B.
Чтобы нам получить определённый символ в определённом месте дисплея, нам необходимо код данного символа отправить по определённому адресу в память DDRAM. Для этого в технической документации существует вот такая информация
Здесь мы видим, по какому адресу нам нужно будет отправлять байт с кодом символа.
Также в документации нам интересны будут команды управления дисплеем. Существует таблица, разъясняющая данные команды, для чего какая нужна, а также сколько времени необходимо для выполнения данной команды или инструкции
Также условием выполнения команды именно с таким временным интервалом является тактирование контроллера дисплея частотой 270 кГц.
Также чтобы запустить наш дисплей в работу, так как там существует контроллер, нужна будет первоначальная инициализация. В докуентации, правда в другой — на дисплей 1602 есть порядок инициализации. Он ничем не отличается от инициализации дисплея 2004. Для 4-битного способа подключения он такой
Проект MYLCD80 я сделал из TEST002 таким же образом, как и на прошлом занятии.
Открываем MYLCD80 в кубе. Отключаем таймер TIM6
Также за ненадобностью отключим лапки PA1,PD12,PD13,PD14,PD15
Включим на выход лапки PB8, PB9,PD0,PD4,PD5,PD6,PD7
В настройках вообще ничего не трогаем. Генерируем проект, собираем.
Видим ошибки на наш ручной код по таймеру, убираем строку
tim6_counter=0;
а также всё написанное нами из бесконечного цикла.
Из main.h переменную
uint8_t tim6_counter;
Подключаем новые файлы lcd.h и lcd.c с содержимым:
lcd.h:
#ifndef LCD_H_
#define LCD_H_
#include «stm32f4xx_hal.h»
#endif /* LCD_H_ */
lcd.c:
#include «lcd.h»
Затем подключаем lcd.h к main.h
main.h:
#include «lcd.h»
Напишем дефайны в файл lcd.h, для удобного управления уровнями на ножках данных
//—————————————-
#define d4_set() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_SET)
#define d5_set() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_SET)
#define d6_set() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_SET)
#define d7_set() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_SET)
#define d4_reset() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_4, GPIO_PIN_RESET)
#define d5_reset() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_5, GPIO_PIN_RESET)
#define d6_reset() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_6, GPIO_PIN_RESET)
#define d7_reset() HAL_GPIO_WritePin(GPIOD, GPIO_PIN_7, GPIO_PIN_RESET)
#define e1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_SET) // установка линии E в 1
#define e0 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, GPIO_PIN_RESET) // установка линии E в 0
#define rs1 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_SET) // установка линии RS в 1 (данные)
#define rs0 HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, GPIO_PIN_RESET) // установка линии RS в 0 (команда)
//—————————————-
Смотрим выше таблицу, как инициализировать 4-битный режим и собственно пишем инициализацию.
Начинаем с задержки
void LCD_ini(void)
{
HAL_Delay(40);
}
Пишем на нее прототип в заголовочный файл.
Затем нам нужно написать функцию записи данных в память дисплея
void LCD_WriteData(uint8_t dt)
{
if(((dt >> 3)&0x01)==1) {d7_set();} else {d7_reset();}
if(((dt >> 2)&0x01)==1) {d6_set();} else {d6_reset();}
if(((dt >> 1)&0x01)==1) {d5_set();} else {d5_reset();}
if((dt&0x01)==1) {d4_set();} else {d4_reset();}
}
Напишем функцию для небольшой задержки
void delay(void)
{
uint16_t i;
for(i=0;i<1000;i++)
{
}
}
Теперь напишем функцию для команд
void LCD_Command(uint8_t dt)
{
rs0;
LCD_WriteData(dt>>4);
e1;
delay();
e0;
LCD_WriteData(dt);
e1;
delay();
e0;
}
Затем продолжим писать функцию инициализации дисплея, поглядыая время от времени в техническую документацию и в урок по AVR, где было рассказано уже про каждую команду
void LCD_ini(void)
{
HAL_Delay(40);
rs0;
LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(1);
LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(1);
LCD_WriteData(3);
e1;
delay();
e0;
HAL_Delay(1);
LCD_Command(0x28);//режим 4 бит, 2 линии (для нашего большого дисплея это 4 линии), шрифт 5х8
HAL_Delay(1);
LCD_Command(0x28);//еще раз для верности
HAL_Delay(1);
LCD_Command(0x0F);//дисплей включаем (D=1), также включаем пока все курсоры
HAL_Delay(1);
LCD_Command(0x01);//уберем мусор
HAL_Delay(2);
LCD_Command(0x06);//пишем влево.
HAL_Delay(1);
LCD_Command(0x02);//возврат курсора в нулевое положение
HAL_Delay(2);
}
Вызываем инициализацию из главного модуля
/* USER CODE BEGIN 2 */
LCD_ini();
/* USER CODE END 2 */
Прошиваем, смотрим.
Как мы видим, дисплей нормально инициализировался. Видим мы это по периодическому миганию курсора в верхнем левом углу.
На следующем занятии мы попытаемся уже вывести какую-то информацию на дисплей, сначала посимвольно, а потом и целыми строками.
Предыдущий урок Программирование МК STM32 Следующий урок
Техническая документация:
Отладочную плату можно приобрести здесь STM32F4-DISCOVERY
Смотреть ВИДЕОУРОК в RuTube
Смотреть ВИДЕОУРОК в YouTube
Здравствуйте. А почему функция записи данных в память дисплея именно так делается, почему нельзя отправлять число сразу в ODR?
Здравствуйте! Мы изучаем применение библиотеки HAL, а не запись в регистры напрямую. Можно конечно и напрямую бесконтрольно, но тогда это не будет соответствовать теме.
У Вас на схеме подключения индикатора ошибка — к сигналу R/S подлючен анод подсветки индикатора. Нужно соеденительную точку чуть ниже опустить — на линию Vdd
Непонятно вот это:
LCD_Command(0x28);//режим 4 бит, 2 линии (для нашего большого дисплея это 4 линии), шрифт 5х8
Каким образом можно передать число 0x28 по четырехбитной линии? В двоичной форме это 101000 и единица на четвертой справа позиции должна уйти от контроллера к дисплею по линии d3, которая у нас не подключена.
Вместо 0x28 путем подбора мне получилось победить дисплей завелся на 0x20
Здравствуйте!
Попытался «переделать» проект под STM32F103C8T6 (отладочная платка с Китая). Настроил порты а Cube, добавил .c и .h файлы из вашего проекта, переправил в .h файле «дефайны» под свои порты. Тактирование от внешнего кварца (8 МГц). Проект собирается нормально, но на дисплее белиберда какая-то. Скажите что я мог сделать не так?
Здравствуйте! Поиграйте с задержками. Порой китайские дисплеи (верней не порой а зачастую) даташитам не подчиняются.
Спустя некоторое время всё-таки вывел текст на экран. Немного поменял алгоритм инициализации и временные задержки.
Спасибо за ваше внимание и труд! Очень интересные и полезные уроки, даже не смотря на «мои танцы с бубном», так сказать. Где-то на видео вы сказали примерно следующее:»Не научившись находить ошибки не научишься программировать» — совершенно с вами согласен! 🙂
Тоже мучаюсь с STM32F103xx , подскажи что именно ты изменил а в алгоритме и какие задержки?
Привет nArod, может ли этот код использоваться для stm32f103c8t?
Здравствуйте!
Можно, но только с задержкой скорей всего другой делитель будет.
По схеме: нога 15(NC_1) дисплея через токоограничивающий резистор подключена к 95(PB8) mc и 4(RS) дисплея. Нужно на 19(Vdd) mc и на 2(Vcc) дисплея. (На 5 вольт). Видимо на схеме ошибка.
Здравствуйте Narod Stream.
Подскажите пожалуйста.Вы подключили дисплей на 5 вольт,а stm32 на 3 вольта?И проблем не возникает?
Например:для управления дисплея как мы понимаем нужен 5 вольт, но stm32 выдает с ног всего лишь по 3 вольта.И другие подобные примеры в связи разницей напряжения.
Спасибо.
Здарвствуйте!
Для управления достаточно и 3х, а вот для питания подсветки желательно 5, особенно если дисплей 20х4.
подключил дисплей от 3.3 вольт. для контрастности маловато получилось буквы еле видно
Надо от 5 вольт.
Добрый день! Не инициализируется дисплей, черные квадраты в верхнем ряду. Задержки менял, не помогает. Под авр работает, здесь нет…Подозреваю что конкретно мой дисплей не работает с 3 вольтами.
Добрый день!
При «линейном программировании» все работает, при попытке использовать библиотеку LCD в Free RTOS не работает.
В чем причина?
Скорей всего потеря значений, так как функции занимают очень продолжительное время (в один квант не влезают) и поэтому вызов идёт из нескольких задач одновременно. Вместо глобальных переменных здесь уже надо использовать параметры или очереди, а также задержки стандартные обязательно заменить на RTOS-овские. А, ещё глобальные переменные полностью исключить.
Добрый вечер! У меня LCD на контроллере KS0066U, для перехода на 4 битный режим надо на DB5 отправить два раза 1, как учесть это в программе не понимаю, это мой первый проект, помогите пожалуйста.
Библиотеку так и не смог запустить, то работает то не работает, хотя просидел весь вечер)) Но все равно спасибо, я как начинающий кое что узнал. В итоге запустил другую с гитхаба. В этой все же не понравился момент с таймером привязанным к тактовой частоте.
И еще один очень важный момент !!! -величина напряжений на информ. выводах дисплея, должна быть равна питанию дисплея. т.е. если у вас дисплей может работать от 3.3 вольт, то все ок, а если работает только от 5 вольт, то надо сделать преобразователь уровня на транзисторах к примеру. Если это правило не соблюсти, то некоторые экземпляры отказываются работать и это сбивает с толку.
Для дисплеев Winstar на контроллере ks0066u
void LCD_Command(uint8_t dt)
{
rs0;
LCD_WriteData(dt);
e1;
delay();
e0;
}
//—————
void LCD_ini(void)
{
HAL_Delay(40);
LCD_Command(2);
LCD_Command(2);
LCD_Command(3);
HAL_Delay(40);
LCD_Command(0);
LCD_Command(15);
HAL_Delay(40);
LCD_Command(0);
LCD_Command(1);
HAL_Delay(40);
LCD_Command(0);
LCD_Command(7);
}
А ты хорош, спасибо!
Друзья! Такой вопрос. В даташите указаны задержки, например, для очистки дисплея 1.52 мс. при тактовой частоте 270 кГц. Не могу уловить, где именно мы задаем тактовую частоту. Получается, что при инициализации мы в ручную дергаем стробирующую ногу МК (е1 и е0). И что самое интересное, мы записываем команду «очистки дисплея» опускаем в нуль стробирующую ногу и ждем положенное время. При этом тактового сигнала нигде нет.
Такой дотошный вопрос, поскольку после процедуры инициализации индикатор никак не хочет оживать.
Команды взяты из документации от МЭЛТ MT16S2D. Инициализировался только так.
HAL_Delay(40);
rs0;
LCD_WriteData(30);
e1;
delay();
e0;
HAL_Delay(40);
LCD_WriteData(30);
e1;
delay();
e0;
HAL_Delay(40);
LCD_WriteData(30);
e1;
delay();
e0;
HAL_Delay(40);
LCD_WriteData(0x02); // команда режим 4 бит
e1;
delay();
e0;
HAL_Delay(40);
LCD_command(0x28); // команда режим 4 бит,
HAL_Delay(1);
LCD_command(0x08); // команда выключить дисплей
HAL_Delay(1);
LCD_command(0x01); // команда очистить дисплей
HAL_Delay(2);
LCD_command(0x0F); // команда включить дисплей, включить курсор
HAL_Delay(1);
Валентин, спасибо! Ваш код для задержек помог.
Автору спасиБог за уроки!
Здравствуйте. Если дисплей изначально в 8-битном режиме, то вместо трижды LCD_WriteData(3);
надо слать трижды
LCD_WriteData(0x30);
же?
Спасибо за уроки!
Доброго дня. Исправьте схему в текстовом уроке. У вас резистор подсветки подключен одним концом не к питанию, а к шине RS он же PB8….