Hd44780 datasheet на русском

Символьный ЖКИ на базе контроллера HD44780


Рис.1 ЖКИ на базе контроллера HD44780

По отношению к обыкновенным 7-сегментным, ЖКИ модули на базе контроллера HD44780 обладают на порядок большими возможностями. Количество строк на экране у разных моделей — 1,2 или 4; число символов в строке: 8,10,16,20,24,30,32 или 40. Каждое знакоместо на дисплее представляет собой матрицу размером 5×8 точек. Индикатор может иметь светодиодную или люминесцентную подсветку практически любого цвета свечения. На рис.1 показан внешний вид модуля A162-D фирмы Ampire с разрешением 16 символов x 2 строки. Напряжение питания контроллера HD44780 5В (реже 3В). Ток потребления контроллера очень мал(100…200 мкА), чего не скажешь о светодиодной подсветке. В зависимости от производителя, его величина составляет 80…120 мА. Для работы некоторых типов ЖКИ может потребоваться дополнительный источник напряжения отрицательной полярности. Технология производства модулей подобного рода непрерывно совершенствуется, что, в целом, положительно сказывается на их размерах и электрических характеристиках.


Рис.2 Таблица символов CGRAM

Изначально HD44780 имеет предопределенную таблицу символов, размещенную в ОЗУ знакогенератора CGRAM (Character Generator RAM). Для отображения любого из них программа микроконтроллера должна передать координаты позиции и, непосредственно за ними, сам адрес символа из CGRAM. Пример таблицы CGRAM приведен на рис.2. Заглавные и прописные буквы латинского алфавита, числовые знаки, а также большинство знаков препинания совпадают в ней с кодами ASCII. Набор символов, размещенных по адресам 0xA0…0xFF, содержит национальный алфавит (в данном случае кириллицу) того региона, где предполагается его использование. Первые 16 ячеек CGRAM имеют особое значение. При желании, в них могут быть записаны любые пользовательские символы, которых нет таблице (сразу после включения модуля в них находится случайная информация). Упростить преобразование строки, состоящей из букв русского и английского алфавитов, в набор кодов HD44780, можно с помощью утилиты «HD44780» (внешний вид на рис.3). Все, что делает эта программа – приводит в соответствие набор введенных символов с их отображением в таблице CGRAM. Результатом преобразования является набор байтов (с нулевым значением в конце), начинающихся с директивы резервирования FLASH-памяти программ .db.


Рис.3 Утилита HD44780

Нумерация и Функциональное назначение выводов ЖКИ приведены в табл.1. Кроме напряжения питания контроллера VCC, модуль имеет вход регулировки контрастности изображения V0. Питание подсветки (если таковая имеется) подается на выводы A и K.


Рис.4 Последовательность передачи данных в HD44780
а — по 8-разрядной шине команд/данных
б — по 4-разрядной шине команд/данных

HD44780 взаимодействует с AVR через 8-битную двунаправленную шину команд/данных DB7:DB0. Временная диаграмма работы шины показана на рис.4а. В момент записи информации в ЖКИ ведущий микроконтроллер выставляет на линиях DB7…DB0 8-разрядный код, после чего формирует на выводе E стробирующий импульс (активный фронт – задний). По окончанию импульса должна быть выдержана пауза до начала новой транзакции. Признаком записи команды/ данных является состояние линии RS. При RS=0 происходит запись команды, при RS=1 – данных. Когда необходимо считать данные из индикатора, то выводы порта DB7:DB0 микроконтроллера настраиваются на ввод. Затем следует импульс подтверждения на линии E и байт данных переписывается во внутренний регистр для дальнейшей обработки. Направление передачи данных определяет уровень на линии R/W (R/W =1 – чтение из индикатора, R/W =0 – запись в индикатор). В реальных приложениях, как правило, нет необходимости в чтении данных. Поэтому вывод R/W всегда соединяют с общим проводом. Схема подключения AVR к A162-D приведена на рис.5a.


Рис.5 Схема подключения символьного ЖКИ к микроконтроллеру
а — при использовании 8-разрядной шины команд/данных
б — при использовании 4-разрядной шины команд/данных

Для управления ЖКИ может быть использован также 4-проводный интерфейс (см. схему подключения на рис.5б), что позволяет сэкономить 4 линии ввода-вывода, при незначительном усложнении программы.

Табл.1. Функциональное назначение выводов символьного ЖКИ на базе D44780:

Номер вывода

Название выводов

Функциональное назначение

Общий вывод

Напряжение питания

Напряжение управления контрастностью

