Учебный курс. Подключение lcd к микроконтроллеру. Получение первых результатов.

29/10/2009 - 21:00
   За все время увлечения электроникой мне довелось пользоваться ЖКД  от нескольких производителей - DataVision, WINSTAR, Uniworld Technology Corp. Они отличались типом контроллера, количеством выводов и длинною строк, но при этом все имели одинаковую схему подключения, систему команд и обслуживались одной и той же программой со стороны микроконтроллера. Поэтому, хотя речь сейчас пойдет о дисплее WH0802A фирмы WINSTAR,  все ниже сказанное применимо к символьным ЖК-дисплеям и других фирм.

Итак, подключаем дисплей WH0802A-YGH-CT к микроконтроллеру

   WH0802A – двухстрочный символьный дисплей на 8 знакомест со встроенным управляющим контроллером KS0066.
   Разбираем назначение выводов дисплея.

назначение выводов lcd

   У некоторых дисплеев есть два дополнительных вывода – выводы подсветки +LED и –LED. Причем если выводы есть – это еще не означает что есть и подсветка. Как и наоборот. У моего дисплея подсветка есть, а выводов управления нет.
  По умолчанию подсветка у дисплея WH0802A-YGH-CT отключена. Чтобы ее включить, нужно проделать парочку нехитрых манипуляций, а именно – установить две перемычки и впаять токоограничительный резистор (смотри на фотке RK, JF и RA соответственно).

WH0802A с обратной стороны

Схема подключения дисплея

схема подключения lcd к микроконтроллеру
    Это типовая схема включения символьных LCD. Схему управления подсветкой дисплея мы задействовать не будем, но я ее на всякий случай нарисовал. 

Начальный код

   Подав питание на схему, нужно покрутить регулятор контраста (резистор R1). Если на экранчике появилась верхняя строка, значит, он живой и самое время приступать к написанию кода. На начальном этапе мы будем использовать 8-ми разрядную шину. Чтобы получить первые результаты, нам понадобится написать две функции – функцию записи данных и функцию записи команд. Отличаются они всего одной строчкой – когда записываются данные, сигнал RS должен быть 1, когда записывается команда, RS должен быть 0. Функции чтения мы пока использовать не будем, поэтому сигнал R/W будет всегда 0.  

   Цикл записи для 8-ми разрядной шины выглядит следующим образом:
1.    Установить RS (0 - команда, 1 – данные)
2.    Вывести значение байта данных на шину DB7…DB0
3.    Установить E=1
4.    Программная задержка 1
5.    Установить E=0
6.    Программная задержка 2

   Контроллер символьного ЖК-дисплея, не обладает бесконечным быстродействием, поэтому между некоторыми операциями используются программные задержки. Первая нужна для удержания на некоторое время строб сигнала, вторая, для того чтобы контроллер успел записать данные или выполнить команду. Величины задержек всегда приводятся в описании на контроллер дисплея и нужно всегда выдерживать хотя бы их минимальное значение, в противном случае неизбежны сбои в работе контроллера.
  Вообще у контроллера дисплея есть так называемый флаг занятости – BF. Если флаг в 1 – контроллер занят, если в 0 – свободен. Вместо второй программной задержки можно читать флаг занятости и проверять, когда контроллер дисплея освободится. Но поскольку мы хотим быстро получить первые результаты, с флагом занятости будем  разбираться потом.

//подключаем символьный ЖК-дисплей к AVR
#include
<ioavr.h>
#include <intrinsics.h>

//порт к которому подключена шина данных ЖКД
#define PORT_DATA PORTD
#define PIN_DATA  PIND
#define DDRX_DATA DDRD

//порт к которому подключены управляющие выводы
#define PORT_SIG PORTB
#define PIN_SIG  PINB
#define DDRX_SIG DDRB

//номера выводов микроконтроллера
//к которым подключены управляющие выводы ЖКД

#define RS 5
#define RW 6
#define EN 7

//макросы для работы с битами
#define ClearBit(reg, bit)       reg &= (~(1<<(bit)))
#define SetBit(reg, bit)          reg |= (1<<(bit))   

#define F_CPU 8000000
#define _delay_us(us)     __delay_cycles((F_CPU / 1000000) * (us));
#define _delay_ms(ms)     __delay_cycles((F_CPU / 1000) * (ms));

//функция записи команды
void LcdWriteCom(unsigned char data)
{
  ClearBit(PORT_SIG, RS);    // устанавливаем RS в 0
  PORT_DATA = data;        // выводим данные на шину   
  SetBit(PORT_SIG, EN);    // устанавливаем Е в 1
  _delay_us(2);
  ClearBit(PORT_SIG, EN);    // устанавливаем Е в 0
  _delay_us(40);
}
 
