Как стать автором
Обновить

Подключение жидкокристаллического дисплея LCD1602 (HD44780) к микроконтроллеру ATmega8 в 4-битном режиме

Уровень сложностиСредний
Время на прочтение8 мин
Количество просмотров11K
Автор оригинала: lokeshc

Примечание:

Статья не является оригинальным переводом. Статья созданы на базе статьи (см. источник информации указанный ниже) путем его перевода, использования основного текста оригинала и дополнена автором; данный код протестирован на реальном устройстве ф. Winstar.

LCD-дисплеи (Liquid Crystal Displays) используют для отображения состояния или параметров в различных приборах.

LCD1602 – это 16-выводное устройство, имеющее 8 выводов для передачи данных (D0-D7) и 3 вывода управления (RS, RW, EN). Остальные 5 выводов предназначены для питания и подсветки ЖК-дисплея. Цифры «1602» указывают на формат выводимой (отображаемой) информации: 16x02 символов (рисунок 1).

Выводы управления помогают нам настроить LCD-дисплей в командном режиме или режиме передачи данных. Они также помогают настроить режим чтения или записи, а также время чтения или записи.

LCD-дисплей 16x2 можно использовать в 4-битном или 8-битном режиме в зависимости от технических требований. Чтобы использовать его, нам необходимо отправить определенные команды на LCD-дисплей в командном режиме, и как только ЖК-дисплей будет настроен в соответствии с нашими требованиями, мы сможем отправить необходимые данные в режиме передачи данных.

Рисунок 1 – Распиновка жидкокристаллического дисплея LCD1602 (HD44780)
Рисунок 1 – Распиновка жидкокристаллического дисплея LCD1602 (HD44780)

В 4-битном режиме данные/команды отправляются в 4-битном (полубайтном) формате.

Для этого сначала необходимо отправить «старшие» (верхние)  4 бита, а затем отправить «младшие» (нижние) 4 бита данных/команд.

Только 4 выводы данных (D4 - D7) LCD-дисплея 16x02 подключены к микроконтроллеру, а другие управляющие выводы RS (выбор регистра), RW (чтение/запись), E (сигнал разрешения) подключены к другим GPIO выводам микроконтроллера (рисунок 2).

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

Рисунок 2 – Подключение жидкокристаллического дисплея LCD1602 к микроконтроллеру ATmega8 в 4-битном режиме
Рисунок 2 – Подключение жидкокристаллического дисплея LCD1602 к микроконтроллеру ATmega8 в 4-битном режиме

Подстрочный резистор R1 предназначен для точной подстройки контрастности дисплея. Резистор R2 предназначен для ограничения тока на аноде подсветки дисплея.

 Функция инициализации дисплея:

1. Необходимо подождать не менее 15 мс, время инициализации включения питания для LCD1602.

2. Отправить команду 0x02, которая инициализирует LCD-дисплей 16x2 в 4-битном режиме.

3. Отправить команду 0x28, которая переводит LCD-дисплей в 2-строчный, 4-битный режим и 5x8 точек.

4. Отправить одну из команд включения курсора дисплея (0x0E, 0x0C).

5. Отправить команду 0x06 (сдвиг курсора вправо).

 Листинг кода:

void LCD_Init (void)  /* LCD Initialize function */
{
	LCD_Dir = 0xFF;		/* Make LCD port direction as o/p */
	_delay_ms(20);		/* LCD Power ON delay always >15ms */
	
	LCD_Command(0x02);	/* Send for 4 bit initialization of LCD  */
	LCD_Command(0x28);	/* 2 line, 5*7 matrix in 4-bit mode */
	LCD_Command(0x0c);	/* Display on cursor off */
	LCD_Command(0x06);	/* Increment cursor (shift cursor to right) */
	LCD_Command(0x01);	/* Clear display screen */
	_delay_ms(2);
}

Теперь мы успешно инициализировали LCD-дисплей, и он готов принимать данные в 4-битном (полубайтном) режиме для отображения.