Выбор записи команды/данные

Выбор направления передачи данных запись/чтение

Вход тактовых импульсов

Шина данных

Анод светодиодной подсветки

Катод светодиодной подсветки

Табл.2а. Команды записи в HD44780:

Состояние линий, при R/W=0

Команды

Максимальное
время
выполнения,
мкс

Полная отчистка дисплея и установка курсора в нулевую позицию.

Установка курсора в нулевую позицию. Установка дисплея в начальное положение.

I/D(Increment/Decrement) — направление сдвига курсора после записи (I/D=1 — сдвиг вправо, I/D=0 – сдвиг влево). S(Shift) – разрешение сдвига дисплея вместе с курсором (S=1 — сдвиг разрешен, S=0 — сдвиг запрещен).

С

D(Display) – включение дисплея (D=1 — дисплей включен, D=0 — дисплей отключен). C(Cursor)- видимость курсора (C=1 – видимый курсор, C=0 – погашенный курсор). B(Blink) — мигание курсора (B=1 – курсор мигает, B=0 – курсор не мигает).

S/C(Screen/Cursor) – перемещение дисплея/курсора (S/C=1 – перемещается дисплей, S/C=0 – перемещается курсор). R/L(Right/Left)- направление перемещения дисплея/курсора (R/L=1 – перемещение вправо, R/L=0 – перемещение влево).

DL(Data Length) – разрядность шины данных (DL=1 – 8 бит, DL=0 – 4 бита). N(Number)- число строк дисплея (N=1 – 2 строки, N=0 – 1 строка). F(Font) – размер шрифта (F=1 – шрифт 5×10 точек, F=0 – шрифт 5×7 точек).

Установка адреса CGRAM (Character Generator RAM). После команды должны следовать данные для записи/чтения в/из CGRAM.

Установка адреса DDRAM (Display Data RAM). После команды должны следовать данные для записи/чтения в/из DDRAM.

Запись данных в DDRAM (Display Data RAM) или CGRAM (Character Generator RAM).

Табл.2б. Команды чтения из HD44780:

NN

Состояние линий, при R/W=1

Команды

Максимальное время выполнения, мкс

Чтение BF (Busy Flag) – флаг завершения операции (BF=1 – операция завершена, BF=0 – операция не завершена) и текущего состояния внутреннего счетчика адреса AC (Address Counter).

Чтение данных из DDRAM (Display Data RAM) или CGRAM (Character Generator RAM).

В этом случае 4-разрядную шину команд/данных формируют линии DB7…DB4 (линии DB3…DB0 остаются незадействованными). Скорость записи снижается в 2 раза, но это, обычно, не вызывает ни каких проблем во время работы. Последовательность передачи данных показана на рис.4б. Команды/ данные передается за два такта. Первым следует старший полубайт, вторым – младший. Каждая тетрада, естественно, должна быть зафиксирована импульсом на линии E.


Рис.6 Адреса ячеек видеопамяти DDRAM

Контроллер HD44780 имеет буфер видеопамяти DDRAM (Display Data RAM), из которой символы переносятся на дисплей. Объем DDRAM зависит от числа строк и позиций на экране. Для индикатора с разрешением 16 символов x 2 строки он составляет 40 б на каждую строку (см.рис.6). Адреса ячеек видеопамяти первой строки 0x80…0xA8, второй 0xC0…0xE8. В текущий момент времени в окно дисплея попадают только 16 символов из DDRAM (положение окна можно изменять программно).

Управляющие команды записи сведены в табл.2а. Запись команды с кодом 0x01 приводит к полной отчистки DDRAM и установке окна дисплея и курсора в начальные позиции. Команда 0x02 заставляет проделать те же самые действия, но при этом оставляет содержимое видеопамяти неизменным. Биты команды под номером 3 задают направление смещение курсора (I/D=1 — сдвиг вправо, I/D=0 – сдвиг влево) и разрешение сдвига дисплея (S=1 — сдвиг разрешен, S=0 — сдвиг запрещен) при вводе очередного символа. Биты команды 4 отвечают за режим отображения курсора (B=1 – курсор мигает, B=0 – курсор не мигает; C=1 – видимый курсор, C=0 – погашенный курсор) и работу экрана (D=1 — дисплей включен, D=0 — дисплей отключен). Команду 5 удобно использовать для реализации бегущей строки. С ее помощью можно принудительно перемещать дисплей или курсор (S/C=1 – перемещается дисплей, S/C=0 – перемещается курсор), в произвольном направлении (R/L=1 – перемещение вправо, R/L=0 – перемещение влево). Содержимое DDRAM, в этом случае, остается неизменным. Команда 6 используется только во время начальной инициализации модуля. Она задает тип интерфейса (DL=1 – 8-проводной, DL=0 – 4-проводной), число строк дисплея (N=1 – 2 строки, N=0 – 1 строка) и размер шрифта (F=1 – шрифт 5×10 точек(не используется), F=0 – 5×7 точек).


