Инициализация LCD дисплея 1602A, с управляющим контроллером ks0066U.

Инициализация LCD дисплея 1602A, с управляющим контроллером ks0066U.

Некоторое время лежал без дела вот такой дисплей.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

И вот появилось желание прикрутить его к одному из проектов, можно, конечно, постараться найти библиотеку с готовыми функциями, но в таком случае картина, о том как работает дисплей, будет неполная, а нас это не устраивает. Один раз, разобравшись с принципом работы LCD дисплея, не составит большого труда написать свою библиотеку под нужный дисплей, если она отсутствуют или чем-то не устраивает.

Итак, начнём.
Первое что надо сделать — это найти распиновку, то есть какой контакт за что отвечает, второе — найти название контроллера, который управляет дисплеем, для этого скачиваем даташит на данный LCD и открываем его на первой странице.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Контакты считаются слева направо, первый отмечен красной стрелочкой. Напряжение питание равно 5 вольтам, управляющий контроллер S6A0069 или аналогичный, например, ks0066U.

Для чего мы искали название управляющего контроллера? Дело в том, что в даташите на дисплей есть временные задержки(timing diagram), описана система команд, но нет банальной инициализации, а без неё никуда.
Далее, открываем вторую страницу и видим таблицу, в которой написано какой контакт за, что отвечает.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

DB7…DB0 – шина данных/адреса.

R/W - определяет что будем делать, считывать(R/W=1) или записывать(R/W=0)

R/S – определяет, что будем слать команду(RS=0) или данные(RS=1)

E – стробирующий вход, изменяя сигнал на этом входе мы разрешаем дисплею считывать/записывать данные.

LED± – управление подсветкой.

Надо сказать, что на доставшемся мне дисплее подсветка просто так не включится, для этого надо впаять резистор, обозначенный на плате как R7. Но пока она нам и не нужна.

Скачиваем даташит на управляющий контроллер и находим инструкцию по инициализации. Картинки можно увеличить, кликнув по ним.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Оказывается, таких инструкций целых две, для 8-битного и 4-битного режима. Что ж это за режимы такие? Данные режимы определяют по скольки проводкам будут передаваться данные: по четырём, либо по восьми. Давайте рассмотрим передачу по 4 проводам, в таком случае дисплей будет работать медленнее, но зато мы сэкономим 4 вывода микроконтроллера, да и реализация восьмибитного режима не намного отличается.

Схема подключения информационных выглядит следующим образом.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Контрастность можно регулировать включив потенциометр между выводами питания.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.


Хотелось бы обратить внимание, что во время инициализации R/S и R/W всегда равны нулю, то есть мы будем слать команды.

При инициализации можно настроить:
  • N - количество отображаемых строк
  • C - включить или выключить курсор
  • B - сделать курсор мигающим
  • I/D - увеличивать или уменьшать значение счётчика адреса
  • SH - двигать окошко дисплея

Два последние пункта рассмотрим подробнее.
На картинке ниже показано по какому адресу надо писать данные чтобы они отобразились в определённой позиции, например, если мы хотим вывести символ на первой позиции второй строки, то мы должны писать по адресу 0х40.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

После этого значение счётчика автоматически изменится, либо увеличится, либо уменьшится, а вместе с ним изменится и положение курсора.

Кстати, память в которую мы пишем, называется DDRAM, все что мы запишем в эту память выведется на дисплей, ещё есть CGROM, в которой хранится таблица знакогенератора.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Эту таблицу нельзя изменить, но из неё можно брать уже готовые символы. Ещё один вид памяти это CGRAM, она то же представляет собой таблицу знакогенератора, но символы в этой таблице мы рисуем сами.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Теперь пару слов о движении экрана, дело в том что обычно на дисплее мы видим не всю DDRAM, а лишь определённую часть , как показано на картинке ниже.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

В невидимую часть мы также можем писать, но то что мы запишем видно не будет, до тех пор, пока мы не подвинем на это место окошко экрана.

С теорией закончили переходим к практике.
Картина общения с LCD дисплеем в 4-битном режиме выглядит следующим образом.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Данные шлются байтами, но так, как у нас 4-битный режим, то для того чтобы отправить байт надо сделать 2 посылки, старшим битом вперёд. На картинке первая посылка обозначена D7(старшая тетрада), вторая D3(младшая тетрада). Перед следующей посылкой мы должны проверить флаг занятости и если он не установлен снова можно слать, если установлен ждём, пока контроллер, управляющий LCD закончит свои дела.

Имея общую картину посылки, давайте разберемся как реализовать операцию отправки.
Инициализация LCD дисплея 1602A, с управляющим контроллером  ks0066U.