Чтобы отправить команду/данные на LCD -дисплей 16x02, мы должны отправить «старший» (верхний) полубайт, а затем «младший» (нижний) полубайт. Поскольку выводы D4-D7 LCD-дисплея 16x02 подключены как выводы данных, мы должны сдвинуть младший полубайт вправо на 4 перед передачей.

Функция записи команд (инструкций):

1. Сначала отправим более «высокий» полубайт команды.

2. Установим «низкий» уровень на выводе RS, RS=0 (регистр команд)

3. Установим вывод RW на «низкий» уровень, RW=0 (операция записи) или подключим его к земле.

4. Подадим импульс от «высокого» до «низкого» при включении (E).

5. Отправим «младший» полубайт команды.

6. Подадим импульс от «высокого» до «низкого» при включении (E). 

Листинг кода:

void LCD_Command( unsigned char cmnd )
{
	LCD_Port = (LCD_Port & 0x0F) | (cmnd & 0xF0);/* Sending upper nibble */
	LCD_Port &= ~ (1<<RS);		/* RS=0, command reg. */
	LCD_Port |= (1<<EN);		/* Enable pulse */
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_us(200);
	LCD_Port = (LCD_Port & 0x0F) | (cmnd << 4);/* Sending lower nibble */
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}

Функция записи данных:

1. Сначала отправим более «высокий» полубайт данных.

2. Установим вывод RS на «высокий» уровень, RS=1 (регистр данных)

3. Установим вывод RW на «низкий» уровень, RW=0 (операция записи) или подключим его к земле.

4. Подадим импульс от «высокого» до «низкого» при включении (E).

5. Отправим «младший» полубайт данных.

6. Подадим «импульс» от «высокого» до «низкого» при включении (E).

Листинг кода:

void LCD_Char( unsigned char data )
{
	LCD_Port = (LCD_Port & 0x0F) | (data & 0xF0);/* Sending upper nibble */
	LCD_Port |= (1<<RS);  /* RS=1, data reg. */
	LCD_Port|= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_us(200);
	LCD_Port = (LCD_Port & 0x0F) | (data << 4);  /* Sending lower nibble */
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}

Листинг программы:

#define F_CPU 8000000UL			/* Define CPU Frequency e.g. here 8MHz */
#include <avr/io.h>			/* Include AVR std. library file */
#include <util/delay.h>			/* Include Delay header file */

#define LCD_Dir  DDRB			/* Define LCD data port direction */
#define LCD_Port PORTB			/* Define LCD data port */
#define RS PB0				/* Define Register Select pin */
#define EN PB1 				/* Define Enable signal pin */
 

void LCD_Command( unsigned char cmnd )
{
	LCD_Port = (LCD_Port & 0x0F) | (cmnd & 0xF0); /* sending upper nibble */
	LCD_Port &= ~ (1<<RS);		/* RS=0, command reg. */
	LCD_Port |= (1<<EN);		/* Enable pulse */
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);

	_delay_us(200);

	LCD_Port = (LCD_Port & 0x0F) | (cmnd << 4);  /* sending lower nibble */
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}


void LCD_Char( unsigned char data )
{
	LCD_Port = (LCD_Port & 0x0F) | (data & 0xF0); /* sending upper nibble */
	LCD_Port |= (1<<RS);		/* RS=1, data reg. */
	LCD_Port|= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);

	_delay_us(200);

	LCD_Port = (LCD_Port & 0x0F) | (data << 4); /* sending lower nibble */
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}

void LCD_Init (void)			/* LCD Initialize function */
{
	LCD_Dir = 0xFF;			/* Make LCD port direction as o/p */
	_delay_ms(20);			/* LCD Power ON delay always >15ms */
	
	LCD_Command(0x02);		/* send for 4 bit initialization of LCD  */
	LCD_Command(0x28);              /* 2 line, 5*7 matrix in 4-bit mode */
	LCD_Command(0x0c);              /* Display on cursor off*/
	LCD_Command(0x06);              /* Increment cursor (shift cursor to right)*/
	LCD_Command(0x01);              /* Clear display screen*/
	_delay_ms(2);
}