Рис.7 Пользовательские символы в таблице CGRAM

Команды 7 и 8 предназначены для установки текущего адреса в CGRAM и DDRAM, соответственно, и могут быть использованы только совместно с командой записи данных 9 (либо с командой чтения 2, из табл.2б, о чем будет сказано ниже). После установки курсора в памяти DDRAM, команда 9 должна передавать адрес символа(0…0xFF) из таблицы CGRAM для его отображения в соответствующей позиции.
Комбинация команд 7 и 9 необходима при программирования пользовательских символов в CGRAM по адресам 0…0x0F. Для записи каждого символа потребуется 8 б памяти микроконтроллера. Полезную информацию будут нести в себе только 5 младших разрядов, соответствующих 5-ти столбцам матрицы (см. рис.7). Логической единице соответствует видимая точка на дисплее.
После установки адреса в ОЗУ знакогенератора, должен следовать 8-байтовый блок данных. Возможна запись нескольких символов подряд. Так, например, чтобы запрограммировать все 16 символов, нужно передать команду 0x40 (установить нулевой адрес в CGRAM), а за ней 16*8 = 128 б данных.

Команды чтения из индикатора приведены в табл.2б. С помощью первой команды может быть считано текущее содержимое счетчика адреса AC в DDRAM и состояние флага завершения операции BF (при BF=1 операция чтения/записи завершена). Команда 2 должна следовать после команды записи 7 или 8 из табл.2а и позволяет считать символы размещенные в CGRAM либо DDRAM.

Как уже говорилось выше, команды чтения не имеют никакой практической ценности. Интерес может представлять только флаг BF. Однако намного удобней программно формировать задержки времени, гарантирующие завершение операций чтения/записи, чем постоянно опрашивать состояние флага окончания операции. Необходимость использования линии R/W при этом также отпадает.

