Экран молчал. Я перепрошил Uno четыре раза, переустановил LiquidCrystal_I2C, покрутил потенциометр контраста до хруста — экран был заведомо рабочий, код был из официального примера. Между делом я успел перечитать три форума и усомниться в честности продавца. Спасла самодельная программа из двадцати строк: сканер I2C показал устройство на адресе 0x3F, а я все четыре часа стучался на 0x27.
Это самая частая история про I2C вообще и про LCD1602 в особенности: одинаковые на вид модули отвечают на разных адресах, и угадать невозможно. В этом уроке напишем сканер, который за секунду опрашивает адреса от 0x03 до 0x77 и печатает, кто откликнулся. Научимся читать коды ошибок endTransmission(), подставлять найденный адрес в скетч и отличать «плохой адрес» от «плохих проводов» — второе, увы, бывает чаще.
Почему 0x27 и 0x3F — одна и та же микросхема
Переходник для LCD-экранов построен на микросхеме PCF8574 — восьмибитном расширителе портов, который превращает два провода шины в восемь линий данных для экрана. Она выпускается в двух версиях: PCF8574 занимает адреса 0x20–0x27, а PCF8574A — 0x38–0x3F. Маркировка у них почти неразличимая, цена одинаковая, в наборах их кладут вперемешку. Конкретный адрес внутри диапазона выбирают перемычки A0, A1 и A2 — на дешёвых переходниках они спаяны намертво, и сменить его нельзя.
Отсюда моё рабочее правило: любой чужой скетч с зашитым адресом — лотерея. Порядок всегда один: подключил модуль → запустил сканер → прочитал адрес из Serial → вставил в свой код. Тридцать секунд против четырёх часов гадания. Сканер я храню отдельным скетчем с 2019 года и запускаю его чаще, чем любой пример из документации.
Как работает сканер: 117 стуков в разные двери
Логика сканера предельно проста. Мастер перебирает адреса от 0x03 до 0x77 — это 117 дверей. Для каждой он делает beginTransmission(addr) и тут же закрывает передачу через endTransmission(), не отправляя ни байта данных. Если на линии есть устройство с таким адресом, оно откликается подтверждением, и endTransmission() возвращает 0. Если никто не ответил — возвращается код ошибки. Обойти подъезды и послушать, где на «тук-тук» откроют, — вот и вся физика.
| Код возврата | Что означает |
|---|---|
| 0 | Устройство ответило (ACK) — адрес занят |
| 1 | Слишком много данных в буфере передачи |
| 2 | На адрес никто не ответил (NACK) |
| 3 | Устройство узнало адрес, но отвергло данные |
| 4 | Прочая ошибка линии |
Для сканера важен только ноль. Остальные коды пригождаются в отладке: двойка почти всегда значит «перепутаны SDA и SCL» либо «нет питания модуля», тройка — что устройство живое, но данные принять не готово.
#include <Wire.h> void setup() { Wire.begin(); // Uno — мастер Serial.begin(9600); Serial.println("Сканер I2C. Ищу устройства от 0x03 до 0x77...");} void loop() { byte count = 0; for (byte addr = 3; addr <= 119; addr++) { // 0x03 ... 0x77 Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { // кто-то ответил? Serial.print("Устройство на адресе 0x"); if (addr < 16) Serial.print("0"); Serial.println(addr, HEX); count++; delay(5); } } if (count == 0) { Serial.println("Пусто. Проверь SDA=A4, SCL=A5, землю и подтяжку."); } else { Serial.print("Найдено устройств: "); Serial.println(count); } Serial.println("Повтор через 5 секунд"); delay(5000);}Построчно, потому что тут есть пара неочевидных мелочей. Переменная addr объявлена как byte: значений всего 120, и переполнения не случится. Но если когда-нибудь захотите перебирать полный диапазон до 0xFF, берите int — иначе byte закольцуется на 255 и цикл станет вечным. Проверка if (addr < 16) добавляет ведущий ноль, чтобы адрес 0x07 не печатался как «0x7». delay(5) внутри находки — пауза, чтобы строки не слипались в кашу на скорости 9600 бод.
Заметьте: сканер ничего не знает о ваших модулях — и это его сила. Ему не нужны библиотеки экранов, драйверы датчиков и даже знание, что за железо висит на шине: только Wire и цикл. Именно поэтому он первым запускается на любой новой плате у меня в мастерской — раньше всех остальных скетчей.
Разбор результатов: что нашлось и что с этим делать
Допустим, сканер напечатал «Устройство на адресе 0x3F». Дальше всё скучно и хорошо: подставляем адрес в скетч — LiquidCrystal_I2C lcd(0x3F, 16, 2); — и экран оживает. Но вывод сканера полезно читать глубже: по адресу часто можно опознать модуль, даже если на нём стёрта маркировка. Вот моя шпаргалка по типовым адресам:
| Адрес | Кто там живёт |
|---|---|
| 0x20–0x27 | PCF8574 — переходник для LCD1602 |
| 0x38–0x3F | PCF8574A — тот же LCD, другая партия |
| 0x3C | OLED-экран SSD1306 |
| 0x50–0x57 | Энергонезависимая память 24Cxx |
| 0x68 | Часы DS1307/DS3231, гироскоп MPU6050 |
| 0x76–0x77 | Датчики погоды BME280/BMP280 |
Если на шине два устройства с одним адресом — например, часы DS3231 на 0x68 и гироскоп MPU6050, тоже на 0x68, — они начнут отвечать одновременно, и данные перемешаются. Диагноз поставит тот же сканер: он увидит один адрес, а вы вспомните, что подключили двух кандидатов. Лечится даташитом: у MPU6050 есть ножка ADO — притяните её к питанию, и модуль переедет на 0x69.
Эксперименты для закрепления
Три задачи — прямо в симуляторе. На реальной плате они выглядят так же, только панель I2C заменяет ваш Serial-монитор:
- Переведите найденный адрес из десятичного вида в шестнадцатеричный вручную: старшую цифру умножьте на 16 и прибавьте младшую. Проверка: 39 → 2 × 16 + 7 → 0x27.
- Добавьте в сканер проверку: если найден адрес 0x27, печатайте «LCD найден — можно печатать». Подсказка: внутри блока if (Wire.endTransmission() == 0) добавьте сравнение addr == 0x27.
- Сузьте диапазон цикла до 32–63 (это 0x20–0x3F) и проговорите, какие модули могли бы откликнуться в этой зоне. Проверьте себя по таблице выше.
Когда сканер не спасает: провода, питание, скорость
Первый подозреваемый — провода. Dupont-контакты надламываются у обжима и работают через раз, а шина на 100 кГц к этому чувствительна. Мой порядок действий: заменить оба сигнальных провода на заведомо целые, проверить питание модуля, убедиться в общей земле — и только потом лезть в код. Про длину тоже помним: стандарт ограничивает ёмкость шины 400 пФ, на практике это «около метра» обычных проводов при 100 кГц; длиннее — подтяжка 2,2 кОм или снижение скорости.
Второй подозреваемый — питание. Экран с переходником ест копейки, но если кормить всю макетку от батарейки-таблетки, ничего не заведётся, и сканер тут не виноват. Третий — скорость: если шину уже разогнали до 400 кГц правкой регистров, на длинных проводах сканер может не увидеть даже живое устройство. Моё мнение: во время отладки полезно временно сбросить скорость до минимума и поставить подтяжку пожирнее, 2,2 кОм, — если после этого девайс отвечает, виновата физика, а не код.
- Сканер перебирает 117 адресов (0x03–0x77) и считает устройство найденным, когда endTransmission() вернул 0
- Коды endTransmission(): 0 — успех, 1 — буфер переполнен, 2 — никто не ответил, 3 — данные отвергнуты, 4 — прочее
- LCD1602 с PCF8574 живёт на 0x27 или 0x3F; чужой скетч с зашитым адресом — всегда лотерея
- «Пусто» чаще значит плохие провода, отсутствие общей земли или подтяжки, чем сломанный код
- Конфликт адресов виден по перемешанным данным; у многих модулей адрес меняется джампером или ножкой ADO
- 1. Сканер считает устройство найденным, когда endTransmission() вернул:
- 2. На каком адресе ответит экран с переходником на PCF8574A?
- 3. Что вернёт endTransmission(), если SDA и SCL перепутаны местами?
- 4. На одной шине часы DS3231 и модуль MPU6050. Что произойдёт?
- 5. Сканер печатает «Пусто», а модуль точно рабочий. Что проверить первым?
Сканер — мой талисман: скучные двадцать строк, которые превращают «экран молчит» в «адрес 0x3F» за одну минуту. В следующем уроке пойдём дальше: две платы на одной шине, где вторая Uno сама становится устройством с адресом 0x08 и отвечает мастеру по запросу. Пока — запустите симуляторный сканер и посмотрите, как панель I2C отрабатывает стук в каждую дверь, пока на 0x27 не откроют.
Частые вопросы по теме урока
- Сканер считает устройство найденным, когда endTransmission() вернул:
- Ноль — устройство подтвердило адрес (ACK). Все остальные коды — ошибки. 0
- На каком адресе ответит экран с переходником на PCF8574A?
- PCF8574A занимает диапазон 0x38–0x3F; у PCF8574 без буквы А — 0x20–0x27. Внешне микросхемы различить почти нельзя. 0x3F
- Что вернёт endTransmission(), если SDA и SCL перепутаны местами?
- Сигнальные линии перепутаны — подтверждений нет, и сканер получает типовой код 2 (NACK по адресу). 2 — на адрес никто не ответил
- На одной шине часы DS3231 и модуль MPU6050. Что произойдёт?
- Адрес конфликтует. У MPU6050 есть ножка ADO: при подтяжке к питанию модуль переезжает на 0x69. Оба отвечают на 0x68 и будут мешать друг другу
- Сканер печатает «Пусто», а модуль точно рабочий. Что проверить первым?
- Физика важнее кода: земля, питание, подтяжка и распиновка — четыре из пяти причин пустого сканера. Провода: SDA→A4, SCL→A5, общую землю, питание и подтяжку