Подключение LCD 1602 по I2C интерфейсу

Подключение LCD 1602
LCD 1602 является широко распространенным и популярным дисплеем среди радиолюбителей, кроме этого, аналогичные дисплеи встраиваются в различные устройства серийного производства. Дисплей построен на базе контроллера HD44780 и его аналогах, широкая известность связана с его давним появлением на рынке, во времена стремительного развития электроники. В этой статье я расскажу про сопряжение интерфейса I2C и LCD 1602, с помощью микросхемы расширителя портов PCF8574.

В продаже имеется два типа дисплея, с зеленым экраном и темными символами (заказать можно здесь), а также с синим экраном и светлыми символами (заказать можно тут), как по мне, то светлый зеленый экран смотрится лучше.

Описание и команды управления LCD 1602

Дисплей содержит две строки, в каждой умещается по 16 символов, напряжение питания может находиться в пределах 4,5-5,5 В, ток потребления составляет 1,2 мА без учета подсветки, которая может потреблять значительный ток. Дисплей имеет достаточно много выводов, назначение которых приведено в следующей таблице:

Номер выводаНазваниеОписание
1VssВывод питания дисплея “-”
2VddВывод питания дисплея “+”
3VOВход регулировки контрастности дисплея
4RSВход выбора типа инструкций, 1 – данные, 0 – команда
5R/WВход направления передачи данных, 1 – запись данных в дисплей, 0 – чтение данных из дисплея
6EВход тактирования
7-14DB0-DB7Линии ввода/вывода данных
15AВывод питания подсветки “+”
16KВывод питания подсветки “-”

Вход VO предназначен для регулировки контрастности экрана, которая зависит от величины напряжения на входе, обычно для этих целей устанавливается переменный резистор сопротивлением 10-20 кОм, подключенный к линии питания. С помощью входов RS, R/W выбирается тип инструкций и направление передачи данных. Вход тактирования E предназначен для “защелкивания” (фиксации) состояний входов и линий ввода/вывода, ввод инструкций в контроллер дисплея, а также считывание данных происходит по спаду сигнала (задний фронт). Линии DB7-DB0 представляют собой 8-битный интерфейс ввода/вывода данных, за один период тактового импульса передается 1 байт данных. Выводы A и K представляют собой анод и катод светодиода подсветки, на плате дисплея установлен токоограничивающий резистор.

Ниже в таблице представлены команды для управления LCD 1602:

Описание инструкцииКод инструкцииВремя выполнения
RSR/WDB7DB6DB5DB4DB3DB2DB1DB0
1Очистка дисплея с установкой курсора в начало первой строки00000000011,53 мс
2Установка курсора в начало первой строки0000000011,53 мс
3Установка направления вывода символов, разрешение сдвига экрана00000001I/DSH39 мкс
4Управление режимом питания дисплея и отображением курсора0000001DCB39 мкс
5Команда сдвига курсора и экрана000001S/CR/L39 мкс
6Настройка интерфейса ввода/вывода данных, количества строк для вывода символов, размера шрифта00001DLNF39 мкс
7Запись адреса CGRAM памяти в адресный указатель0001AC5AC4AC3AC2AC1AC039 мкс
8Запись адреса DDRAM памяти в адресный указатель001AC6AC5AC4AC3AC2AC1AC039 мкс
9Команда чтения флага занятости и адресного указателя01BFAC6AC5AC4AC3AC2AC1AC00 мкс
10Запись данных во внутреннюю память дисплея10D7D6D5D4D3D2D1D043 мкс
11Чтение данных из внутренней памяти дисплея11D7D6D5D4D3D2D1D043 мкс

Назначение битов представлено в следующей таблице:

БитЗначениеОписание
I/D0Вывод символов справа-налево, декремент адресного указателя DDRAM/CGRAM памяти
1Вывод символов слева-направо, инкремент адресного указателя DDRAM/CGRAM памяти
SH0Запрет сдвига экрана при выводе символов
1Разрешение сдвига экрана при выводе символов
D0Выключить экран дисплея, сегменты погашены, содержимое внутренней памяти сохраняется
1Включить экран дисплея, нормальный режим работы
C0Отключить отображение курсора
1Включить отображение курсора
B0Отключить функцию мигания курсора
1Включить функцию мигания курсора
S/C0Выбрать курсор для сдвига
1Выбрать экран (вместе с курсором) для сдвига
R/L0Сдвиг влево (только курсор или весь экран, зависит от бита S/C)
1Сдвиг вправо (только курсор или весь экран, зависит от бита S/C)
DL04-битный интерфейс ввода/вывода данных
18-битный интерфейс ввода/вывода данных
N0Использовать одну строку для вывода символов
1Задействовать 2 строки для вывода символов
F0Размер шрифта 5×8 пикселей
1Размер шрифта 5×11 пикселей
BF0Контроллер дисплея готов к обработке новой команды
1Контроллер дисплея занят выполнением внутренних операций

Дисплей можно подключить к микроконтроллеру используя все линии ввода/вывода, то есть через 8-битный интерфейс, но для этого потребуется значительное количество выводов микроконтроллера. Можно сократить количество выводов, если переключиться на 4-битный интерфейс (инструкция №6 из таблицы), в этом режиме задействованы линии ввода/вывода DB7-DB4, линии DB3-DB0 становятся неактивными. В этом случае 1 байт данных передается за два тактовых импульса, сначала старшие 4 бита, затем младшие.

Каждая инструкция выполняется определенное время, которое указано в вышеприведенной таблице, завершение внутренней операции можно узнать с помощью чтения флага занятости BF (инструкция №9). Но обычно флаг не опрашивают, а просто выдерживают соответствующую паузу после передачи инструкции. Вообще чтение данных из дисплея применяется редко, и не представляет особого интереса.

Внутренняя память LCD 1602 подразделяется на 3 вида: DDRAM, CGROM и CGRAM. Область DDRAM (Display Data RAM) памяти используется для хранения 8-битного кода ASCII символов, отображаемых на экране. Адреса регистров DDRAM памяти связаны с положением символов на экране, соответствие приведено в следующей таблице:

Адреса ячеек
1-я строка000102030405060708090A0B0C0D0E0F
2-я строка404142434445464748494A4B4C4D4E4F

На каждой строке экрана умещается 16 символов, но это только видимая область, общий объем DDRAM памяти составляет 80 байт, то есть в каждую строку можно записать по 40 символов, и только 16 из них будут отображаться на экране, остальные символы останутся в невидимой области. С помощью команды сдвига экрана (инструкция №5) можно просмотреть все остальные символы, наподобие эффекта бегущей строки. Регистры с адресами 0x00-0x27 составляют первую строку, ячейки 0x40-0x67 вторую строку. Если задать только одну строку для вывода (бит N=0 в инструкции №6, при этом будет использована 1-я строка), то длина строки увеличиться, и будет вмещать 80 символов.

Память CGROM (Character Generator ROM) представляет собой знакогенератор и содержит данные для прорисовки ASCII символов. В памяти заложены спецзнаки, цифры, латинский алфавит, если не ошибаюсь, имеются китайские иероглифы, а также символы греческого алфавита, которые используются для обозначения физических величин. Каждый символ занимает 5 байт в памяти, что соответствует размеру шрифта 5×8 пикселей. На следующей картинке представлена таблица символов в соответствии с ASCII кодами:
LCD 1602 таблица символов
Кириллица в LCD 1602 не заложена, что не удивительно, ведь это китайский дисплей, но существуют аналогичные отечественные дисплеи фирмы МЭЛТ с поддержкой русского алфавита.

Для вывода символа необходимо записать адрес регистра DDRAM памяти в адресный указатель (инструкция №8), тем самым выбрав положение символа на экране, затем записать в выбранный регистр код ASCII символа (инструкция №10), исходя из полученного кода, контроллер дисплея извлекает данные из CGROM памяти для прорисовки символа в заданном положении на экране. После вывода символа, адресный указатель автоматически инкрементируется или декрементируется, в зависимости от того, какое значение было задано биту направления I/D. Таким образом, символы можно выводить последовательно, при этом корректировка адреса DDRAM памяти не требуется. Например, если первая строка полностью заполнится символами, то произойдет переход на вторую строку, и наоборот.