void LCD_String (char *str)		/* Send string to LCD function */
{
	int i;
	for(i=0;str[i]!=0;i++)		/* Send each char of string till the NULL */
	{
		LCD_Char (str[i]);
	}
}

void LCD_String_xy (char row, char pos, char *str)	/* Send string to LCD with xy position */
{
	if (row == 0 && pos<16)
	LCD_Command((pos & 0x0F)|0x80);	/* Command of first row and required position<16 */
	else if (row == 1 && pos<16)
	LCD_Command((pos & 0x0F)|0xC0);	/* Command of first row and required position<16 */
	LCD_String(str);		/* Call LCD string function */
}

void LCD_Clear()
{
	LCD_Command (0x01);		/* Clear display */
	_delay_ms(2);
	LCD_Command (0x80);		/* Cursor at home position */
}
 
int main()
{

	LCD_Init();			/* Initialization of LCD*/

	LCD_String("Hello World");	/* Write string on 1st line of LCD*/
	LCD_Command(0xC0);		/* Go to 2nd line*/
	LCD_String("4 bit");	/* Write string on 2nd line*/
	while(1);
}

Для удобства использования оформим описанный выше код в виде библиотеки. Для этого создадим два файла LCD1602.h и LCD1602.c.

 Листинг файла LCD1602.h

#ifndef LCD1602_H_
#define LCD1602_H_

#define LCD_Dir  DDRB
#define LCD_Port PORTB
#define RS PB0
#define EN PB1

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

void LCD_Command( unsigned char cmnd );
void LCD_Char( unsigned char data );
void LCD_Init (void);
void LCD_String (char *str);
void LCD_String_xy (char row, char pos, char *str);	
void LCD_Clear();

#endif /* LCD1602_H_ */

Листинг файла LCD1602.c

#include "LCD1602.h"

void LCD_Command( unsigned char cmnd )
{
	LCD_Port = (LCD_Port & 0x0F) | (cmnd & 0xF0);
	LCD_Port &= ~ (1<<RS);
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);

	_delay_us(200);

	LCD_Port = (LCD_Port & 0x0F) | (cmnd << 4);
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}


void LCD_Char( unsigned char data )
{
	LCD_Port = (LCD_Port & 0x0F) | (data & 0xF0);
	LCD_Port |= (1<<RS);
	LCD_Port|= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);

	_delay_us(200);

	LCD_Port = (LCD_Port & 0x0F) | (data << 4);
	LCD_Port |= (1<<EN);
	_delay_us(1);
	LCD_Port &= ~ (1<<EN);
	_delay_ms(2);
}

void LCD_Init (void)
{
	LCD_Dir = 0xFF;
	_delay_ms(20);
	
	LCD_Command(0x02);
	LCD_Command(0x28);
	LCD_Command(0x0c);
	LCD_Command(0x06);
	LCD_Command(0x01);
	_delay_ms(2);
}


void LCD_String (char *str)
{
	int i;
	for(i=0;str[i]!=0;i++)
	{
		LCD_Char (str[i]);
	}
}

void LCD_String_xy (char row, char pos, char *str)
{
	if (row == 0 && pos<16)
	LCD_Command((pos & 0x0F)|0x80);
	else if (row == 1 && pos<16)
	LCD_Command((pos & 0x0F)|0xC0);
	LCD_String(str);
}

void LCD_Clear()
{
	LCD_Command (0x01);
	_delay_ms(2);
	LCD_Command (0x80);
}

Для подключения библиотеки необходимо файлы LCD1602.h и LCD1602.c поместить в папку с проектом, в программе Atmel Studio правой клавишей мыши кликнуть на имя проекта, выбрать Add - Existing Item, найти и выбрать все два файла, нажать OK.

Далее пишем код:

#include <avr/io.h>
#include "LCD1602.h"

int main()
{
	LCD_Init();
	LCD_String("Hello World");
	LCD_Command(0xC0);
	LCD_String("4 bit");

	while(1)
    {				
	
    }
}

