Для активизации четырехбитового режима надо программно сформировать сигналы управления согласно временным диаграммам на рис.1. По структуре они совпадают с диаграммой 8-ми разрядной шины за исключением удвоенного числа импульсов "Е". Линии связи проходят через старшие разряды шины данных DB4-DB7, младшие DB0-DB3 остаются не задействованными.

Рис.1

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

Принцип работы 4-х разрядной шины рассмотрим на примере тестовой программы для нашего LCD. На дисплей будут с секундными паузами выводиться цифры десятичного адреса знакоместа 0-255 и графические образы содержащихся в них символов.

Рис. 2

Как известно, каждый LCD имеет встроенный знакогенератор, представляющий собой область ПЗУ объемом более 8 Кб, которая прошивается на заводе-изготовителе. Традиционно первая половина ПЗУ с адресами 00-7Fh содержит начертания цифр, знаков препинания, а также заглавных и строчных букв латинского алфавита. Все как в IBM PC. Вторая половина "отдана на откуп" национальным алфавитам. В связи с этим HD44780 имеет модификации исполнения с тремя основными вариантами зашивки знакогенератора:

латиница и европейские языки (European standard font или Euro)
латиница и японские иероглифы (Japanese standard font или Japan)
латиница и кириллица (Custom font или Russian, рис.2)

Не все из ячеек знакогенератора заполнены. При обращении к "пустым" ячейкам на экране будет выведена произвольная информация, чаще всего состоящая из засвеченных точек. Первые 8 символов с адресами 0х00-0х07 отмечены "звездочкой". При желании они могут быть самостоятельно запрограммированы пользователем.

Какой знакогенератор имеется в конкретном LCD, должно быть указано в его условном обозначении или в технических параметрах, хотя на практике приходится верить честному слову продавца. Другой подход воочию увидеть на экране LCD все возможные начертания символов. Напишем, откомпилируем программу и запрограммируем контроллер, после чего в верхней строке экрана LCD будут с секундными паузами будут выводиться цифры десятичного адреса знакоместа 0-255 и графические образы содержащихся в них символов. Если графика и очередность появления символов соответствует рис.2, значит, LCD в порядке.

Далее собираем схему согласно рис.3, ее отличие только в том что шина данных подключена по 4-х проводной линии, т.е. DB4-DB7 подключены, а DB0-DB3 остаются не задействованными. Вывод R/W дисплея подключен на минус, т.к. дисплей у нас является приемником данных.

Подключение по 4-х проводной шине

Код программы проверки знакогенератора LCD приведен ниже.

// Программа проверки знакогенератора LCD

#include <avr/io.h>
#include <util/delay.h>

#define RS PC0 
#define EN PC2

// Функция записи команды в ЖКИ
void lcd_com(unsigned char p)
{
PORTC &= ~(1 << RS); // RS = 0 (запись команд)
PORTC |= (1 << EN);  // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
_delay_us(100);
PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
_delay_us(100);
PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция записи данных в ЖКИ
void lcd_dat(unsigned char p)
{
PORTC |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл
_delay_us(100);
PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл
_delay_us(100);
PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}

// Функция инициализации ЖКИ
void lcd_init(void)
{
DDRC |= (1 << PC2)|(1 << PC0); // PC1, PC0 - выходы
PORTC = 0x00;
DDRD = 0xFF; // порт D - выход
PORTD = 0x00;

_delay_ms(50); // Ожидание готовности ЖК-модуля

// Конфигурирование четырехразрядного режима
PORTD |= (1 << PD5);
PORTD &= ~(1 << PD4);

// Активизация четырехразрядного режима
PORTC |= (1 << EN);
PORTC &= ~(1 << EN);
_delay_ms(5); 

lcd_com(0x28); // Шина 4 бит, LCD - 2 строки
lcd_com(0x08); // Полное выключение дисплея
lcd_com(0x01); // Очистка дисплея
_delay_us(100);
lcd_com(0x06); // Сдвиг курсора вправо
_delay_ms(10);
lcd_com(0x0C); // Включение дисплея, курсор не видим
}

// Основная программа
int main (void)
{
unsigned char znak = 0; // определяем переменную

lcd_init(); // Инициализация дисплея

while (1)
{
lcd_com(0x80); // Вывод в верхнюю левую позицию 1 строки
lcd_dat(znak/100 + '0'); // Выделяем сотни
lcd_dat((znak/10)%10 + '0'); // Выделяем десятки 
lcd_dat(znak%10 + '0'); // Выделяем единицы
lcd_dat('='); // Выводим знак равенства
lcd_dat(znak); // Выводим содержимое знакогенератора
_delay_ms(100); // Тут можно поменять задержку вывода символов
znak++; // Следующий символ знакогенератора
}
}