Знакомый по HD44780 текстовый индикатор можно подключить к микроконтроллеру всего тремя выводами, если поставить между ними обычный сдвиговый регистр. Готовый драйвер для компилятора CCS PIC C берёт всю работу с последовательной посылкой на себя.
Зачем нужен трёхпроводный интерфейс
Даже в экономичном четырёхбитном режиме символьный LCD требует от микроконтроллера как минимум шесть линий: четыре линии данных, линия RS и линия разрешения E. Для многих корпусов это неприемлемо — например, у семейства PIC12F просто нет такого количества свободных выводов. Значит, нужен способ уменьшить число задействованных ног.
Решение простое: данные передаются к индикатору последовательно, через сдвиговый регистр с последовательным входом и параллельным выходом. Микроконтроллер работает с регистром всего по двум линиям — линии данных и линии тактирования, а регистр уже раскладывает принятые биты по параллельным выводам индикатора. Третья линия — разрешение E — идёт от микроконтроллера к LCD напрямую. Итого три вывода вместо шести.
Подойдёт любой сдвиговый регистр типа «последовательный вход — параллельный выход»: 74HC595, 74HC164, CD4094 (он же HEF4094) и аналогичные.
Схемы включения
Аппаратная часть предельно проста — кроме индикатора и микроконтроллера в ней есть только сдвиговый регистр. Ниже приведены три варианта соединения, отличающиеся типом применённой микросхемы.
Вариант на 74HC595:
Нажмите для увеличения (1024x609)
Вариант на 74HC164:
Нажмите для увеличения (1024x609)
Вариант на HEF4094 (CD4094, 74HC4094):
Нажмите для увеличения (1024x613)
Исходный код драйвера
Ниже полный текст драйвера для компилятора CCS PIC C, рассчитанного на контроллеры индикаторов, совместимые с HD44780. Драйвер проверен на индикаторах 1602 (16x2) и 2004 (20x4) при тактовых частотах кварца 8 МГц и 48 МГц.
// 3-Wire LCD driver for CCS PIC C compiler (for HD44780 compliant controllers)
#define LCD_FIRST_ROW 0x80
#define LCD_SECOND_ROW 0xC0
#define LCD_THIRD_ROW 0x94
#define LCD_FOURTH_ROW 0xD4
#define LCD_CLEAR 0x01
#define LCD_RETURN_HOME 0x02
#define LCD_CURSOR_OFF 0x0C
#define LCD_UNDERLINE_ON 0x0E
#define LCD_BLINK_CURSOR_ON 0x0F
#define LCD_MOVE_CURSOR_LEFT 0x10
#define LCD_MOVE_CURSOR_RIGHT 0x14
#define LCD_TURN_ON 0x0C
#define LCD_TURN_OFF 0x08
#define LCD_SHIFT_LEFT 0x18
#define LCD_SHIFT_RIGHT 0x1E
short RS;
void lcd_write_nibble(unsigned int8 n)
{
unsigned int8 i;
output_low(LCD_CLOCK_PIN);
output_low(LCD_EN_PIN);
for(i = 8; i > 0; i = i >> 1)
{
if(n & i)
output_high(LCD_DATA_PIN);
else
output_low(LCD_DATA_PIN);
Delay_us(10);
output_high(LCD_clock_PIN);
Delay_us(10);
output_low(LCD_clock_PIN);
}
if(RS)
output_high(LCD_DATA_PIN);
else
output_low(LCD_DATA_PIN);
for(i = 0; i < 2; i++)
{
Delay_us(10);
output_high(LCD_clock_PIN);
Delay_us(10);
output_low(LCD_clock_PIN);
}
output_high(LCD_EN_PIN);
delay_us(2);
output_low(LCD_EN_PIN);
}
void LCD_Cmd(unsigned int8 Command)
{
RS = 0;
lcd_write_nibble(Command >> 4);
lcd_write_nibble(Command & 0x0F);
if((Command == 0x0C) || (Command == 0x01) || (Command == 0x0E) ||
(Command == 0x0F) || (Command == 0x10) || (Command == 0x1E) ||
(Command == 0x18) || (Command == 0x08) || (Command == 0x14) ||
(Command == 0x02))
Delay_ms(50);
}
void LCD_GOTO(unsigned int8 col, unsigned int8 row)
{
switch(row)
{
case 1:
LCD_Cmd(0x80 + col - 1);
break;
case 2:
LCD_Cmd(0xC0 + col - 1);
break;
case 3:
LCD_Cmd(0x94 + col - 1);
break;
case 4:
LCD_Cmd(0xD4 + col - 1);
break;
}
}
void LCD_Out(unsigned int8 LCD_Char)
{
RS = 1;
lcd_write_nibble(LCD_Char >> 4);
delay_us(10);
lcd_write_nibble(LCD_Char & 0x0F);
break;
}
void LCD_Initialize()
{
RS = 0;
output_low(LCD_DATA_PIN);
output_low(LCD_CLOCK_PIN);
output_low(LCD_EN_PIN);
output_drive(LCD_DATA_PIN);
output_drive(LCD_CLOCK_PIN);
output_drive(LCD_EN_PIN);
delay_ms(40);
lcd_Cmd(3);
delay_ms(5);
lcd_Cmd(3);
delay_ms(5);
lcd_Cmd(3);
delay_ms(5);
lcd_Cmd(2);
delay_ms(5);
lcd_Cmd(0x28);
delay_ms(50);
lcd_Cmd(0x0C);
delay_ms(50);
lcd_Cmd(0x06);
delay_ms(50);
lcd_Cmd(0x0C);
delay_ms(50);
}
Драйвер распространяется и отдельным файлом 3WireLCD.c, который достаточно положить в папку проекта или в каталог драйверов CCS PIC C.
Как драйвер формирует посылку
Вся низкоуровневая работа сосредоточена в функции lcd_write_nibble(). Перед началом посылки линии тактирования и разрешения сбрасываются в ноль. Затем цикл for(i = 8; i > 0; i = i >> 1) перебирает биты полубайта от старшего к младшему: маска начинается с восьмёрки и на каждом шаге сдвигается вправо. Проверенный бит выставляется на линии данных, после чего формируется тактовый импульс — пауза 10 мкс, подъём линии тактирования, ещё 10 мкс и спад. Так в регистр уходят четыре бита данных.
Дальше на линию данных выставляется состояние переменной RS (признак «команда/данные») и выдаются ещё два тактовых импульса — этим бит RS дотесняется до того выхода регистра, который соединён с одноимённым входом индикатора. Только после этого микроконтроллер собственной ногой формирует стробирующий импульс на линии E: подъём, задержка 2 мкс, спад. По его спаду индикатор защёлкивает выставленный регистром полубайт.
Байт передаётся двумя такими посылками. Функция LCD_Cmd() сбрасывает RS в ноль и отправляет сначала старший полубайт (Command >> 4), затем младший (Command & 0x0F). Часть команд выполняется контроллером индикатора заметно дольше остальных, поэтому для списка «медленных» кодов (0x0C, 0x01, 0x0E, 0x0F, 0x10, 0x1E, 0x18, 0x08, 0x14, 0x02) добавлена пауза 50 мс. Функция LCD_Out() устроена так же, но с RS = 1, а между полубайтами вставлена задержка 10 мкс.
Позиционирование в LCD_GOTO() сделано через базовые адреса строк: к 0x80, 0xC0, 0x94 или 0xD4 прибавляется номер колонки минус единица.
Инициализация LCD_Initialize() начинается с перевода всех трёх линий в ноль и настройки их на вывод (output_drive). После паузы 40 мс идёт классическая для HD44780 стартовая последовательность: три посылки кода 3 с интервалом 5 мс, затем код 2 — перевод в четырёхбитный режим. Дальше 0x28 задаёт формат, 0x0C включает дисплей без курсора, 0x06 — автоинкремент позиции, и завершающий 0x0C; каждая из этих команд отделена паузой 50 мс.
Подключение драйвера к проекту
Файл драйвера подключается обычной директивой:
#include <3WireLCD.c>
До подключения необходимо объявить, на каких выводах микроконтроллера сидят линия данных, линия тактирования и линия разрешения. Например, для данных на RB0, тактирования на RB1 и разрешения на RB2:
#define LCD_DATA_PIN PIN_B0
#define LCD_CLOCK_PIN PIN_B1
#define LCD_EN_PIN PIN_B2
Набор доступных функций невелик:
- LCD_Initialize(); — должна вызываться до любой другой функции драйвера.
- LCD_GOTO(unsigned int8 col, unsigned int8 row); — установка позиции вывода. Левый верхний угол — это 1,1, а первая позиция второй строки — 1,2.
- LCD_Out(unsigned int8 LCD_Char); — вывод символа на индикатор.
- LCD_Cmd(unsigned int8 Command); — отправка команды индикатору.
Команды индикатора
С функцией отправки команд используются готовые константы, объявленные в начале драйвера (пример вызова: LCD_Com(LCD_CLEAR);):
- LCD_FIRST_ROW — перевести курсор в 1-ю строку.
- LCD_SECOND_ROW — перевести курсор во 2-ю строку.
- LCD_THIRD_ROW — перевести курсор в 3-ю строку.
- LCD_FOURTH_ROW — перевести курсор в 4-ю строку.
- LCD_CLEAR — очистить дисплей.
- LCD_RETURN_HOME — вернуть курсор в исходную позицию и вернуть сдвинутое изображение на место; содержимое display data RAM при этом не затрагивается.
- LCD_CURSOR_OFF — выключить курсор.
- LCD_UNDERLINE_ON — включить курсор-подчёркивание.
- LCD_BLINK_CURSOR_ON — включить мигающий курсор.
- LCD_MOVE_CURSOR_LEFT — сдвинуть курсор влево без изменения display data RAM.
- LCD_MOVE_CURSOR_RIGHT — сдвинуть курсор вправо без изменения display data RAM.
- LCD_TURN_ON — включить дисплей.
- LCD_TURN_OFF — выключить дисплей.
- LCD_SHIFT_LEFT — сдвинуть изображение влево без изменения display data RAM.
- LCD_SHIFT_RIGHT — сдвинуть изображение вправо без изменения display data RAM.
Похожие материалы на сайте