Для отправки надо по 8-битной шине:
  • убедиться, что управляющий контроллер свободен
  • установить RS в 0(команда) или 1(данные), в зависимости оттого что будем слать
  • R/W установить в 0
  • поднимаем строб E(устанавливаем в 1)
  • выдаём код команды/данные в шину
  • задержка 2us
  • опускаем строб Е


Операция чтения реализуется аналогично:
  • убедиться, что управляющий контроллер свободен
  • порт данных на вход с подтяжкой
  • установить RS в 0(команда) или 1(данные), в зависимости оттого что будем читать
  • R/W установить в 1
  • поднимаем строб E( в этот момент LCD выдаст данные в шину)
  • задержка 2us
  • читаем то что выдал LCD
  • опускаем строб Е

Откуда взялась задержка 2us?

Выше таймингов есть таблица в которой написано чему равны задержки изображённые на графике, так вот длительность стробирующего импульса - tw должна быть равна 230nS или 450nS в зависимости от напряжения питания, мы взяли чуть с запасом. Почему мы учли только эту задержку? Потому что значение остальных задержек очень мало.

Для отправки по 4-битной шине:
  • убедиться, что управляющий контроллер свободен
  • установить RS в 0(команда) или 1(данные), в зависимости оттого что будем слать
  • R/W установить в 0
  • поднимаем строб E(устанавливаем в 1)
  • выдаём старшую тетраду в шину
  • задержка 2us
  • опускаем строб Е
  • задержка 1us
  • поднимаем строб E(устанавливаем в 1)
  • выдаём младшую тетраду в шину
  • задержка 2us
  • опускаем строб Е


Для чтения по 4-битной шине:
  • убедиться, что управляющий контроллер свободен
  • порт данных на вход с подтяжкой
  • установить RS в 0(команда) или 1(данные), в зависимости оттого что будем читать
  • R/W установить в 1
  • поднимаем строб E(устанавливаем в 1)
  • задержка 2us
  • читаем старшую тетраду
  • опускаем строб Е
  • задержка 1us
  • поднимаем строб E(устанавливаем в 1)
  • задержка 2us
  • читаем младшую тетраду
  • опускаем строб Е


Поднятие строба и вывод команды/данных в шину, можно поменять местами. Теперь не составит труда инициализировать дисплей. Для упрощения инициализации, мы заменим чтение флага занятости задержкой, а работу с флагом рассмотрим позже.
Надо отметить, что при инициализации в 4-битном режиме используются 4-битные команды, а после инициализации 8-битная система команд, поэтому для инициализации мы реализуем отдельную функцию отправки команд void Write_Init_Command(uint8_t data).

//Код инициализации для Atmega16
#define F_CPU 8000000UL

#define LCD_PORT PORTA
#define LCD_DDR DDRA
#define LCD_PIN PINA

#define DATA_BUS 0XF0

#define RS 0
#define RW 1
#define E 2


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

void Write_Init_Command(uint8_t data)
{
	//ножки по которым передаются команды/данные  на выход
	LCD_DDR |= DATA_BUS;
        //будем слать команду
	LCD_PORT &= ~(1<<RS);
	LCD_PORT &= ~(1<<RW);
	
        //затираем прошлую отправку нулями
	LCD_PORT &= ~DATA_BUS;
        //выводим команду в шину
	LCD_PORT |= data;
	Strob();
	_delay_us(100);
	
}

void Strob(void)
{
	LCD_PORT |= (1<<E);
	_delay_us(2);
	LCD_PORT &= ~(1<<E);
}

void LCD_Init(void)
{
	_delay_ms(50);
	
	Write_Init_Command(0x20);
	Write_Init_Command(0x20);
        //включаем дисплей, в режиме 2-х линий
	Write_Init_Command(0xC0);
	
	_delay_us(50);
	
	Write_Init_Command(0x00);
        //включаем отображение мерцающего курсора
	Write_Init_Command(0xF0);
	
	_delay_us(50);
	
	Write_Init_Command(0x00);
        //очищаем дисплей
	Write_Init_Command(0x10);
	
	_delay_ms(2);
	
	Write_Init_Command(0x00);
       //значение DDRAM увеличивается, без сдвига экрана
	Write_Init_Command(0x60);
	
	
}


int main(void)
{
	LCD_DDR = 0XFF;
	LCD_Init();
    while(1)
    {
        
    }
}

Весело мигающий курсор, свидетельствует о том, что инициализация прошла успешно. В следующей статье будем учиться выводить символы на LCD дисплей.
комментарии
18