Энергонезависимая память CGRAM (Character Generator RAM) предназначена для создания уникальных символов под нужды разработчика. Объем памяти небольшой, и позволяет хранить 8 произвольных символов. Для создания одного символа размером 5×8 пикселей, необходимо передать 8 байт данных в регистры памяти. На картинке ниже изображена структура CGRAM памяти и пример расположения символов:
LCD 1602 CGRAM память
Перед созданием символа необходимо записать адрес регистра CGRAM памяти в адресный указатель (инструкция №8), далее передать байты данных, которые составят изображение символа (инструкция №10), адресный указатель автоматически инкрементируется/декрементируется (в зависимости от бита I/D), как и в случае DDRAM памяти. В создании символа участвуют только 5 младших битов байта данных. Символам присваиваются коды 0x00-0x07 в соответствии с их расположением в памяти CGRAM, они отображены в ранее приведенной таблице символов.

Инициализация LCD 1602

После подачи питания необходимо провести инициализацию дисплея для корректного функционирования, в этой статье я не стану рассматривать 8-битный интерфейс передачи данных, так как все операции с LCD 1602 по I2C интерфейсу выполняются в 4-битном режиме. Порядок инструкций для инициализации представлен на следующей картинке:
Инициализация LCD 1602
После инициализации дисплей настроен на использование 2-х строк, экран очищен от мусора, курсор установлен в начало первой строки (адрес DDRAM памяти 0x00), отображение курсора отключено, выбран шрифт 5×8 пикселей, направление вывода символов слева-направо, сдвиг экрана при выводе символов запрещен.

Если разрешить сдвиг экрана при выводе символов (бит SH=1), то с каждым новым символом экран будет сдвигаться в выбранном направлении, то есть новые символы будут появляться в заданном положении на экране, а остальные сдвигаться.

Дополнительно на экране можно включить отображение курсора (бит C=1), который выглядит в виде линии подчеркивания, и может мигать в зависимости от настроек. Положение курсора на экране соответствует текущему адресу DDRAM памяти в адресном указателе. Курсор можно сдвигать по экрану (инструкция №5), при этом адресный указатель будет инкрементироваться/декрементироваться в зависимости от направления сдвига.

Подключение LCD 1602 через I2C интерфейс

Специально для дисплея, в продаже имеется модуль-переходник на основе микросхемы PCF8574AT, которая предназначена для расширения количества линий ввода/вывода. Микросхема подключается по I2C интерфейсу и имеет порт из 8 линий ввода/вывода, принцип функционирования простой, при записи байта данных в микросхему, линии порта принимают уровни, соответствующие значениям битов полученного байта. Операция чтения возвращает байт данных, биты которого указывают состояние линий порта. Таким образом, микросхема позволяет расширить количество линий ввода/вывода, используя два управляющих провода. Фото модуля представлено ниже:
Модуль PCF8574 I2C
Заказать модуль можно тут. На модуле установлен подстроечный резистор для регулировки контрастности экрана, также имеется джампер (перемычка), разрешающий управление подсветкой дисплея, о котором будет сказано позже.

На следующей картинке приведена схема подключения LCD 1602 и I2C модуля:
Подключение LCD 1602 по I2C интерфейсу
Адрес микросхемы PCF8574AT на шине I2C можно настраивать, старшие 4 бита адреса фиксированы, и равны 0111, младшие 3 бита зависят от состояния входов микросхемы A2-A0. На модуле данные входы подтянуты к высокому уровню, соответственно адрес микросхемы принимает значение 0111111.

Как видно из схемы, к микросхеме подключена только часть линий ввода/вывода дисплея DB7-DB4, это означает, что управление дисплеем возможно только через 4-битный интерфейс. Для ввода инструкции в дисплей требуется 2 тактовых импульса на линии E, то есть последовательность уровней 1010 (“защелкивание” данных происходит по спаду уровня), в итоге необходимо записать в микросхему 4 байта для одной инструкции. На картинке ниже приведен пример записи инструкции в дисплей по интерфейсу I2C:
Передача данных LCD 1602 через PCF8574
Сначала передается старший полубайт инструкции с битом E=1, затем то же самое с битом E=0, при этом в дисплей передается первая половина инструкции. Далее таким же образом передается вторая половина (младший полубайт).