После загрузки прошивки на дисплее LCD1602 мы увидим следующий результат, показанный на рисунке 3.

Рисунок 3 – Результат работы прошивки
Рисунок 3 – Результат работы прошивки

Расшифровка наиболее употребляемых команд, посылаемых от микроконтроллера в дисплей LCD1602 (HD44780) приведена в таблицах 1, 2.

Таблица 1 – Расшифровка наиболее употребляемых команд, посылаемых от микроконтроллера в дисплей LCD1602 (HD44780)

Команда ЖКИ

HEX-код

Выполняемые действия

Время вып., мкс

Очистка дисплея

0x01

Пустой экран, очистка памяти, курсор в левой верхней позиции

1640

Возвращает курсор в начало

0x02

Курсор в левой верхней позиции, память не очищается

1640

Сдвиг курсора влево

0x04

После вывода очередного символа курсор автоматически сдвигается на одно знакоместо влево

40

Сдвиг курсора вправо

0x06

После вывода очередного символа курсор автоматически сдвигается на одно знакоместо вправо

40

Выключение дисплея

0x08

Полное отсутствие изображения на экране ЖКИ

40

Выключение курсора

0x0C

Разрешается вывод изображения, но курсор не виден

40

Прямоугольная форма курсора

0x0D

Разрешается вывод изображения, курсор в виде темного мигающего прямоугольника

40

Линейная форма курсора

0x0E

Разрешается вывод изображения, курсор в виде нижней подстрочной немигающей линии

40

Комплексная форма курсора

0x0F

Разрешается вывод изображения, курсор в виде мигающего прямоугольника с подчеркиванием

40

Интерфейс 4 бита, 1 строка

0x20

Связь с однострочным ЖКИ через 4 линии шины данных

40

Интерфейс 4 бита, 2 строки

0x28

Связь с двухстрочным ЖКИ через 4 линии шины данных

40

Интерфейс 8 бит, 1 строка

0x30

Связь с однострочным ЖКИ через 8 линий шины данных

40

Интерфейс 8 бит, 2 строки

0x38

Связь с двухстрочным ЖКИ через 8 линий шины данных

40

Доступ к ОЗУ знакогенератора

0x40-0x7F

Запись данных по этим адресам позволяет создавать 16 своих символов

40

Установка позиции курсора

0x80-0xCF

Курсор устанавливается в позицию согласно табл. 2

40

Время выполнения команд указано приблизительно. Оно определяется частотой внутреннего RC-генератора LCD-дисплея, которая, в свою очередь, зависит от технологического разброса и температуры нагрева корпуса.

Таблица 2 – Команды для перехода на определенное знакоместо верхней или нижней строки экрана для дисплея LCD1602 (HD44780)

Верхняя строка ЖКИ

0x80

0x81

0x82

0x83

0x84

0x85

0x86

0x87

0x88

0x89

0x8A

0x8B

0x8C

0x8D

0x8E

0x8F

0xC0

0xC1

0xC2

0xC3

0xC4

0xC5

0xC6

0xC7

0xC8

0xC9

0xCA

0xCB

0xCC

0xCD

0xCE

0xCF

Нижняя строка ЖКИ

См. также: Подключение жидкокристаллического дисплея LCD1602 (HD44780) к микроконтроллеру ATmega8 в 8-битном режиме

Источник информации: https://www.electronicwings.com/avr-atmega/interfacing-lcd-16x2-in-4-bit-mode-with-atmega-16-32-

Теги:
Хабы:
Всего голосов 6: ↑3 и ↓3+2
Комментарии4

Публикации

Истории

Ближайшие события

27 августа – 7 октября
Премия digital-кейсов «Проксима»
МоскваОнлайн
28 сентября – 5 октября
О! Хакатон
Онлайн
3 – 18 октября
Kokoc Hackathon 2024
Онлайн
10 – 11 октября
HR IT & Team Lead конференция «Битва за IT-таланты»
МоскваОнлайн
25 октября
Конференция по росту продуктов EGC’24
МоскваОнлайн
7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн