Первый LCD 16x2 пролежал у меня без дела целый месяц. Я воткнул его по фотографии из интернета, загрузил код из той же статьи, включил питание — и увидел два ряда аккуратных синих квадратов вместо текста. Провода сидели плотно, скетч был скопирован верно, плата мигала своим светодиодом, а дисплей упорно показывал «кирпичи». Спасла одна отвёртка: синий подстроечный резистор на модуле, который я крутил в самую последнюю очередь, — он задаёт контраст, и из-под квадратов проступили буквы. Об этом тупике ниже будет отдельный разговор, а пока запомните главное: если на экране квадраты, дисплей, скорее всего, жив, и чинится он за пятнадцать секунд.
В этом уроке мы подключим символьный LCD к Arduino Uno по шине I2C — всего четыре провода, без паутины из сигнальных линий. Напишем первый скетч с библиотекой LiquidCrystal_I2C, поймём, зачем нужны lcd.init() и lcd.backlight(), как работают setCursor() и print(), а в конце соберём в симуляторе маленький прибор: термометр с таймером на экране. Пятнадцать минут на всё про всё — и это без обмана: первый текст появляется на дисплее минут через пять после вскрытия пакета.
Что у вас на столе: HD44780 и модуль I2C
Практически каждый символьный LCD из наборов — потомок контроллера HD44780 от Hitachi, выпущенного ещё в восьмидесятых годах прошлого века. Схема оказалась настолько удачной, что десятилетия спустя дисплеи «16 символов в 2 строках» работают по тем же командам, а libraries для них называются почти одинаково. Каждый знак рисуется на сетке 5 на 8 точек — поэтому буквы такие угловатые, — экран питается от 5 В, и сама стеклянная панель ничего не знает ни про USB, ни про I2C: она ждёт команды по параллельной шине данных.
Чтобы не занимать шесть пинов Arduino, на плату дисплея с завода напаивают переходник с чипом PCF8574 — крошечным расширителем портов, который переводит обмен на шину I2C: две линии данных вместо шести сигнальных. И здесь деталь, о которой молчит половина инструкций: у переходников на чипе PCF8574 адрес по шине — 0x27, а у версии на PCF8574A — 0x3F. Внешне модули не отличить: те же четыре отверстия, та же надпись. Если скетч с адресом 0x27 молчит, просто поменяйте число на 0x3F — в половине случаев «дисплей не работает» решается именно так.
У Uno шина I2C сидит на фиксированных пинах: A4 — это SDA, A5 — это SCL. Питание берём с 5V и GND, дополнительных резисторов и обвязки не нужно — подтяжки уже стоят на модуле. Четыре провода, и можно писать код.
| Вывод модуля LCD | Куда подключить на Uno | Что это |
|---|---|---|
| VCC | 5V | питание — дисплей ждёт именно 5 В |
| GND | GND | общий провод, минус |
| SDA | A4 | линия данных I2C |
| SCL | A5 | линия тактирования I2C |
#include <Wire.h>#include <LiquidCrystal_I2C.h> // адрес 0x27 (чаще всего) или 0x3F, экран 16 символов на 2 строкахLiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { lcd.init(); // инициализация дисплея lcd.backlight(); // включаем подсветку lcd.setCursor(0, 0); // столбец 0, строка 0 — левый верхний угол lcd.print("Privet, Arduino!"); lcd.setCursor(0, 1); // начало второй строки lcd.print("LCD 16x2 po I2C");} void loop() { // здесь пока пусто: текст уже нарисован и никуда не денется}Разберём три строки, без которых экран молчит. lcd.init() отправляет контроллеру стартовую последовательность: без неё дисплей включён, но не настроен — чаще всего он показывает пустую первую строку с тусклыми квадратами или вообще ничего. lcd.backlight() зажигает подсветку; на некоторых моделях она горит и без этой команды, но полагаться на везение я перестал после третьего «почему у меня экран тёмный». lcd.setCursor(столбец, строка) перемещает невидимый курсор: нумерация с нуля, левый верхний угол — setCursor(0, 0), правый нижний — setCursor(15, 1). Каждый следующий print() пишет текст с текущей позиции и сам сдвигает курсор вправо.
У print() есть удобный побочный талант: он печатает не только строки в кавычках, но и числа. lcd.print(25) выведет «25», lcd.print(temp) — значение переменной. Это и делает LCD рабочим инструментом: температуру, счётчик, напряжение можно вывести одной командой без ручных пересчётов.
Теперь о диапазонах, потому что здесь спотыкаются все. В экране 16x2 столбцы нумеруются от 0 до 15, строки — от 0 до 1. Строка длиннее шестнадцати символов не вызовет ошибку: текст просто перетечёт на вторую строку или уйдёт за край, и выглядеть это будет как поломка, хотя код честно сделал то, что вы попросили.
Термометр с таймером: собираем прибор в симуляторе
Печатать текст ради текста скучно, поэтому соберём настоящий прибор: на верхней строке — температура, на нижней — сколько секунд работает плата. В симуляторе к пину A0 подключена ручка потенциометра: крутишь её — меняется напряжение от 0 до 5 В, а analogRead(A0) превращает его в число от 0 до 1023. Это десятибитный аналого-цифровой преобразователь Uno: два в десятой степени минус один и есть 1023. Функция map() пересчитывает этот диапазон в понятные градусы, скажем от -20 до +50. Таймер считаем из millis() — количества миллисекунд с момента включения, делённого на тысячу.
Отдельно про кириллицу, чтобы вы не поймали ту же граблю, что и я. У классического HD44780 в постоянной памяти зашита таблица символов без русских букв, поэтому на настоящем железе lcd.print("Привет") рисует кракозябры. Есть модули с «русским» знакогенератором, но рассчитывать на них нельзя — привычка писать экранные тексты латиницей экономит нервы. Симулятор этого ограничения не повторяет: он рисует сетку 16 на 2 и согласен на любой язык, так что для виртуальных экспериментов пишите как удобно.
В этом скетче появился приём, который вы будете тащить за собой в каждый дисплейный проект: затирание пробелами. Представьте, что температура была 5 градусов, а потом подскочила до 25. Без пробелов на экране останется «Temp: 255» — последняя цифра никуда не делась, потому что никто её не перезаписал. Хвост из нескольких пробелов после числа решает проблему радикально: пробел — такой же символ, как буква, он честно рисуется поверх старого и делает его невидимым.
Есть и второй путь — lcd.clear() перед каждой отрисовкой. Он стирает весь экран целиком, и если вызывать его десятки раз в секунду, дисплей начинает заметно мерцать: гаснет, рисуется заново, гаснет. Выглядит это дёшево, как вывеска ночного ларька. В уроке про меню мы доведём идею точечного обновления до конца: научимся перерисовывать только те ячейки, которые реально изменились.
Эксперименты
- Поменяйте адрес в конструкторе на 0x3F и запустите снова. В симуляторе экран останется тем же, но привычка проверять второй адрес сэкономит вам вечер с реальным модулем на PCF8574A.
- Сместите надписи к центру: setCursor(3, 0) для первой строки и setCursor(5, 1) для второй. Посчитайте в уме длину строки так, чтобы текст не вылез за шестнадцатый столбец.
- Выведите на вторую строку рядом с таймером текущее значение raw — убедитесь, что map() честно переводит 0..1023 в выбранный вами диапазон температур.
- Заставьте таймер показывать минуты и секунды в формате MM:SS. Подсказка: минуты — это sec / 60, секунды — sec % 60, а перед печатью секунд меньше десяти добавляйте ведущий ноль через if.
- LCD 16x2 питается от 5 В и построен на контроллере HD44780; по I2C нужны всего 4 провода: 5V, GND, SDA (A4), SCL (A5)
- Адрес модуля I2C: 0x27 у чипа PCF8574 или 0x3F у PCF8574A — если экран молчит, попробуйте второй адрес
- lcd.init() и lcd.backlight() — обязательный минимум в setup(); setCursor(столбец, строка) ставит курсор, print() пишет с текущей позиции
- Нумерация с нуля: столбцы 0–15, строки 0–1; строка длиннее 16 символов перетекает на соседнюю строку
- Квадраты вместо текста — крутите потенциометр контраста; залита верхняя строка целиком — проверьте адрес и SDA/SCL
- Старые цифры затирают пробелами, а не lcd.clear() — иначе экран мерцает
- 1. Сколько проводов занимает LCD 16x2 с модулем I2C на Arduino Uno?
- 2. Какой адрес чаще всего у модуля I2C на чипе PCF8574?
- 3. Что означает setCursor(0, 1)?
- 4. На экране два ряда квадратов, текста нет. Что проверяем первым делом?
- 5. Зачем после lcd.print(temp) печатают несколько пробелов?
Дисплей перестал быть чёрным ящиком: четыре провода, две команды инициализации, невидимый курсор и печать — вот и весь фундамент. В следующем уроке снимем переходник I2C и подключим LCD по-классически, через библиотеку LiquidCrystal и шесть сигнальных проводов, — вы увидите, что код почти не изменится, и поймёте, в каких случаях параллельный интерфейс честно лучше. А пока выведите на экран что-нибудь своё: имя проекта, версию прошивки или шутку для того, кто откроет корпус через год.
Частые вопросы по теме урока
- Сколько проводов занимает LCD 16x2 с модулем I2C на Arduino Uno?
- Модуль I2C сводит подключение к питанию и двум линиям шины: итого четыре провода. Шесть сигнальных — это про параллельный интерфейс без переходника. Четыре: 5V, GND, SDA, SCL
- Какой адрес чаще всего у модуля I2C на чипе PCF8574?
- PCF8574 по умолчанию отвечает на 0x27, а его собрат PCF8574A — на 0x3F. Модули внешне не отличить, поэтому адрес пробуют оба. 0x27
- Что означает setCursor(0, 1)?
- Первый аргумент — столбец, второй — строка, нумерация с нуля. Значит, столбец 0 и строка 1 — это самое начало второй строки. Начало второй строки
- На экране два ряда квадратов, текста нет. Что проверяем первым делом?
- Ровные ряды квадратов — признак того, что дисплей питается, но контраст не настроен. Крутите подстроечный резистор на модуле. Потенциометр контраста на модуле
- Зачем после lcd.print(temp) печатают несколько пробелов?
- Печать не стирает остальную строку. Без пробелов «5» и «25» сливаются в «255»: старая цифра остаётся, если её ничем не перезаписать. Чтобы затереть цифры предыдущего значения