У друга из радиокружка я однажды выпросил второй LCD 16x2 — без модуля I2C, просто стеклянный бутерброд с шестнадцатью ножками снизу. Пришлось вспоминать классическую схему: шесть сигнальных проводов, питание, земля и отдельный потенциометр на контраст. Тогда я был уверен, что это шаг назад, — и ошибся. Параллельное подключение до сих пор живёт не по инерции: у него есть сценарии, где оно объективно лучше шины I2C, и умение развести дисплей «по-старому» не раз выручало меня в проектах, где каждый пин на счету, а шина уже занята другими устройствами.
В этом уроке разберём 4-битный режим контроллера HD44780: что делают линии RS и E, почему линию RW прижимают к земле и как шесть проводов заменяют десять. Соберём стандартную схему из примеров Arduino IDE — LiquidCrystal(12, 11, 5, 4, 3, 2) — и убедимся, что в симуляторе экран ведёт себя в точности как I2C-вариант из прошлого урока: меняются провода, а не язык рисования.
Как дисплей принимает команды: RS, E и четыре линии данных
Контроллер HD44780 умеет общаться двумя способами. В 8-битном режиме байт команды передаётся целиком по восьми линиям данных — плюс управляющие, итого десять проводов. В 4-битном режиме байт режется на две половинки по четыре бита — старшую и младшую, — и линий данных нужно всего четыре. Плата лишнего такта на передачу почти не замечает: экран и так успевает обновляться десятки раз в секунду, а человеческий глаз больших скоростей от символьного дисплея и не ждёт. Поэтому библиотеки исторически используют именно 4-битный режим — экономия проводов дороже потерянных наносекунд.
Теперь управляющие линии, их всего две с половиной. RS (register select) выбирает регистр: низкий уровень значит «сейчас пойдёт команда», высокий — «сейчас пойдёт символ для отрисовки». E (enable) — строб: по фронту импульса на этой линии контроллер читает данные. RW (read/write) задаёт направление обмена, но дисплею почти никогда не нужно ничего читать, поэтому RW сажают на землю навсегда — шина становится линией только записи. Линии D4–D7 несут сами данные, а D0–D3 остаются неподключёнными. Итого RS, E, D4, D5, D6, D7 — те самые шесть сигнальных проводов из заголовка.
И принципиальное отличие от I2C-варианта: контраст. На модуле с переходником подстроечник уже распаян, а «голому» дисплею нужен внешний потенциометр на 10 кОм между 5 В и землёй, движок — на вывод V0. Без него вы увидите либо пустоту, либо залитые квадраты, потому что контраст — это физическое напряжение, а не программная настройка, и никакой код его не заменит.
| Вывод LCD | Куда подключить на Uno | Зачем |
|---|---|---|
| VSS | GND | земля |
| VDD | 5V | питание |
| V0 | движок потенциометра 10 кОм | контраст — без него экран пустой |
| RS | пин 12 | команда или символ |
| RW | GND | всегда запись |
| E | пин 11 | строб |
| D4, D5, D6, D7 | пины 5, 4, 3, 2 | данные, 4 бита |
| A, K | 5V и GND | подсветка, часто уже разведена на модуле |
#include <LiquidCrystal.h> // RS, E, D4, D5, D6, D7 — именно в этом порядкеLiquidCrystal lcd(12, 11, 5, 4, 3, 2); void setup() { lcd.begin(16, 2); // у I2C-версии эту роль играл lcd.init() lcd.setCursor(0, 0); lcd.print("Parallel mode"); lcd.setCursor(0, 1); lcd.print("6 provodov");} void loop() {}Конструктор принимает номера пинов в строгом порядке: RS, E, D4, D5, D6, D7. Перепутаете пару линий — экран молчит или рисует мусор, и это нормальная часть пути: ни одну свою первую параллельную схему я не собрал без одной перепутанной ножки. lcd.begin(16, 2) инициализирует контроллер и сообщает библиотеке размер экрана — аналог пары из lcd.init() и размеров в конструкторе у I2C-версии. Дальше начинается приятное: setCursor(), print() и работа с курсором совпадают до буквы. Библиотеки разные, а язык рисования общий, потому что обе в итоге переводят одни и те же команды HD44780.
Из этого следует практический вывод, который экономит кучу сил: логику скетча можно писать, вообще не думая об интерфейсе. Термометр из прошлого урока переезжает с I2C на параллельную схему заменой трёх строк — подключение, конструктор, инициализация. Всё, что касается позиций, затирания пробелами и обновления значений, остаётся как было.
Любопытно заглянуть под капот передачи. Библиотека на каждый байт сначала выставляет старшую четвёрку бит на D4–D7 и дёргает линию E, потом младшую — и дёргает E ещё раз. RS при этом держится в нужном состоянии: низко для команды, высоко для символа. На этой механике построены обе библиотеки раздела, и если когда-нибудь придётся работать с дисплеем без библиотеки вовсе — например, на экзотическом процессоре, — вы будете знать, что именно мигать линиями и в каком порядке.
Когда I2C не нужен
Если пинов хватает и шина свободна, я, как и большинство, ставлю модуль I2C: два провода вместо шести, меньше пайки, макетку пересобрал за минуту. Но три сценария заставляют меня брать параллельную схему. Первый: шина I2C уже занята часами, датчиками, памятью — каждый новый адрес это риск конфликта, а дисплей на своих шести пинах никого не трогает. Второй: скорость. Параллельный интерфейс обновляет экран заметно быстрее, и если вы рисуете бегущую строку или анимацию, разница видна глазом. Третий — житейский: в ящике у меня лежит горсть дешёвых дисплеев без переходников; подключить их напрямую проще и дешевле, чем паять модуль.
Отдельно про 20x4, потому что запрос «дисплей 20x4 подключение» звучит постоянно. Это та же семья HD44780: четыре строки по двадцать символов, подключение идентичное, меняются только begin(20, 4) и размеры в конструкторе I2C-версии. Потенциометр контраста всё так же обязателен, адреса переходников — те же 0x27 и 0x3F, строки нумеруются с нуля до трёх. Купите такой экран — этот абзац будет единственной разницей.
Эксперименты
- Перепишите скетч под параллельную схему: замените подключение библиотеки, конструктор и init() на begin(16, 2). Остальной код должен остаться без единой правки — это и есть суть совместимости двух библиотек.
- Выведите строку из 25 символов и посмотрите, куда уедет хвост: на экране 16x2 он перетекает на вторую строку. Строка длиннее 32 символов исчезнет безвозвратно.
- Поменяйте в конструкторе местами два пина — например, RS и E — и запустите. Симулятор от несуществующей проводки не страдает, но именно это вы увидели бы на железе: пустой экран при живой подсветке.
- Возьмите термометр-таймер из прошлого урока и переведите его на параллельный интерфейс: изменятся три строки кода, поведение прибора — ни на бит.
- 4-битный режим HD44780: 6 сигнальных проводов — RS, E, D4–D7; D0–D3 не подключаются, RW садится на GND
- LiquidCrystal lcd(12, 11, 5, 4, 3, 2) — порядок: RS, E, D4, D5, D6, D7; lcd.begin(16, 2) вместо lcd.init()
- setCursor() и print() у LiquidCrystal и LiquidCrystal_I2C совпадают — меняется только нижний слой интерфейса
- Голому дисплею нужен потенциометр 10 кОм на V0: контраст — это напряжение, кодом его не задать
- Параллельный интерфейс берут, когда шина I2C занята, нужна скорость обновления или дисплей без переходника
- Дисплей 20x4 подключается так же: begin(20, 4), адреса переходников прежние — 0x27 и 0x3F
- 1. Сколько сигнальных проводов занимает LCD в 4-битном режиме, без питания и подсветки?
- 2. Что делает линия RS?
- 3. Куда подключают линию RW в любительских схемах?
- 4. Чем lcd.begin(16, 2) отличается от lcd.init()?
- 5. В каком случае параллельное подключение объективно лучше I2C?
- 6. Верхняя строка LCD залита квадратами, нижняя пустая. Первый подозреваемый?
Теперь оба интерфейса у вас в руках: I2C для быстрых сборок и параллельная классика для плотных проектов. Заметьте, как мало изменилось между уроками: провода и две строки инициализации — вот и вся разница, потому что оба пути упираются в один и тот же контроллер сорокалетней давности. Дальше по плану самое интересное: берём дисплей под полный контроль — меню из трёх пунктов, курсор-стрелка и обновление значений без единого мерцания.
Частые вопросы по теме урока
- Сколько сигнальных проводов занимает LCD в 4-битном режиме, без питания и подсветки?
- RS, E и четыре линии данных D4–D7 — итого шесть. Четыре провода — это про I2C-модуль, десять — про 8-битный режим. Шесть
- Что делает линия RS?
- RS — register select: низкий уровень означает команду, высокий — символ. Остальные функции выполняют V0, A/K и E. Различает команды и символы для отрисовки
- Куда подключают линию RW в любительских схемах?
- Дисплей только слушает команды, читать из него нечего: RW прижимают к земле, превращая шину в линию записи. К GND
- Чем lcd.begin(16, 2) отличается от lcd.init()?
- У I2C-версии размер экрана указан в конструкторе, поэтому хватало init(). Классическая LiquidCrystal узнаёт размер из begin(). begin() задаёт размер экрана и пришёл из библиотеки LiquidCrystal, init() — из I2C-версии
- В каком случае параллельное подключение объективно лучше I2C?
- Параллельная схема не занимает шину и работает быстрее. Дефицит пинов — наоборот, аргумент за I2C. Когда шина I2C уже занята другими устройствами или нужна скорость обновления экрана
- Верхняя строка LCD залита квадратами, нижняя пустая. Первый подозреваемый?
- Залитая верхняя строка при пустой нижней — классический признак несостоявшейся инициализации. Проверьте порядок RS и E в конструкторе. Инициализация: перепутаны RS и E