Для управления подсветкой дисплея, на плате модуля установлен транзистор, подключенный к линии P3 микросхемы. Таким образом, 3-й бит в байте данных управляет подсветкой, 0 – выключена, 1 – включена. При отсутствии джампера (о котором я писал выше) подсветка отключается, независимо от бита управления.

Я подключил дисплей с модулем к микроконтроллеру PIC16F628A, схема представлена ниже:
Схема подключения LCD 1602 по интерфейсу I2C
Кнопка SB1 управляет подсветкой дисплея, светодиод HL1 загорается в случае ошибки передачи данных. Неполный код программы приведен ниже (полную версию можно скачать в конце статьи):

Микроконтроллер настроен на внутренний тактовый генератор с частотой 4 МГц. После настройки внутренних регистров, выполняется включение подсветки дисплея, после чего вызывается подпрограмма инициализации дисплея, порядок инструкций для инициализации я приводил выше. Далее в первой строке выводится надпись “Radiolaba.ru”, на второй строке “LCD 1602A”, затем выполняется сдвиг экрана влево на 16 позиций, то есть для отображения выбирается свободная область DDRAM памяти. Здесь в продолжение первой строки выводится надпись “PCF8574AT”, и для второй строки “I2C bus”. Далее выполняется круговой цикл, где экран поочередно сдвигается на 16 позиций вправо и влево. Таким образом, можно просмотреть все символы записанные в DDRAM память. Одновременно в цикле выполняется опрос кнопки подсветки SB1.

Для установки адреса DDRAM памяти (позиция на экране) вызывается подпрограмма lcd_adr, для записи данных в дисплей – подпрограмма lcd_dat. Подпрограмма lcd_comm используется для записи команды, для установки адреса CGRAM памяти – lcd_cgram. Включение и выключение подсветки – подпрограммы lcd_led_on и lcd_led_off.

Инструкции очистки дисплея и возврата курсора выполняются значительно дольше остальных, поэтому, после передачи этих инструкций в дисплей, в коде выполняется пауза на 2 мс.

У дисплея имеются некоторые особенности, при смене количества используемых строк, меняется контрастность экрана, то есть она не одинакова для однострочного и двухстрочного режима. Шрифт 5×10 в дисплее не заложен, хотя команда настройки размера имеется, судя по матрице, она физически не предназначена для такого шрифта.
LCD 1602 макетная плата

Заказать комплектующие можно по ссылкам ниже:
Дисплей LCD 1602
Дисплей LCD 2004
Модуль I2C PCF8574

На следующем видеоролике представлен пример вывода символов на дисплей:

Прошивка МК и исходник для подключения LCD 1602

