В моём ящике живёт коллекция модулей с распродаж: экран без единой надписи, чёрная коробочка с четырьмя отверстиями и кварцем, плата со стёртой маркировкой. Даташитов нет, продавец не помнит, что продавал. Пять лет назад такой ящик пылился бы месяц — теперь я раскладываю его за пять минут: сканер I2C стучится в каждую адресную дверь и печатает, где открыли. Потом — стикер на модуль: 0x27, 0x68, 0x50. Адресная книга собрана, и половина её строк когда-то была «неизвестным железом». Один и тот же скетч раскладывает по полочкам и экраны, и часы, и датчики — ему всё равно, что за чип за дверью, он спрашивает только: «есть кто?».
Сканер — второй по полезности скетч в моём арсенале после мигалки, и при этом самый недооценённый: его стыдятся запускать «пока не подключу настоящую библиотеку», а потом теряют часы на угадывание адресов. Между тем весь сканер — это один цикл и проверка нуля, без единой библиотеки сверх стандартной Wire. В этом уроке напишем классический сканер с нуля, научимся читать его показания в Serial без мозгового штурма и разберём две ловушки, которые крадут время у каждого второго: десятичную запись адресов в мониторе порта и восьмибитные числа из даташитов, на которых шина молчит принципиально.
Механика: 112 стуков за доли секунды
Логика сканера примитивна до гениальности. Мастер перебирает адреса с 0x08 по 0x77 — ровно те 112 дверей, что остаются на шине после служебных номеров. На каждый адрес отправляется пустой пакет: beginTransmission(addr), ни одного байта данных, endTransmission(). Пустой пакет — легальный ход: адрес подтверждается сразу в рамке, поэтому даже молчаливое устройство обязано отозваться на свой номер, если оно живое. Не подтвердил никто — вернётся код ошибки, и сканер молча идёт к следующей двери. Нижнюю границу диапазона мы тоже обязаны модулям: первые семь номеров держит общий вызов и служебные форматы, поэтому с них осмысленный разговор не начинается.
Один зонд — это адрес плюс бит направления, девять тактов, около 90 мкс на 100 кГц; с накладными на код полный обход 112 адресов укладывается в десятки миллисекунд. Пять возможных кодов возврата endTransmission() мы разобрали построчно в базовом уроке про сканер — здесь из всех пяти работает только ноль, он и означает «дверь открыли». Существует ещё десятибитная адресация, но классические сканеры её не видят: под неё отданы старшие адреса, которые мы и так обходим.
#include <Wire.h> void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Сканер I2C: стучусь по адресам 8..119");} void loop() { int found = 0; unsigned long t0 = millis(); for (int addr = 8; addr <= 119; addr++) { Wire.beginTransmission(addr); if (Wire.endTransmission() == 0) { // ACK — дверь открыли found++; Serial.print("Адрес "); Serial.print(addr); // десятичное: 39 Serial.print(" = 0x"); if (addr < 16) Serial.print("0"); Serial.println(addr, HEX); // шестнадцатеричное: 27 } } Serial.print("Найдено устройств: "); Serial.println(found); Serial.print("Обход занял "); Serial.print(millis() - t0); Serial.println(" мс"); Serial.println("---"); delay(5000);}Что появится в Serial: строка «Адрес 39 = 0x27». Для человека с десятичными привычками она вводит в ступор: 39 и 27 — это же разные числа! Разбираем: Serial.print(addr) печатает число как есть, в десятичной системе, а Serial.print(addr, HEX) — то же самое число в шестнадцатеричной, только без префикса, его я дописываю строкой выше. Тридцать девять в десятичной — это 2×16 + 7, то есть 0x27. Мой способ перевода в голове: делите на 16; частное и остаток дают две шестнадцатеричные цифры. Сто четыре: 6 и остаток 8 — 0x68, часы. Шестьдесят: 3 и остаток 12 — 0x3C, на этом адресе обычно живёт OLED.
Замер времени — не украшение, а аргумент. Полный обход дешевле, чем поиск адреса в документации, поэтому сканер я запускаю раньше, чем открываю чужой скетч. Пятисекундная пауза между проходами нужна не шине — ей всё равно, — а человеку, читающему монитор порта: сами строки на 9600 бод уходят за миллисекунды, и без паузы результаты нескольких проходов сливаются в кашу. На живой плате обход занимает десятки миллисекунд — если замер вдруг выдал сотни, значит, в цикле затесался лишний delay после каждой находки, а шина ни при чём.
Две ловушки: 39 — это не 27, а 0x4E — не адрес
Первая ловушка — числа из Serial. Новичок видит 39 и вставляет в конструктор LiquidCrystal_I2C lcd(39, 16, 2) — и всё работает: 39 и 0x27 одно и то же число, библиотеке без разницы. Но через месяц, сверяя свой скетч с чужим примером, где стоит 0x27, он решит, что у него «другой модуль», и начнёт второй круг поисков. Привычка, которую я рекомендую: в своём коде всегда пишите шестнадцатеричную запись с префиксом 0x, а десятичные показания сканера переводите сразу, при копировании. Тогда подставить найденный адрес в экранный скетч, как в уроке про первый текст на LCD, получится без сюрпризов.
Вторая ловушка — даташиты. NXP печатает на переходнике PCF8574 диапазон 0x40–0x4E, продавцы клеят стикер 0x4E, а Wire ждёт 0x20–0x27. Дело в разности традиций: даташит показывает восьмибитное слово — семибитный адрес, сдвинутый влево, с битом записи-чтения в младшем разряде. Библиотека Wire принимает семибитный адрес и сама добавляет бит направления, поэтому восьмибитное число ей не годится. Перевод один: делите на два. 0x4E пополам — 0x27; у часов DS1307 даташит вообще печатает пару: 0xD0 для записи и 0xD1 для чтения — пополам выходит 0x68 и то же значение с битом чтения. Если адрес из документации чётный и больше 0x77, это почти наверняка восьмибитная запись — делите на два и пробуйте снова. И не стесняйтесь проверять себя сканером: реальный ответ шины всегда весомее, чем число с наклейки.
Сканер как первый скетч на новой плате
Мой ритуал: любую новую плату или модуль я запускаю сканером до того, как открою чужой пример и повешу на шину второе устройство. Логика простая: если пустая шина с одним модулем молчит, виновата физика или адрес — и честнее это выяснить на минимуме переменных. Показания сканера — это диагноз из четырёх сценариев, таблица ниже работает быстрее любого поиска по форумам. Стикер с адресом на каждый модуль и список на крышке коробки — низкотехнологично, но за пять лет ни один адрес не потерялся: приходил новый модуль — тридцать секунд сканера, строка в списке, стикер на корпус. Кстати, почему первый свободный адрес для своих слейвов — 0x08 и как вообще плата становится устройством, рассказано в уроке про мастер-слейв на I2C: сканер видит и такие платы.
| Показание сканера | Диагноз | Что делать |
|---|---|---|
| Один адрес, например 39 (0x27) | Устройство живое, шина настроена | Подставляйте адрес в рабочий скетч |
| Два-три адреса в списке | На шине несколько модулей | Сверьте с таблицей типовых адресов: 0x27 экран, 0x68 часы |
| Ответ на каждом адресе | Замкнута линия SDA или модуль умирает | Отключайте устройства по одному и ищите виновника |
| Полная тишина | Провода, земля, питание или подтяжка | Вернитесь к уроку про подключение и прозвоните схему |
В симуляторе шина отвечает на каждый адрес — движок эмулирует идеальную связь без молчащих дверей, поэтому список выходит сплошным. Читайте это как стресс-тест: на живой плате такой результат означал бы замкнутую линию SDA, и диагноз из таблицы выше сказал бы «отключайте устройства по одному». Реальный список — один-два адреса по числу модулей. Схему перед сканом я проверяю по порядку сборки из урока про подключение и подтяжки: земля, питание, сигнальные, резисторы. Осталось научить сканер опознавать жильцов и переваривать ваши вводные — три задачи ниже.
Адресная книга собрана, две ловушки обезврежены, сканер — в папке инструментов. Опознать найденный адрес по владельцу помогает шпаргалка типовых жильцов из базового урока про сканер: 0x20–0x27 переходники PCF8574, 0x3C OLED, 0x68 часы, 0x50 энергонезависимая память. Дальше по разделу — анатомия самой библиотеки Wire и чтение регистров настоящих датчиков, а с адресами в кармане вы уже можете поднять на одной шине экран и часы одновременно, как делали в уроке про подключение. Если захотите вводить адрес с клавиатуры монитора порта и строить на этом меню диагностики — чтение чисел из Serial даст недостающий приём: parseInt с проверкой буфера, чтобы пустой ввод не превращался в фиктивный ноль.
- Зафиксируйте время обхода из Serial, затем сузьте цикл до 32–63 (это 0x20–0x3F) и замерьте снова. Объясните разницу сами: диапазон стал короче вчетверо.
- Добавьте опознаватель: если найден адрес 39, печатайте «экран», если 104 — «часы». Подсказка: цепочка if внутри блока с нулевым ответом.
- Переведите на бумажке показания 39, 60 и 104 из десятичной системы в шестнадцатеричную и сверьте с Serial. Шестидесяти соответствует 0x3C — на этом адресе обычно живёт OLED-экран.
- Сканер — это цикл по адресам 8–119 плюс beginTransmission и endTransmission; ноль в ответе означает «устройство здесь»
- Пустой пакет легален: адрес подтверждается в рамке, поэтому даже молчаливое устройство отзывается, если живое
- Один зонд — 9 тактов, около 90 мкс на 100 кГц; полный обход 112 адресов укладывается в десятки миллисекунд
- Даташиты печатают восьмибитные адреса: 0x4E и 0xD0 делите на два — получите 0x27 и 0x68
- Ответ на каждом адресе — замкнутая SDA или умирающий модуль; полная тишина — земля, питание, провода, подтяжка
- 1. Сканер считает адрес занятым, когда endTransmission() вернул:
- 2. В Serial напечатано «Адрес 104». Какой это адрес в шестнадцатеричной записи?
- 3. Даташит печатает адрес 0x4E. Что подставлять в вызовы Wire?
- 4. Сканер отвечает «устройство найдено» на каждом адресе с 8 по 119. Что это чаще всего значит на реальной плате?
- 5. Зачем в сканере delay(5000) между проходами?
Частые вопросы по теме урока
- Сканер считает адрес занятым, когда endTransmission() вернул:
- Ноль — устройство подтвердило адрес битом ACK. Остальные коды означают разные виды молчания: от переполненного буфера до прочих сбоев линии. 0
- В Serial напечатано «Адрес 104». Какой это адрес в шестнадцатеричной записи?
- 104 = 6×16 + 8, то есть 0x68 — типичный адрес часов DS3231 и DS1307, а заодно и MPU6050. Десятичная и шестнадцатеричная запись — два имени одного числа. 0x68
- Даташит печатает адрес 0x4E. Что подставлять в вызовы Wire?
- Wire принимает семибитный адрес и сам добавляет бит направления, поэтому восьмибитное число из даташита делят на два: 0x4E → 0x27. 0x27 — восьмибитную запись делят на два
- Сканер отвечает «устройство найдено» на каждом адресе с 8 по 119. Что это чаще всего значит на реальной плате?
- Линия, прижатая к земле постоянно, отвечает ACK на любой адрес. Отключайте устройства по одному и ищите виновника. Замкнута линия SDA или умирает модуль
- Зачем в сканере delay(5000) между проходами?
- Шине пауза не нужна — обход занимает миллисекунды. Пауза для человека: без неё строки на 9600 бод сливаются в кашу, и сложить картину по трём проходам подряд невозможно. Чтобы человек успел прочитать монитор порта