У меня на столе лежит OLED-модуль, который молчал два вечера. Провода были собраны по схеме из урока про подключение, скетч — из примеров библиотеки, компиляция зелёная, а экран — просто стекло. Я менял провода, платы, порты USB — ничего. Спас меня случайно оставленный в скетче сканер из урока про сканер адресов: он напечатал единственный адрес 0x78. Туториал мой был написан под 0x3C — и только позже я узнал, что 0x78 это тот же адрес в другой записи. Поставил 0x3C — экран ожил.
В этом уроке разберём адресную кухню SSD1306: почему адресов два — 0x3C и 0x3D, откуда берётся 0x78 в некоторых туториалах, что означают коды возврата endTransmission и как по ним отличить пустую шину от молчащего кода. Затем — чек-лист диагностики тёмного экрана: питание, проводка, подтяжки, сосед по шине. И практика: сканер по списку типовых адресов в симуляторе, где каждое обращение видно в журнале шины.
Мораль на старт, чтобы было за чем вернуться: сканер — это тридцать секунд и один цикл for. Экономия — вечер. Я прогоняю его на любом новом модуле до первого рисования, и советую вам сделать то же.
Цена вопроса: без сканера диагноз тёмного экрана — это круговая пересборка макетки наугад. Я за те два вечера успел сменить три провода, две платы и один — как тогда казалось, бракованный — модуль, который оказался исправен. Сканер закрыл вопрос за минуту: шина живая, устройство отвечает, виновата запись адреса. После этого сканер живёт в моей библиотеке скетчей отдельным файлом и запускается первым на любом незнакомом модуле — OLED это, RTC или плата с барахолки без единой подписи.
Адреса 0x3C и 0x3D: почему сканер пишет 0x78
Каждое устройство на линии I2C слушает свой номер. У SSD1306 таких номеров два: 0x3C — подавляющее большинство модулей 0.96 и 1.3 дюйма — и 0x3D. Второй в магазине почти не встречается, зато получается своими руками: на плате модуля есть контакт SA0, и если соединить его с VCC вместо GND, адрес переезжает с 0x3C на 0x3D. Зачем это нужно: чтобы посадить два одинаковых OLED на одну шину — по двум проводам I2C — и показывать, скажем, температуру на одном и график на другом. Перепайка одного резистора — и конфликт адресов исчез.
Теперь 0x78. Шина передаёт адрес семью битами, а восьмой бит — направление: чтение или запись. Некоторые сканеры и даташиты печатают адрес вместе с этим битом: 0x3C, сдвинутый на разряд влево, — это 0x78. Это тот же прибор, та же ножка, та же подпись в begin() — просто запись восьмибитная. Если ваш сканер напечатал 0x78, делите на два — получайте 0x3C. Мой модуль с истории в начале урока жил именно на 0x78 в чужой записи, и два вечера я искал несуществующий второй адрес.
Симметрично про соседей: у LCD с переходником PCF8574 адрес 0x27 — мы его видели в уроке про LCD по I2C, у часов DS3231 — 0x68 из урока про RTC. На одной шине эти три прибора живут вместе: адреса не пересекаются, и каждый слушает только свои пакеты. Сканер покажет все три сразу — 0x27, 0x3C, 0x68 — и это здоровая, нормально населенная шина, а не хаос.
Как это работает под капотом: мастер открывает пакет стартовым условием, отправляет семь бит адреса и бит направления, после чего ждёт подтверждения. Каждый слейв на линии сравнивает принятые биты со своим номером, и совпавший прибор прижимает линию SDA на девятом такте — это и есть подтверждение ACK. Отсюда и арифметика адресного пространства: семь бит — это сто двадцать восемь комбинаций, из которых рабочими остаются около сотни, а границы зарезервированы служебно. Два одинаковых OLED на одной шине различаются одним битом: SA0 переключает младший разряд, и модуль меняет ответ с 0x3C на 0x3D.
Коды endTransmission и диагностика молчащего экрана
Сканер — не магия, а цикл: Wire.beginTransmission(адрес), затем Wire.endTransmission() и код возврата. Ноль — устройство откликнулось, адрес занят и всё хорошо. Двойка — на этот адрес никто не ответил: линия свободна, но подтверждения не пришло. Четвёрка и пятёрка — ошибки самой передачи. Мастер и slave из урока про master-slave живут по этим же правилам. По кодам видно, жива ли шина как физика: если ноль не вернулся ни на один адрес из ста двадцати — проблема не в адресе, а в проводке, питании или подтяжках.
Отсюда порядок диагностики, который я выписал списком ниже: сверху вниз, не перескакивая. Девять тёмных экранов из десяти лечатся первыми тремя пунктами — питание не туда, линии поменяны, адрес в скетче не тот. Четвёртый и пятый пункты редки, но именно на них умирают вечера, поэтому они в списке, а не в моей памяти.
Практика с кодами — простая привычка: печатать не только адрес, но и код, если он не ноль. Код 2 на всех адресах подряд означает, что электрика умерла до шины: нет питания, линии перепутаны или подтяжек нет вовсе. Код 2 на одном адресе из списка — это пустой адрес, обычное дело, если сканите кандидатов, а не весь диапазон. А четвёртый код часто ловится на слишком длинных проводах и лечится не кодом, а ножницами: укоротите перемычки — ошибка уходит сама.
Мой любимый вариант — сканер, который печатает находки на самом OLED: шапка со словом Skan, ниже — список найденных адресов мелким шрифтом. Прибор перестаёт зависеть от компьютера: воткнул макетку в повербанк, подождал секунду, посмотрел на экран. На полевом диагнозе — гараж, дача, антресоль — это разница между «принёс ноутбук» и «разобрался на месте». В симуляторе ниже ограничимся Serial, а экранную версию оставьте себе как задание: она собирается из двух скелетов этого раздела за десять минут.
#include <Wire.h> void setup() { Serial.begin(9600); Wire.begin(); Serial.println("Skan I2C: 8..119"); for (int addr = 8; addr < 120; addr++) { Wire.beginTransmission(addr); int kod = Wire.endTransmission(); if (kod == 0) { Serial.print("Est otvet: "); Serial.println(addr); } delay(5); } Serial.println("Skan zavershen");} void loop() {}Почему границы именно 8 и 119: адреса от 0 до 7 и от 120 до 127 зарезервированы под служебные цели шины, устройства там не живут — скан их только добавляет пауз. Задержка delay(5) на адрес — не суеверие: некоторые модули медленно отпускают линию после пакета, а без паузы сканер начинает путать отклики. Скан всего диапазона занимает около секунды — дешевле, чем одна перепрошивка наугад.
Про печать адресов в Serial — маленькая рутина, которая спасает глаза: контроллер печатает десятичные числа, а шестнадцатеричную запись 0x3C приходится собирать вручную из цифр и букв. Поэтому в своих сканерах я печатаю оба вида: десятичный — для арифметики и условий, шестнадцатеричный — для сверки с даташитом и туториалами. И следите за ведущим нулём: 0x0C и 0xC0 — два разных адреса, а перепутанная запись даёт симптом «адрес тот же, а модуль не отвечает».
Разница между списком кандидатов и полным сканом — в цене вопроса. Полный скан 8–119 обязателен, когда вы понятия не имеете, что за модуль в руках. Список из четырёх-пяти типовых адресов быстрее и нагляднее в демонстрациях, но он найдёт только то, что вы заранее предположили: модуль с нестандартным адресом вылетит из списка, и вы снова будете винить проводку. Правило у меня такое: первый скан — всегда полный, списком пользуюсь в повторных проверках после пересборки.
- Питание. Мультиметром на ножках VCC и GND модуля: пять вольт для платы со стабилизатором, 3.3 — для голого стекла. Просевшее питание выглядит как мёртвый экран.
- Проводка. SDA на A4, SCL на A5 у Uno и Nano; четыре провода подряд, без разрывов в рейках макетки.
- Сканер. Wire.begin, перебор адресов 8–119, печать кодов. Код 0 на 0x3C — электрика в порядке, виноват код.
- Адрес. Подставьте то, что напечатал сканер: 0x3C у большинства, 0x3D после перепайки SA0, 0x78 — та же 0x3C в восьмибитной записи.
- Подтяжки. У модуля обычно 4.7–10 кОм на борту; у голого стекла их нет — без резисторов к питанию шина глючит или молчит.
- Соседи по шине. Отключайте устройства по одному и сканьте после каждого: один зажатый прибор кладёт всю шину, виноватым выглядит экран.
- Адреса SSD1306: 0x3C и 0x3D; 0x78 — тот же 0x3C в восьмибитной записи со сдвигом влево
- Сканер: beginTransmission плюс endTransmission; код 0 — ответ, код 2 — пустой адрес
- Контакт SA0 переводит модуль на 0x3D — два одинаковых OLED живут на одной шине
- Сканер доказывает только адрес: картинка может отсутствовать при живом адресе — проверяйте display() и тип чипа
- Порядок диагностики: питание, проводка, сканер, адрес, подтяжки, соседи по шине — сверху вниз, не перескакивая
- Адреса 0–7 и 120–127 зарезервированы служебно — устройства там не ищут
- 1. Сканер напечатал устройство на 0x78. Какой это 7-битный адрес?
- 2. Что означает код 2 от Wire.endTransmission()?
- 3. Как развести два одинаковых OLED по разным адресам на одной шине?
- 4. Сканер видит 0x3C, но экран тёмный. Где искать дальше?
На этом электрическая часть раздела закрыта: экран подключён, адрес известен, сканер в арсенале. Дальше по плану — меню на кнопках и свои шрифты с кириллицей: интерактив и родной алфавит на пиксельном стекле, где 1024 байта буфера оставляют рисованию полную свободу. Скелеты сканера и скан-списка заберите в свою шпаргалку — они переживут не один проект.
И привычка на всю жизнь: новый модуль — сначала сканер, потом код. Тридцать секунд на сканирование экономят вечер угадывания; я теперь делаю это машинально, как проверяю полярность перед подачей питания. Тёмный экран почти всегда оказывается не браком, а непрочитанным адресом.
И про наклейку: адрес — это паспорт модуля, а паспорта хранят на корпусе. Кусочек малярного скотча с надписью 0x3C и размером 128x64 на обратной стороне платы экономит и сканер, и заглядывание в документацию в следующий раз. Мои модули уже год носят такие ярлыки, и ни один из них с тех пор не провёл вечер в статусе «брак по непонятной причине».
Частые вопросы по теме урока
- Сканер напечатал устройство на 0x78. Какой это 7-битный адрес?
- 0x78 — это 0x3C со сдвигом влево, вместе с битом направления. Делим на два — получаем 0x3C для begin(). 0x3C
- Что означает код 2 от Wire.endTransmission()?
- Код 2 — NACK на адрес: линия жива, но устройства с таким номером нет. Код 0 — отклик есть, адрес занят. На этот адрес никто не ответил: подтверждение не пришло
- Как развести два одинаковых OLED по разным адресам на одной шине?
- SA0 переключает адрес между 0x3C и 0x3D. Один резистор — и два экрана различимы для библиотеки. Перепаять адресную перемычку SA0 у одного модуля
- Сканер видит 0x3C, но экран тёмный. Где искать дальше?
- Адрес и электрика уже доказаны сканером. Дальше — display.display() после рисования, третий аргумент конструктора и тип контроллера на плате. В коде и типе чипа: display(), &Wire, SH1106 вместо SSD1306