Примечание:
Статья не является оригинальным переводом. Статья созданы на базе статьи (см. источник информации указанный ниже) путем его перевода, использования основного текста оригинала и дополнена автором; данный код протестирован на реальном устройстве ф. Winstar.
LCD-дисплеи (Liquid Crystal Displays) используют для отображения состояния или параметров в различных приборах.
LCD1602 – это 16-выводное устройство, имеющее 8 выводов для передачи данных (D0-D7) и 3 вывода управления (RS, RW, EN). Остальные 5 выводов предназначены для питания и подсветки ЖК-дисплея. Цифры «1602» указывают на формат выводимой (отображаемой) информации: 16x02 символов (рисунок 1).
Выводы управления помогают нам настроить LCD-дисплей в командном режиме или режиме передачи данных. Они также помогают настроить режим чтения или записи, а также время чтения или записи.
LCD-дисплей 16x2 можно использовать в 4-битном или 8-битном режиме в зависимости от технических требований. Чтобы использовать его, нам необходимо отправить определенные команды на LCD-дисплей в командном режиме, и как только ЖК-дисплей будет настроен в соответствии с нашими требованиями, мы сможем отправить необходимые данные в режиме передачи данных.
В 4-битном режиме данные/команды отправляются в 4-битном (полубайтном) формате.
Для этого сначала необходимо отправить «старшие» (верхние) 4 бита, а затем отправить «младшие» (нижние) 4 бита данных/команд.
Только 4 выводы данных (D4 - D7) LCD-дисплея 16x02 подключены к микроконтроллеру, а другие управляющие выводы RS (выбор регистра), RW (чтение/запись), E (сигнал разрешения) подключены к другим GPIO выводам микроконтроллера (рисунок 2).
Таким образом, благодаря такому подключению мы можем сэкономить четыре GPIO вывода микроконтроллера, которые можно использовать для других целей.
Подстрочный резистор 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.
Расшифровка наиболее употребляемых команд, посылаемых от микроконтроллера в дисплей LCD1602 (HD44780) приведена в таблицах 1, 2.
Время выполнения команд указано приблизительно. Оно определяется частотой внутреннего RC-генератора LCD-дисплея, которая, в свою очередь, зависит от технологического разброса и температуры нагрева корпуса.
Источник информации:
https://www.electronicwings.com/avr-atmega/interfacing-lcd-16x2-in-4-bit-mode-with-atmega-16-32-
Комментарии (4)
DrGluck07
04.04.2023 21:01+3Передача каждой команды занимает минимум 200 микросекунд, really? Проверьте свои тайминги, это не должно работать так медленно и печально. Насколько я помню, там вообще нет таймингов больше 1 микросекунды и все тайминги даны в наносекундах. Это мы так будем одну строку рисовать несколько миллисекунд, а весь остальной контроллер будет стоять и ждать (в смысле всё, что работает в фоновом цикле).
DrGluck07
04.04.2023 21:01+2Дополнение, потому что слона-то я и не заметил. Задержка 2-3 миллисекунды между символами?
Rusrst
Это мэлтовский что ли индикатор? Если да, то у него система команд таки слегка отличается. Я уже за давностью лет не помню в чем там соль.
FranzDev Автор
Winstar на МЭЛТ не тестировалось