.def data = R16 ;регистр для передачи команд и данных .def row = R17 ;регистр с номером строки .def col = R18 ;регистр с номером позиции на индикатор .def temp = R19 ;регистр для промежуточных операций .equ DELAY = 500 ;задержка времени на частоте 1 МГЦ .equ RS = PC0 .equ EN = PC1 ldi temp,high(RAMEND) ;инициализация стека out SPH,temp ldi temp,low(RAMEND) out SPL,temp . clr temp ;обнуляем регистры PORTD, PORTC out PORTD,temp out PORTC,temp ldi temp,0b11110000;для шины 4 бита настраиваем PD7…PD4 на вывод ;ldi temp,0b11111111;для шины 8 бит настраиваем PD7…PD0 на вывод out DDRD,temp ldi temp,0b00000011 ;настраиваем линии PC1,PC0 на вывод out DDRC,temp rcall hd44780_init;инициализируем модуль перед началом работы . ldi row,1 ;выводим надпись «Hello World !» ldi col,3 ;в строку 1 начиная с позиции 3 ldi ZH,high(2*first_string) ldi ZL,low(2*first_string) rcall show_string ldi row,2 ;выводим надпись «Я люблю AVR” ldi col,4 ;в строку 2 начиная с позиции 4 ldi ZH,high(2*second_string) ldi ZL,low(2*second_string) rcall show_string . first_string: ; строка 13 символов «Hello World !» .db «Hello World !»,0 second_string: ; строка 11 символов «Я люблю AVR” .db 0x20,0xB1,0x20,0xBB,0xC6,0xB2,0xBB,0xC6 .db 0x20,0x41,0x56,0x52,0x20,0x20,0x20,0x20,0 ; Набор подпрограмм для работы с HD44780(16 символов x 2 строки) ; R16 – регистр для передачи команд и данных ; R17,R18 – счетчики циклов, регистры для задания номеров строки ; и позиции в видеопамяти индикатора ; XH:XL – используется как 2-байтовый регистр для задержки ; времени ; ZH:ZL – указатель на строку символов, хранящуюся во ; FLASH-памяти программ ; DELAY – постоянная для задержка времени (500…10000) ; Вспомогательная подпрограмма записи байта при использовании ; 4-проводного интерфейса ; В R16 в подпрограмму передается байт для записи write_byte: push R16 ;сохраняем регистр с байтом данных andi R16,0xF0 ;выделяем старший полубайт в регистре in R17,PORTD ;копируем содержимое порта во временный andi R17,0x0F ;регистр для модификации or R16,R17;оставляем неизменным состояние младших 4-х линий sbi PORTC,EN ;устанавливаем на линии EN лог.1 out PORTD,R16 ;выводим новое значение в порт ldi XH,high(DELAY) ;формируем задержку времени ldi XL,low(DELAY) rcall pause cbi PORTC,EN ;устанавливаем на линии EN лог.0 pop R16 ;восстанавливаем регистр с байтом данных swap R16;обмениваем полубайты для выделения младших 4-х бит andi R16,0xF0 ;выделяем старший полубайт в регистре in R17,PORTD ;копируем содержимое порта во временный andi R17,0x0F ;регистр для модификации or R16,R17;оставляем неизменным состояние младших 4-х линий sbi PORTC,EN ;устанавливаем на линии EN лог.1 out PORTD,R16 ;выводим новое значение в порт ldi XH,high(DELAY) ;формируем задержку времени ldi XL,low(DELAY) rcall pause cbi PORTC,EN ;устанавливаем на линии EN лог.0 ldi XH,high(DELAY) ;формируем задержку времени ldi XL,low(DELAY) rcall pause ret ; Вспомогательная подпрограмма записи байта при использовании ; 8-проводного интерфейса ; В R16 в подпрограмму передается байт для записи ;write_byte: ; sbi PORTC,EN ;устанавливаем на линии EN лог.1 ; out PORTD,R16 ;выводим байт информации в порт ; ldi XH,high(DELAY) ;формируем задержку времени ; ldi XL,low(DELAY) ; rcall pause ; cbi PORTC,EN ;устанавливаем на линии EN лог.0 ; ldi XH,high(DELAY) ;формируем задержку времени ; ldi XL,low(DELAY) ; rcall pause ; ret ; Вспомогательная подпрограмма задержки времени pause: sbiw XH:XL,1 brne pause ret ; Подпрограмма записи команды ; В R16 передается код команды перед вызовом подпрограммы. write_com: cbi PORTC,RS ;устанавливаем на линии RS лог.0 rjmp write_byte ;записываем команду ret ; Подпрограмма записи данных ; В R16 передается байт данных перед вызовом подпрограммы. write_dat: sbi PORTC,RS ;устанавливаем на линии RS лог.1 rjmp write_byte ;записываем данные ret ; Подпрограмма записи символа ; В R16 передается код символа(0…255), в R17 номер строки ; (1,2), в R18 номер символа в строке (1…40) при входе в ; подпрограмму. show_char: push R16 ;сохраняем регистр с символом subi R18,-0x7F ;если запись идет в строку 1, для получения sbrc R17,1 ;адреса в DDRAM к номеру символа в строке subi R18,-0x40 ;добавляется 0x7F, если в строку 2, mov R16,R18 ;то добавляется 0xBF rcall write_com ;задаем адрес символа в видеопамяти pop R16 ;восстанавливаем регистр с символом rcall write_dat ;записываем символ по текущему адресу ret ; Подпрограмма записи строки символов из FLASH-памяти ; В указателе Z передается адрес строки из FLASH-памяти (стока ; должна заканчиваться нулем), в R17 номер строки (1,2), в R18 ; начальный номер позиции, с которой должна начинаться строка ; (1…40) при входе в подпрограмму. show_string: lpm R16,Z+ ;загружаем код символ из строки tst R16 ;если код 0 (NUL), то строка закончена brne ss1 ret ;и выход из подпрограммы ss1: push R17 ;сохраняем регистр с номер строки push R18 ;сохраняем регистр с начальный номером позиции rcall show_char ;записываем символ pop R18;восстанавливаем регистр с начальным номером позиции pop R17 ;восстанавливаем регистр с номер строки inc R18 ;увеличиваем на 1 номер позиции rjmp show_string ; Подпрограмма инициализации hd44780_init: ldi XH,high(5*DELAY) ;формируем задержку времени ldi XL,low(5*DELAY) rcall pause ldi R16,0x33 ;записываем необходимую для инициализации rcall write_com ;холостую команду ldi XH,high(5*DELAY) ;формируем задержку времени ldi XL,low(5*DELAY) rcall pause ldi R16,0x32 ;записываем необходимую для инициализации rcall write_com ;холостую команду ldi R16,0x28 ;выбираем разрядность шины 4 бита и 2 строки ; ldi R16,0x38;выбираем разрядность шины 8 бит и 2 строки rcall write_com ;на дисплее ldi XH,high(5*DELAY) ;формируем задержку времени ldi XL,low(5*DELAY) rcall pause ldi R16,0x08 ;записываем команду выключения дисплея rcall write_com ldi R16,0x01 ;записываем команду начального сброса rcall write_com ldi XH,high(5*DELAY) ;формируем задержку времени ldi XL,low(5*DELAY) rcall pause ldi R16,0x06 ;записываем команду, задающую направление rcall write_com ;сдвига курсора вправо ldi R16,0x0C ;записываем команду включения дисплея rcall write_com ret

