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

   За все время увлечения электроникой мне довелось пользоваться ЖКД  от нескольких производителей - 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;
}
   Первые результаты мы получили.  Доводить программу до ума будем в следующих статьях.

Файлы

У вас недостаточно прав для комментирования.