У этой записи 34 комментариев

  1. Непонятно, правда, зачем єто нужно?
    Разве что когда нехватает выводов микроконтроллера… А не будет ли дешевле взять контроллерс большим количеством пинов?

  2. Эта статья заслуживает уважения. Очень подробно всё расписано. Особенно пример отправки одной команды по I2C. Спасибо автору за труд. Обожаю когда с виду сложные вещи объясняют на простым языком. Нет ничего сложного для понимания микроконтроллеров с такими статьями.

  3. В коде есть пару ошибок, думаю не случайных. А так код , после 2х часов плясок с бубном, ожил. Вообще-то чтобы использовать I2C для связи с дисплеем нужна какая-то крайняя необходимость. Я в простых схемах использую регистр 74HC595 (когда нет необходимости читать дисплей). да и код более оптимальный.

    1. Приветствую! Что за ошибки вы нашли в коде? если не сложно укажите на них. У меня все работает без ошибок, что и продемонстрировано на видео.

      1. >Что за ошибки вы нашли в коде?

        В таблице, команда 2, написано “Установка курсора в начало первой строки”. Но это инициализация, а не просто перевод курсора.

    2. Насчет нужды в применении I2C – как раз все очень понятно. Если свободных портов хватает, никто и не будет мудрить. А при нехватке – переходить на PIC16F873 (или похожее) вместо PIC16F628 уж совсем не хочется. Два порта вместо шести (или три вместо семи) – это существенно! Громила типа 16F873 имеет на 10 выводов больше. Тем более, если I2C требуется для связи с другими устройствами, выигрыш очевиден. Понятно, если размер устройства значения не имеет, можно и PIC16F877 применить, там портов много.

  4. Не нашел в коде где устанавливаются биты PORTB,0 и PORT,1 из-за этого собственно и не было передачи данных пришлось добавить в этом куске кода:
    sda_1 bsf sda ; ПРИШЛОСЬ ДОБАВИТЬ ИНАЧЕ ВЫВОД PORTB,0 НЕ ;УСТАНАВЛИВАЕТСЯ В 1
    bsf STATUS,RP0 ;перенастройка линии sda на вход
    bsf sda_io ;
    bcf STATUS,RP0 ;
    return ;возврат из подпрограммы

    sda_0 bcf sda ;ТО-ЖЕ ДОБАВЛЕНО ДЛЯ УСТАНОВЛЕНИЯ PORTB,O В 0
    bsf STATUS,RP0 ;
    bcf sda_io ;
    bcf STATUS,RP0 ;
    return ;возврат из подпрограммы

    scl_1 bsf scl ; ТО-ЖЕ ДЛЯ УСТАНОВКИ PORTB,1 В 1
    bsf STATUS,RP0 ;перенастройка линии scl на вход
    bsf scl_io ;
    bcf STATUS,RP0 ;
    return ;возврат из подпрограммы

    scl_0 bcf scl ; ТО-ЖЕ ДЛЯ УСТАНОВКИ PORTB,1 В 0
    bsf STATUS,RP0 ;
    bcf scl_io ;
    bcf STATUS,RP0 ;
    return ;возврат из подпрограммы

    1. так-же пришлось добавить в начале подтяжку вн.резисторов PORT,B(bit 7 RBPU: PORTB Pull-up Enable bit
      1 = PORTB pull-ups are disabled
      0 = PORTB pull-ups are enabled by individual port latch values):
      BANKSEL TRISA
      MOVLW B’01111111′
      MOVWF OPTION_REG
      BANKSEL PORTA

      1. Насчет подтягивающих резисторов, они уже имеются на плате переходнике I2C PCF8574AT. Поэтому я ничего не подтягивал, ни программно, ни физически.

        1. ААА, я сначала в Proteuse тестил, потом на макетке собрал. В Proteuse пока подтяжку не включил вообще ничего не шло!

      2. с этим понятно, но вот Ваш код:
        bsf STATUS,RP0 ;
        bcf sda_io ;
        bcf STATUS,RP0 ;
        собственно он и должен установить PORTB,0, а бит sda_io, я так понял относится к регистру TRISB, для изменения вход-выход. А где устанавливается бит sda?

        1. Просто вы не до конца изучили протокол I2C, нельзя на линиях устанавливать лог.1 ! Линии должны быть подтянуты к + питания, а ведущий и ведомый должны иметь выходы с открытым стоком или коллектором. Чтобы выдать лог.0 на кто то из них притягивает линию к 0, а чтобы выдать лог.1 соответственно отпускают линию, отсюда в коде и подпрограммы которые меняют направление линии.

          Например линию SCL может контролировать и ведомый, он может ее удерживатьв 0, если занят. А если в этот момент ведущий установит линию в 1, то произойдет замыкание! Можете почитать про I2C у меня на сайте https://radiolaba.ru/microcotrollers/i2c-interfeys.html

          1. тут Вы меня ставите в тупик… т.е. команды установки битов PORTB,0 и 1 не нужны? и передача данных . точнее выставление на шину 0 и 1 осуществляется за счет изменения направления регистра TRIS? потому что в этой части кода:
            bsf STATUS,RP0 ;
            bcf sda_io ;
            bcf STATUS,RP0 ;
            изменяется только направление бита 0 PORTB на вход-выход

            1. нужна только команда сброса соответствующего бита PORTB. Чтобы установить лог.0 на линии, нужно предварительно сбросить бит линии в 0 (например bcf sda (что равнозначно bcf PORTB,0)), и перенастроить ее на выход (bcf sda_io (что равнозначно bcf TRISB,0)), чтобы установить лог.1 нужно просто перенастроить линию на вход (bsf sda_io (что равнозначно bsf TRISB,0)), вот и все.

    2. Так это же специально сделано, чтобы можно было любой удобный вывод назначить под это дело.
      Все это задается в начале программы:
      #DEFINE sda PORTB,0 ;линия sda
      #DEFINE scl PORTB,1 ;линия scl
      #DEFINE sda_io TRISB,0 ;линия направления sda
      #DEFINE scl_io TRISB,1 ;линия направления scl

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

  5. и после вывода второй части текста на дисплее оставалось часть первого сообщения, пришлось добавить между ними:
    movlw b’00000001′ ;ОЧИСТКА
    call lcd_comm

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

      1. у меня сообщения выходят без сдвига. просто сначала одно потом через 0,5с второе, думаю связано с дисплеем

  6. “… ведь это китайский дисплей”
    Японский.

    “… имеются китайские иероглифы”
    Это японские. Китайские встречаются в некоторых нестандартных вариантах кодовых страниц.

    “… длина строки увеличиться”
    Ну вот как так можно, а?!

  7. Добрый день.

    Большой проект Color and Code версии 19. Определение элементов по цвету, коду, справочники, включая Arduino.

    Есть встроенный калькулятор LCD1602 символов, генерация программной строки….

    Может кому пригодится https://colorandcode.su

  8. R/W – определяет что будем делать, считывать(R/W=1) или записывать(R/W=0)
    Исправьте ошибку, долго не мог найти, что не так

  9. Спасибо, все работает, легко переделал под PIC16F877, были проблемы, но из-за того, что у меня не такие чуть-чуть модули, у меня в них микросхема PCF8574T, а по даташиту у неё другой адрес, после включения дисплей не инициализировался, горела подсветка, на кнопку не реагировал, после замены адреса микросхемы в программе на “01001110”, все заработало идеально как на видео.

  10. Здравствуйте!
    Хотел бы спросить: “Регистры с адресами 0x00-0x27 составляют первую строку, ячейки 0x40-0x67 вторую строку”, является ли “0x40-0x67” опечаткой? Разве не эти адреса 0х28-0х4F составляют вторую строку?

    1. Это не опечатка, все правильно

  11. Привет. Уважаемый автор, ваш код я переделал под PIC16F648A и управляю часами на DS3231. Всё прекрасно работает, но я так и не понял из даташита, возможно ли во время работы устройства, программно переключать PCF8574 линии P4-P7 на вход, дабы считывать флаг BF и счётчик адреса. Ведь в вашем коде нет опроса этого флага. В даташите только при инициализации, разово можно установить часть пинов на вход, а часть на выход.

  12. Здравствуйте. Я пока учусь программированию, и хотел попросить программу под PCF8574T, PIC16F628А Надо менять адрес, но у меня не получается. Очень прошу выложить прошивку под PCF8574T, я попробую сравнить с прошивкой под PCF8574АT, тогда до меня быстрее дойдет. Спасибо .

  13. Я не пойму ни как… А где строб в инициализации? Где он вообще? ауу…

  14. И зачем в инициализации 4 битного режима нужна8 битная инструкция? Я сразу начал с инициализации 4бит и все нормально

    1. у меня так же, по-моему 8-битная инструкция и не выполняется

  15. Спасибо, пока из Китая едет модуль, буду вникать в Ваш код. В Протеусе работает.

  16. Огромная благодарность за предоставленный материал!
    Именно отсюда вразумился как дисплей управляется по (4+3)-проводной шине. Тем более, что первый полученный дисплей с интерфейсом I2C имел неисправную интерфейсную плату.
    Программировал на ассемблере для AVR.
    Спасибо!!!
    Виктор Парамонов. viktor-paramon@yandex.ru

  17. Спасибо! Всё хорошо работает на pic12f629, с заменой адреса на “01001110”

  18. Почему работа идёт в 4-битном режиме, хотя и дисплей и PCF8574 поддерживают 8 битный режим? Сборка LCD+PCF выглядит так, будто распаяны все 8 пинов данных.

    1. Похоже, 4 бит хотя бы из-за того что физически не хватает ног на PCF8574. Спасибо за статью, помогло запустить проект!

Имя (обязательно)Email (обязательно)Веб-сайт

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