Набор подпрограмм для работы с символьным ЖКИ приведен выше. Подпрограммы write_com, write_dat производят запись команд и данных соответственно. Подпрограмма show_char выводит символ на экран дисплея; show_string переписывает строку, хранящуюся во FLASH-памяти программ, в DDRAM индикатора. Обе подпрограммы в качестве параметров принимаю начальные координаты записи — строку и столбец. В show_string, кроме этого необходимо передать еще и указатель на строку в регистре ZH:ZL.

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

Перейти к следующей части: Аналоговый вывод

Теги:

  • HD44780
  • LCD
  • AVR
  • Микроконтроллер

4a4ik

Если нужно срочно вывести символы и не интересно как он работает, то я расписал как работать с готовой для него библиотекой
Datasheet


Заказал с ebay дисплей 2 линии на 16 символов каждая.

Вообщем HD44780 это контроллер управления одноцветным LCD дисплеем, такие дисплеи были очень популярны в 90-е
связку контроллер, дисплей и внешние выводы я буду называть просто дисплей.
У таких дисплеев 16 выводов
1) VSS — GND, земля или общий провод.
2) VDD — питание контроллера 2.7 — 5.5 В
3) V0 — настройка контрастности, нужно подбирать вручную, у меня всё ясно видно при 2,16 В если использовать 1 линию, 1,19 В если 2. В моём случае яркость отображения символов, меняется в зависимости от количества используемых линий.
4) RS — выбираем что записывать (читать) 0 — команды, 1 — символы.
5) RW — 0 — записывать, 1 — считывать, обычно этот вывод заземляют если предполагается что нужно будет только записывать символы.
6) E — подав сюда импульс происходит передача команды дисплею.
7 — 10) D0 — D3 — low биты передачи данных, не используются при 4 битной работе.
11 — 14) D4 — D7 — high биты передачи данных
15) A — Анод подсветки дисплея (+), советуют ставить 100 ом резистор, но у меня и без него всё в порядке.
16) K — катод подсветки.
Для того чтобы общаться с дисплеем необходимо ему говорить что происходит передача данных, для этого подается импульс на вывод E не меньше 230 нс.
Для того чтобы узнать что команда выполнена можно либо подождать некоторое время, (для каждой команды указано сколько необходимо ждать). Либо проверять «busy flag».
Когда происходит выполнение команды дисплей выставляет «busy flag», когда команда выполнилась он его сбрасывает. Его можно считать подав на RS 0, RW 1, и тогда он будет на выводе D7, но всё равно следует подождать некоторое время даже если он равен 0, не меньше 6 мкс.
Дисплей будет игнорировать все попытки с ним связаться пока не закончиться выполнение текущей команды.
Чтобы вывести определённый символ нужно передать его код дисплею при RS 1, RW 0,

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

1) Clear display — очистить дисплей и вернуть курсор в левый верхний угол (начало).
2) Return home — вернуть курсор в начало.
3) Entry mode set — выбор как будут выводиться символы на экране при их записи. I/D = 1 курсор движется вправо, 0 влево. S = 1 — вместо курсора движется дисплей, 0 — нет.

I/D =0
S = 0
I/D =0
S = 1
I/D =1
S = 0
I/D =1
S = 1