//функция записи данных
void LcdWriteData(unsigned char data)
{
  SetBit(PORT_SIG, RS);     //устанавливаем RS в 1
  PORT_DATA = data;        //выводим данные на шину     
  SetBit(PORT_SIG, EN);     //устанавливаем Е в 1
  _delay_us(2);
  ClearBit(PORT_SIG, EN);    // устанавливаем Е в 0
  _delay_us(40);
}

int main( void )
{
  while(1);
  return 0;
}


Здесь нет сложных мест, все должно быть понятно. Идем дальше.

   Любой ЖК-дисплей перед использованием нужно инициализировать. Процесс инициализации обычно описан в datasheet`е на контроллер дисплея. Но даже если там и нет информации, последовательность, скорее всего, будет такая.
 
1. Подаем питание

2. Ждем >40 мс

3. Подаем команду Function set



DL – бит установки разрядности шины
0 – 4 разрядная шина, 1 – 8 разрядная шина

N – бит установки количества строк дисплея
0 – однострочный режим, 1 – двухстрочный режим

F – бит установки шрифта
0 – формат 5*8, 1 – формат 5*11

* - не важно что будет в этих битах

4. Подаем команду Display ON/OFF



D – бит включения/выключения дисплея
0 – дисплей выключен, 1 – дисплей включен

C – бит включения/выключения курсора
0 – курсор выключен, 1 – курсор включен

B – бит включения мерцания
0 – мерцающий курсор включен, 1 – мерцающий курсор выключен

5. Подаем команду Clear Display



6. Ждем > 1,5 ms


7. Подаем команду Entry Mode Set



I/D – порядок увеличения/уменьшения адреса DDRAM(ОЗУ данных дисплея)
0 – курсор движется влево, адрес уменьшается на 1, 1 – курсор движется вправо, адрес увеличивается на 1

SH – порядок сдвига всего дисплея
0 – сдвига нет, 1 – сдвиг происходит согласно сигналу I/D – если он 0 – дисплей сдвигается вправо, 1 – дисплей сдвигается влево

Для нашего примера функция инициализации будет выглядеть так
//функция инициализации
void InitLcd(void)
{
  //настраиваем порты ввода/вывода
  DDRX_DATA = 0xff;
  PORT_DATA = 0xff;   
  DDRX_SIG = 0xff;
  PORT_SIG |= (1<<RW)|(1<<RS)|(1<<EN);
  ClearBit(PORT_SIG, RW);

  _delay_ms(40);
  LcdWriteCom(0x38); //0b00111000 - 8 разрядная шина, 2 строки
  LcdWriteCom(0x0f);  //0b00001111 - дисплей, курсор, мерцание включены
  LcdWriteCom(0x01);  //0b00000001 - очистка дисплея
  _delay_ms(2);
  LcdWriteCom(0x06);  //0b00000110 - курсор движется вправо, сдвига нет
}


   Теперь у нас есть необходимый минимум, чтобы начать работу с дисплеем. Выведем на него слово "Test."
//собственно наша программа
int main( void )
{
  InitLcd();
  LcdWriteData('T');
  LcdWriteData('e');
  LcdWriteData('s');
  LcdWriteData('t');
  LcdWriteData('.');
  while(1);
  return 0;
}
   Первые результаты мы получили.  Доводить программу до ума будем в следующих статьях.

Файлы

Комментарии   

# alexandershahbazov 08.12.2009 20:57
Точно не помню похож ли контроллер
KS0066 на HD44780 .
Т.е. применимо ли выше сказанное для
HD44780 .
Ответить | Ответить с цитатой | Цитировать
# Pashgan 08.12.2009 22:28
У HD44780 немного отличается инициализация.. она выполняется без чтения флага занятости. Если его не использовать, то все тоже самое.
Ответить | Ответить с цитатой | Цитировать
# Vetal-Soft 27.01.2010 09:32
У дисплея на фото выводы для подключения подсветки обозначены А и К. Анод и катод соответственно :-)
Ответить | Ответить с цитатой | Цитировать
# Guest 16.04.2010 18:16
Спасибо тебе огромное за статьи, всё понятно и доходчиво. Я как начинающий электронщик не знаю что бы без них делал, и с нетерпением жду новых статей.
Если по делу, то в файле исходника для IAR для соответствия прогаммы и схемы неибходимо заменить соответствующую часть на:
#define RS 4
#define RW 5
#define EN 6
Проверенно во Proteus
Ответить | Ответить с цитатой | Цитировать
# Aleks 21.10.2010 00:14
По-моему источник этой ошибки - комментарий
"//номера выводов микроконтроллер а
//к которым подключены управляющие выводы ЖКД"
Как в комментарии указано "номера выводов", так и определено: RS прицеплен к выводу 5 (корпус DIP), вот и определено
#define RS 5
был бы использован корпус TQFP, было бы
#define RS 44
___
По-моему если бы комментарий был:
"//номера битов порта микроконтроллер а
//к которым подключены управляющие выводы ЖКД", то и было бы
#define RS 4
или
#define RS PB4
___
Это кстати не только для IAR, а во всех примерах эта ошибка, и в html (2010-10-20)
___
С уважением Александр.
За статьи спасибо.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 17.04.2010 06:12
Спасибо, исправлю.
Ответить | Ответить с цитатой | Цитировать
# Guest 04.06.2010 14:09
спасибо большое.
Вс' так красиво и доступно расписано.

Только вот здеся ошибочка есть:
"3. Установить E=0
4. Программная задержка 1
5. Установить E=1
6. Программная задержка 2"

наоборот же надо ведь, во всяком случае у вас в коде наоборот написано, сначала установить, потом обнулить
Ответить | Ответить с цитатой | Цитировать
# Pashgan 04.06.2010 17:48
Ужас.. косяки так и всплывают. Давно писал этот материал, руки не доходят покопаться в нем снова.
Ответить | Ответить с цитатой | Цитировать
# Guest 05.07.2010 18:48
Замечательная серия статеи. У мня ЖКД -
MTC-16201x (mega16, кварц 11.0592). Были проблемы с отображением,
терялись символы, нарушалась последовательно сть, но после установки флага BF, все пришло в норму (подобрать задержку не получилось).
Подскажите, как русифицировать текст?
Спасибо за статьи.
Ответить | Ответить с цитатой | Цитировать
# Guest 26.08.2010 10:08
Спасибо за статьи продолжай в том же духе!
Исправь здесь пожалуйста
1.Установить RS (0 - данные, 1 – команда)
Ответить | Ответить с цитатой | Цитировать
# Pashgan 26.08.2010 16:16
Исправил все перечисленные ошибки ;-)
Ответить | Ответить с цитатой | Цитировать
# Ali 19.11.2010 07:08
А чтонить поменяется в программе, железе если использовать wh1602a ?
Ответить | Ответить с цитатой | Цитировать
# Not_found 21.11.2010 05:55
А статья для 4-х битного подключения будет? уже год прошел с момента написания, но эта тема все равно всегда будет актуальна... ;-)
Ответить | Ответить с цитатой | Цитировать
# Not_found 21.11.2010 06:06
упс, простите за флуд кое-что нашел)) :oops:
Ответить | Ответить с цитатой | Цитировать
# Volonter 21.11.2010 20:47
Почему в коде:
#define PORT_DATA PORTC
#define PIN_DATA PINC
#define DDRX_DATA DDRC

"С" если на схеме четко показано что двунаправленная шина подключена к порту "D"?
Ответить | Ответить с цитатой | Цитировать
# Pashgan 21.11.2010 22:59
В тексте очепятка. Исправил
Ответить | Ответить с цитатой | Цитировать
# Volonter 21.11.2010 23:36
Не могу заставить работать lcd точно такой-же как у тебя на картинке с atmega128 в proteus... хоть убей но не пашет, на макетке то-же...
Ответить | Ответить с цитатой | Цитировать
# Pashgan 29.01.2011 22:06
Выложи код на форуме, там разберемся.
Ответить | Ответить с цитатой | Цитировать
# АлексейЛ81 28.01.2011 21:26
Добрый день. Попробовал, не работает. Точнее рисует "0__\_" Я скачал ваш пример (для CodeVision) точно также. Подскажите, почему так. Контроллер ATmega8535 в TQFP корпусе.
Ответить | Ответить с цитатой | Цитировать
# АлексейЛ81 28.01.2011 21:27
Я новичёк в контроллерах, не ругайтесь.
Ответить | Ответить с цитатой | Цитировать
# Oposition Spb 10.03.2011 13:24
У меня тоже что-то все печально...
пробывал юзать HDM08216L - он вроде на HD44780 ... результат дальше появления второй строчки не ушел. Уже даже сам написал весь код используя даташит. И не получилось ничего с WH1602L-DYK-CTT , получил на экране появление кирилицы в место S - З, t-г, f-Л... если слать более трех символов курсор начинает бегать по полю... Наверно мне дохлые стекляшки попались :(
Ответить | Ответить с цитатой | Цитировать
# Pashgan 15.03.2011 19:45
Попробуй вот эту библиотеку http://chipenable.ru/index.php/programming-c/74-avr-biblioteka-lcd-ks0066-hd44780.html
Ответить | Ответить с цитатой | Цитировать
# Pigsel 24.03.2011 12:10
В схеме внешний кварц обязателен, или со внутреннего генератора будет работать?
Ответить | Ответить с цитатой | Цитировать
# AndreySkv 17.07.2011 18:02
Вот еще ошибку нашел:

//номера выводов микроконтроллер а
//к которым подключены управляющие выводы ЖКД
#define RS 5
#define RW 6
#define EN 7

Не номера выводов микроконтроллер а , а номера порта!!!

Правильно так:

#define RS 4
#define RW 5
#define EN 6

Иначе не работает. Проверено!
Ответить | Ответить с цитатой | Цитировать
# Cudi 13.09.2011 15:17
Обяхательно все контакты LCD (Data) подключать?! НА бейсике я подключал только 4 порта Дата : data7, 6, 5, 4.
Нельзя код сделать для 4х портов дата?!
Ответить | Ответить с цитатой | Цитировать
# Stranger 16.12.2011 03:54
Что надо сделать, чтобы подключить индикатор к ATmega8?
Можно ли использовать тактовую частоту контроллера от внутреннего генератора 1МГц?
Пробовал подключить индикатор к тем же портам контоллера ATmega8 - не работает. В тексте кода (из примера для CodeVision) поменял первую строку на #include . Нужно ли еще что нибудь менять?
Ответить | Ответить с цитатой | Цитировать
# Шурик 02.01.2012 13:05
Скажи, пожалуйста, где ты нашел переменный резистор в Eagle, когда схему рисовал.
Ответить | Ответить с цитатой | Цитировать
# _Alex_ 23.03.2012 18:22
Зачётная статья =)
Ответить | Ответить с цитатой | Цитировать
# usta 05.09.2012 08:20
огромное спасибо автору
совместно с этим статя будет понятна всем
http://www.geocities.com/dinceraydin/djlcdsim/djlcdsim.html
Ответить | Ответить с цитатой | Цитировать
# MS24 14.10.2012 07:46
А как сделать буегущую строку? я пытаюсь сдвигать экран, но у меня ничего не получается, просто уходит в невидимую область.
Ответить | Ответить с цитатой | Цитировать
# Константин1982 17.10.2012 20:36
Дисплей как у вас, как сделать что бы писал и на нижней строке?
Ответить | Ответить с цитатой | Цитировать
# Константин1982 18.10.2012 08:22
Сам отвечу на свой вопрос. Информацию подчерпнул из даташита. Что бы начать писать во вторую строку надо перевести внутренний счетчик контроллера дисплея на адрес 40H, следовательно ввести "команду" 11000000 bin или C0 Hex, а после можно снова вводить "данные"
Ответить | Ответить с цитатой | Цитировать
# Pashgan 18.10.2012 20:06
Почитай следующие статьи посвященные lcd. Там добавлен макрос LCD_Goto(x, y) для позиционировани я курсора.
Код:
/*позиционирование курсора*/
#define LCD_Goto(x,y) LCD_WriteCom(((((y)& 1)*0x40)+((x)& 7))|128)
Ответить | Ответить с цитатой | Цитировать
# Константин1982 04.11.2012 18:30
Спасибо!
Ответить | Ответить с цитатой | Цитировать
# Игорь7 17.01.2013 14:46
Здравствуйте, я попробовал по описанной выше методике сделать программу на ассемблере в Algoritthm Builder, но почему-то самостоятельно МК отказывается работать. Не подскажите почему? При самом программировани и все выводится на экран как нужно, а после отключения программатора и перезапуска МК только экран очищается.
Ответить | Ответить с цитатой | Цитировать
# Pashgan 18.01.2013 06:37
А дисплей такой же?
Ответить | Ответить с цитатой | Цитировать
# Игорь7 18.01.2013 10:41
Дисплей WH0802A1.
Ответить | Ответить с цитатой | Цитировать
# Игорь7 18.01.2013 10:42
Может я что-то в программе не так делаю ...
Ответить | Ответить с цитатой | Цитировать
# Игорь7 18.01.2013 11:35
http://www.fayloobmennik.net/2532296
Ответить | Ответить с цитатой | Цитировать
# Валян 04.08.2016 12:45
Спасибо большое! Как раз то, что искал. Везде с 4 выводами только, а мне вот этот как раз нужно было. Чтобы понять все что к чему.
Ответить | Ответить с цитатой | Цитировать

Добавить комментарий

Защитный код
Обновить