Анимация взята
4) Display on/off control — D дисплей вкл (1) / выкл (0), C курсор вкл (1) / выкл (0), B курсор мигает (1) / нет (0). 5) Cursor or display shift — сдвигает S/C 0 = курсор, S/C 1 = дисплей, влево R/L = 0 или вправо R/L = 1/ 6) Function set — настраиваем режим работы дисплея, DL = 1 8 бит ( 0 = 4 бита ), N = 1 2 линии ( 0 = 1 линия ), F = 1 шрифт 5×10, = 0 5×8 7) Set CGRAM adress — используется для записи своих символов 8) Set DDRAM adress — используется для перемещения курсора. 9) Read busy flag and adress — чтение «busy» флага/
Рассмотрим работу с дисплеем на примере, используется ATmega8, дисплей 2×16 и передавать данные буду по 8 битам.
Сразу обозначаю ножки которые буду использовать, работаю с ATmega8 у которой нет 8 выводов на 1 порт, поэтому использую несколько портов.
//PORT B #define PIN_RS 0 #define PIN_RW 1 #define PIN_E 2 //PORT C #define PIN_DB0 0 #define PIN_DB1 1 #define PIN_DB2 2 #define PIN_DB3 3 //PORT D #define PIN_DB4 0 #define PIN_DB5 1 #define PIN_DB6 2 #define PIN_DB7 3
Здесь я проверяю «busy» флаг, чтобы узнать что команда выполнена, для передачи нужно подать импульс который измеряется в нс, так что 1 мкс должно хватить.
void wait_busy() { PORTB |= ( 1 << PIN_RW ); do { PORTB &= ~( 1 << PIN_E ); _delay_us(1); PORTB |= ( 1 << PIN_E ); } while ( PIND & ( 1 << PIN_DB7 ) ); PORTB = 0; _delay_us(7); }
передача данных.
void send() { PORTB |= ( 1 << PIN_E ); //wait _delay_us(1); //don’t send PORTB &= ~( 1 << PIN_E ); //clear command PORTD = 0; PORTC = 0; }
Весь код
#define F_CPU 8000000UL //PORT B #define PIN_RS 0 #define PIN_RW 1 #define PIN_E 2 //PORT C #define PIN_DB0 0 #define PIN_DB1 1 #define PIN_DB2 2 #define PIN_DB3 3 //PORT D #define PIN_DB4 0 #define PIN_DB5 1 #define PIN_DB6 2 #define PIN_DB7 3 #include <avr/io.h> #include <util/delay.h> void wait_busy(); void send(); int main(void) { DDRB = 0xFF; DDRC = 0xFF; DDRD = 0xFF; //—wait for initialization wait_busy(); //—8 bit 2 line mode PORTD |= ( 1 << PIN_DB4 )|( 1 << PIN_DB5 ); PORTC |= ( 1 << PIN_DB3 ); //send data send(); wait_busy(); //—-turn on display, enable cursor, blniking cursor PORTC |= (1 << PIN_DB0 )|(1 << PIN_DB1 )|(1 << PIN_DB2 )|(1 << PIN_DB3 ); //send data send(); wait_busy(); //—return cursor to beginning PORTC |= ( 1 << PIN_DB1 ); //send data send(); wait_busy(); //—write symbol PORTB |= ( 1 << PIN_RS ); PORTD |= ( 1 << PIN_DB4 )|( 1 << PIN_DB5 ); //send data send(); wait_busy(); //—write symbol PORTB |= ( 1 << PIN_RS ); PORTD |= ( 1 << PIN_DB6 )|( 1 << PIN_DB7 ); PORTC |= ( 1 << PIN_DB3 ); //send data send(); wait_busy(); while(1) { } } void wait_busy() { PORTB |= ( 1 << PIN_RW ); do { PORTB &= ~( 1 << PIN_E ); _delay_us(1); PORTB |= ( 1 << PIN_E ); } while ( PIND & ( 1 << PIN_DB7 ) ); PORTB = 0; _delay_us(7); } void send() { PORTB |= ( 1 << PIN_E ); //wait _delay_us(1); //don’t send PORTB &= ~( 1 << PIN_E ); //clear command PORTD = 0; PORTC = 0; }

GitHub
Результат

У дисплея есть 2 набора символов, как видно у меня тот в котором есть японский.

Но всё таки нужно написать функции которые бы облегчили бы работу с дисплеем.
Можно заметить что символы в памяти дисплея совпадают по порядку с таблицей ASCII.
void write_symbol( char a ) { int small_bit = a % 16; int high_bit = a / 16; PORTB |= (1 << PIN_RS ); PORTC = small_bit; PORTD = high_bit; //send data send(); //wait _delay_us(50); }
нужно написать функцию инициализации или выбора режима работы
void initialize( int bit_num, int line_num ) { DDRB = 0xFF; DDRC = 0xFF; DDRD = 0xFF; //—wait for initialization //check busy flag PORTB |= ( 1 << PIN_RW ); do { PORTB &= ~( 1 << PIN_E ); _delay_us(1); PORTB |= ( 1 << PIN_E ); } while ( PIND & ( 1 << PIN_DB7 ) ); PORTB = 0; //—8 bit 2 line mode PORTD |= ( bit_num << PIN_DB4 )|( 1 << PIN_DB5 ); PORTC |= ( line_num << PIN_DB3 ); //send data send(); //wait _delay_us(50); }
и включения дисплея с курсором.
void enable_display( int display_on, int cursor_on, int blinking_on ) { //—-turn on display, enable cursor, blniking cursor PORTC |= (blinking_on << PIN_DB0 )|(cursor_on << PIN_DB1 )|(display_on << PIN_DB2 )|(1 << PIN_DB3 ); //send data send(); //wait _delay_us(50); }
Выбираем кто сдвигается (дисплей или курсор) и куда (влево вправо)
void set_mode( int display_shift, int cursor_shift ) { //—-turn on display, enable cursor, blniking cursor LOW_PORT |= (display_shift << PIN_DB0 )|(cursor_shift << PIN_DB1 )|(1 << PIN_DB2 ); //send data send(); //wait _delay_us(50); }
Очищаем дисплей
void clear_display() { //—return cursor to beginning LOW_PORT |= (1 << PIN_DB0 ); //send data send(); //wait _delay_us(50); }
Нужно научиться передвигать курсор по дисплею.
Т.к контроллер дисплея универсальный и его ставят на разные виды дисплеев, то он может работать с различными размерами экранов. В памяти хранится текущее положение курсора и его можно менять с помощью 8 команды, нужно указать адрес куда мы хотим переместить курсор.

Здесь коды указаны в 16 коде, 2 строка начинается с 40 и не важно сколько в нашем дисплее символов на строку, если мы запишем символ в 0х39, хотя у нас дисплей меньше чем на 40 символов, то он просто не отобразиться, хотя дисплей будет считать что всё в порядке.
Возвращаем курсор на место (1 команда).
void return_cursor() { //—return cursor to beginning LOW_PORT |= (1 << PIN_DB1 ); //send data send(); //wait _delay_us(1550); }
Передвигаем курсор.
void move_cursor( int x_pos, int line ) { int small_bit = x_pos % 16; int high_bit = x_pos / 16; //—moves cursor to the beginning of 2 line HIGH_PORT = high_bit; HIGH_PORT |= (1 << PIN_DB7 )|((line — 1) << PIN_DB6 ); LOW_PORT = small_bit; //send data send(); //wait _delay_us(50); }
Весь код
#define F_CPU 8000000UL #define PROGRAM_PORT PORTB //PORT B #define PIN_RS 0 #define PIN_RW 1 #define PIN_E 2 #define LOW_PORT PORTC //PORT C #define PIN_DB0 0 #define PIN_DB1 1 #define PIN_DB2 2 #define PIN_DB3 3 #define HIGH_PORT PORTD //PORT D #define PIN_DB4 0 #define PIN_DB5 1 #define PIN_DB6 2 #define PIN_DB7 3 #include <avr io.h> #include <util delay.h> void send(); void write_symbol( char a ); void initialize( int bit_num, int line_num ); void set_mode( int display_shift, int cursor_shift ); void enable_display( int display_on, int cursor_on, int blinking_on ); void return_cursor(); void clear_display(); void move_cursor( int x_pos, int line ); int main(void) { initialize( 1, 1 ); set_mode( 0, 1 ); enable_display( 1, 0, 0 ); clear_display(); return_cursor(); write_symbol(‘ ‘); write_symbol(‘ ‘); write_symbol(‘ ‘); write_symbol(‘ ‘); write_symbol(‘ ‘); write_symbol(‘4’); write_symbol(‘a’); write_symbol(‘4’); write_symbol(‘i’); write_symbol(‘k’); while(1); } void send() { PROGRAM_PORT |= ( 1 << PIN_E ); //wait _delay_us(1); //don’t send PROGRAM_PORT &= ~( 1 << PIN_E ); //clear command PROGRAM_PORT = 0; HIGH_PORT = 0; LOW_PORT = 0; } void write_symbol( char a ) { int small_bit = a % 16; int high_bit = a / 16; PROGRAM_PORT |= (1 << PIN_RS ); LOW_PORT = small_bit; HIGH_PORT = high_bit; //send data send(); //wait _delay_us(50); } void initialize( int bit_num, int line_num ) { DDRB = 0xFF; DDRC = 0xFF; DDRD = 0xFF; //—wait for initialization //check busy flag PROGRAM_PORT |= ( 1 << PIN_RW ); do { PROGRAM_PORT &= ~( 1 << PIN_E ); _delay_us(1); PROGRAM_PORT |= ( 1 << PIN_E ); } while ( PIND & ( 1 << PIN_DB7 ) ); PROGRAM_PORT = 0; //—8 bit 2 line mode HIGH_PORT |= ( bit_num << PIN_DB4 )|( 1 << PIN_DB5 ); LOW_PORT |= ( line_num << PIN_DB3 ); //send data send(); //wait _delay_us(50); } void enable_display( int display_on, int cursor_on, int blinking_on ) { //—-turn on display, enable cursor, blniking cursor LOW_PORT |= (blinking_on << PIN_DB0 )|(cursor_on << PIN_DB1 )|(display_on << PIN_DB2 )|(1 << PIN_DB3 ); //send data send(); //wait _delay_us(50); } void set_mode( int display_shift, int cursor_shift ) { //—-turn on display, enable cursor, blniking cursor LOW_PORT |= (display_shift << PIN_DB0 )|(cursor_shift << PIN_DB1 )|(1 << PIN_DB2 ); //send data send(); //wait _delay_us(50); } void return_cursor() { //—return cursor to beginning LOW_PORT |= (1 << PIN_DB1 ); //send data send(); //wait _delay_us(1550); } void clear_display() { //—return cursor to beginning LOW_PORT |= (1 << PIN_DB0 ); //send data send(); //wait _delay_us(50); } void move_cursor( int x_pos, int line ) { int small_bit = x_pos % 16; int high_bit = x_pos / 16; //—moves cursor to the beginning of 2 line HIGH_PORT = high_bit; HIGH_PORT |= (1 << PIN_DB7 )|((line — 1) << PIN_DB6 ); LOW_PORT = small_bit; //send data send(); //wait _delay_us(50); }

GitHub
Результат

С дисплеем можно работать в 4 битном режиме вместо 8.
Теперь для передачи данных используются выводы DB4 — DB7, но данные нужно передавать 2 раза, начиная с старших битов, то есть 01001000 передается как -> 0100 передача, 1000 передача.
По умолчанию (при включении) дисплей работает в 8-ми битном режиме, поэтому если мы хотим работать с 4 битами то при выборе режима команда отсылается 1 раз, после чего дисплею нужно посылать по 2 команды.
Код для работы с помощью 4 битов
К тому же можно создавать свои символы.
Нельзя записать символ в EEPROM (энергонезависимая память), а только в CGRAM (Character Generator RAM), так что если отключить питание символ исчезнет. Там отведена память под 16 символов которые можно менять (первые 128 байт) На самом деле нам доступно только 8 символов, остальные 8 это копия предыдущих, они обозначены в таблице символов номерами в скобках. (1 и 9 символ один и тот же, если изменить 1 то во втором сразу появится его копия), если попытаться записать в другое место то ничего не запишется и появится мусор.

То же самое можно увидеть если попытаться вывести первые 16 символов предварительно ничего туда не записав.

Функция для записи нового символа,
1) выставляем режим для работы с CGRAM и ставим адрес с какого начинаем записывать,
2) когда записываем строку адрес сам увеличивается на 1 и начинаем записывать следующую, так записываем сколько нам необходимо.
3) возвращаемся в режим работы с DDRAM и возвращаем курсор на начало.
void add_character( int where, unsigned char line1, unsigned char line2, unsigned char line3, unsigned char line4, unsigned char line5, unsigned char line6, unsigned char line7, unsigned char line8 ) { // starts sending char lines int small_bit = where % 16; int high_bit = where / 16; HIGH_PORT = high_bit; HIGH_PORT |= (1 << PIN_DB6 ); LOW_PORT = small_bit; send(); // 1 line small_bit = line1 % 16; high_bit = line1 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 2 line small_bit = line2 % 16; high_bit = line2 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 3 line small_bit = line3 % 16; high_bit = line3 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 4 line small_bit = line4 % 16; high_bit = line4 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 5 line small_bit = line5 % 16; high_bit = line5 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 6 line small_bit = line6 % 16; high_bit = line6 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 7 line small_bit = line7 % 16; high_bit = line7 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // 8 line small_bit = line8 % 16; high_bit = line8 / 16; HIGH_PORT = high_bit; LOW_PORT = small_bit; PROGRAM_PORT |= (1 << PIN_RS ); send(); // Back to normal regime HIGH_PORT |= (1 << PIN_DB7 ); send(); }
Теперь записываем данные, where = 0 — это 1 строка 1 символа в CGRAM, 8 — это 1 строка 2 символа и т.д. Записываются только биты с 0 по 4 (т.к ширина символа 5 пикселей).