Сканер из базового урока напечатал два устройства — и на этом его служба закончилась. Только список адресов отвечает на вопрос «кто живёт», а не «здоровы ли жильцы»: экран может подтвердить адрес и остаться пустым, часы — откликнуться на стук и молчать на запрос времени. Разница прячется в коде, который Wire.endTransmission() возвращает на каждый адрес, — обычный сканер его выбрасывает, увидев ноль. Здесь я дорабатываю сканер до диагноста: он проходит адреса с 8 по 119, ловит код ответа каждого и переводит его в человеческую фразу. Классическую механику и ловушки с 39 против 0x27 разобрал урок про классический сканер — мне добавлять к нему нечего, кроме одного: кодов, которые у него шли на выброс.
Пять возможных кодов — это пять разных диагнозов, и путать их дорого. Двойка говорит «на этом адресе никого», тройка — «кто-то есть, но байт не взял», четвёрка — «шина заклинила». Новичок видит в мониторе просто «не работает» и начинает с замены библиотеки; диагност видит тройку и понимает, что библиотека ни при чём — занят сам чип. Вся разница между часом блужданий и минутой ремонта — одна переменная, в которую стоило сохранить результат.
Справедливости ради: не всякий ремонт начинается с кодов. Половина моих историй про I2C кончалась на первом же прогоне обычного сканера — адрес нашёлся, вопрос закрылся. Диагност нужен тогда, когда сканер уже отработал, а симптом остался: модуль на месте, а байты кривые, экран подтверждает адрес и молчит, список живёт своей жизнью между прогонами. Для этих случаев и пишется расширенная версия — она же бесплатно превращается в конвейер приёма новых модулей, где каждый жилец попадает в журнал с кодом ответа и датой.
Пустой пакет: как мастер спрашивает «есть кто?»
Механика опроса умещается в две строки. Wire.beginTransmission(addr) открывает рамку и кладёт в буфер адресное слово, Wire.endTransmission() отправляет его и ставит стоп-условие. Если между ними не было ни одного Wire.write(), пакет состоит из одного адреса и бита направления — семь бит адреса, бит чтения-записи и девятый такт подтверждения. При 100 кГц такой зонд занимает около 90 микросекунд, полный обход 112 дверей укладывается в десятки миллисекунд. Устройство обязано прижать линию SDA на девятом такте, если номер его, — и молчать, если чужой. По этому единственному биту сканер и отличает жильца от пустоты.
Диапазон обхода не случаен: с 8 по 119. Стандарт NXP держит нижние номера 0x00–0x07 под общий вызов и служебные форматы — туда стучать бессмысленно, чипы на них не отвечают. Верхние 0x78–0x7F отданы десятибитной адресации, которую классический сканер всё равно не увидит. Живых дверей остаётся 112, и ходить по ним стоит по порядку — про соседние адреса и их владельцев собрана таблица типовых модулей. Полную анатомию всех пяти кодов построчно раскладывал урок про endTransmission; здесь коды нужны как инструмент, а не как предмет.
| Код | Что значит | Типовая причина |
|---|---|---|
| 0 | адрес подтверждён, передача прошла | устройство на месте и живое — рабочий случай |
| 1 | байтов больше, чем влезает в буфер | буфер Wire на Uno держит 32 байта; сканер с пустым пакетом этот код не увидит |
| 2 | никто не подтвердил адрес | модуля нет, питание снято, провода или подтяжки |
| 3 | адрес подтвердили, байт отклонили | чип занят: EEPROM на цикле записи до 5 мс, регистр закрыт конфигурацией |
| 4 | прочий сбой передачи | линия придавлена, мастер проиграл арбитраж, шум на длинных проводах |
#include <Wire.h> void decodeCode(int code) { if (code == 0) Serial.println(" код 0: адрес подтверждён"); else if (code == 1) Serial.println(" код 1: байтов больше, чем влезает в буфер"); else if (code == 2) Serial.println(" код 2: адрес никто не подтвердил"); else if (code == 3) Serial.println(" код 3: адрес подтверждён, байт отклонён"); else Serial.println(" код 4: передача сорвалась, проверяйте линию");} void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Сканер-диагност: адреса 8..119"); int found = 0; for (int addr = 8; addr <= 119; addr++) { Wire.beginTransmission(addr); int code = Wire.endTransmission(); // здесь живёт весь диагноз if (code != 2) { // всё, что не пустая дверь Serial.print("0x"); if (addr < 16) Serial.print("0"); Serial.println(addr, HEX); decodeCode(code); found++; } } Serial.print("Отклики: "); Serial.println(found); Serial.println("Прочие адреса: код 2, дверь никого не услышала");} void loop() { delay(3000); // диагносту хватает одного прохода}Сколько это стоит во времени
Арифметика опроса достойна отдельного абзаца, потому что спорит с интуицией. Один зонд — девять тактов на 100 кГц, около 90 микросекунд; полный обход 112 адресов занимает около десяти миллисекунд даже с накладными на буфер и Serial. Это значит, что диагност можно звать хоть каждые полсекунды: для скетча он ничего не стоит, а для вас между прогонами видна живая динамика шины. Пауза в три секунды из моего скетча существует не для платы — ей всё равно, — а для человека, читающего монитор порта: без неё строки нескольких прогонов сливаются в сплошную простыню.
Второй практический вывод из той же арифметики: сканер дешевле любых справочников. Пока вы ищете адрес модуля по форумам и даташитам, диагност уже трижды обошёл шину и напечатал ответ первым лицом — от самой шины, без переводчиков. Десятая доля секунды на прогон превращает опрос в ритуал наравне с подключением питания: собрал — прогнал — записал. У меня этот ритуал живёт в первом же скетче каждой новой платы, раньше мигалки.
Чтение протокола: три сценария из Serial
Первый сценарий — норма: одна-две строки с нулями на адресах модулей, остальные 110 молчат с двойкой. Второй — полный ноль откликов на всей шине: код 2 везде означает физику, а не адреса — земля не общая, питание не дошло, подтяжек нет. Порядок сборки и прозвонку разобрал урок про подключение и подтяжки, повторять его не буду. Третий сценарий самый коварный: отклик на каждом из 112 адресов. Так выглядит линия SDA, придавленная к земле кем-то из участников, — для мастера каждый зонд «подтверждён», ведь линия прижата постоянно, а не на девятом такте.
Честная оговорка про коды 1 и 3: диагност с пустым пакетом их никогда не встретит. Единица живёт в обычных скетчах, которые пишут длинные страницы, тройка — там, где мастер торопится занятый чип. В таблицу они попали не для сканера, а для вас: когда рабочий скетч с экраном вернёт тройку, вы уже будете знать, что это занятость, а не пропажа модуля. Код 4 на живой макетке редкость, но на длинных проводах за метр-другой — частый гость, у меня он вылезал на шине, протянутой в коридор: виноват был не чип, а шлейф, и урок про длину проводов закрыл вопрос.
Подтяжки 4,7 кОм, перемычки A0/A1/A2 и перебор адресов 0x08–0x77: найдите каждое устройство и поймайте конфликт адресов.
Подгружаем интерактив…
В симуляторе список короче, чем на плате с той же распечаткой, по одной причине: модель устройств отвечает за два адреса, а не за все 112. На живой макетке прогоните диагност дважды — до и после подключения очередного модуля. Разница между двумя распечатками и есть его вклад в шину: у меня на столе разница держится на 0x27 и 0x68 уже третий год, и новые жильцы прописываются в этот список за тридцать секунд. Если проходы различаются между собой без ваших действий — шина плавит контакты или провод переломан внутри изоляции, и это тоже диагноз.
Отдельный вопрос — что диагност не умеет. Он не проверяет содержимое чипов: экран с убитой жидкокристаллической панелью и целым переходником получит честный ноль, спящий MPU6050 ответит как здоровый, пока вы не разбудите его байтом в регистр питания. Он не измеряет качество контакта: окисленная перемычка на макетке отвечает через раз, и между прогонами список будет плясать. И он слеп к частоте: диагност на 100 кГц ничего не скажет о поведении той же шины на 400 кГц, где ёмкость проводов уже режет фронты. Для каждого из этих случаев нужен свой инструмент — но первым делом всё равно пишется диагност: он дешёвый и сразу сужает круг подозреваемых вдвое.
Диагност — не замена снифферу: он отвечает на вопрос «кто и как отвечает», но не показывает содержимое кадров. Хотите видеть байты между мастером и чипом — урок про sniffer трафика ловит каждую рамку. А если единица и тройка зацепили всерьёз, у урока про endTransmission есть построчный разбор каждого кода с примерами скетчей, которые их добывают.
- 1. Какой пакет сканер отправляет на каждый адрес?
- 2. endTransmission вернул 2. Что произошло?
- 3. Диагност печатает отклик на каждом из 112 адресов. Первый подозреваемый?
- 4. Почему цикл сканера начинается с 8, а не с 0?
- 5. Мастер пишет второй байт в EEPROM во время цикла записи чипа (до 5 мс). Что вернёт endTransmission?
- Сканер-диагност отличается от обычного одной строкой: код endTransmission сохраняется и расшифровывается, а не выбрасывается
- Обход идёт по 112 живым адресам 8–119: нижние и верхние номера NXP резервирует под служебные нужды
- Код 0 — жилец, 1 — переполнение буфера (32 байта на Uno), 2 — адрес молчит, 3 — байт отклонён занятым чипом, 4 — срыв передачи
- Отклик на всех адресах — придавленная SDA или зависший слейв; лечится отключением модулей по одному
- Пустой пакет добывает только 0 и 2; тройку приносит занятый EEPROM или закрытый регистр в рабочем скетче
Частые вопросы по теме урока
- Какой пакет сканер отправляет на каждый адрес?
- beginTransmission(addr) без единого write, затем endTransmission. Адрес подтверждается сразу в рамке, поэтому байты не нужны — девятый такт уже даёт ответ. Пустой: только адресное слово, без единого байта
- endTransmission вернул 2. Что произошло?
- Двойка — нет подтверждения адреса. Единица — переполнение буфера, тройка — отказ на байте записи, четвёрка — прочие срывы передачи. Адресное слово ушло в пустоту: никто не подтвердил
- Диагност печатает отклик на каждом из 112 адресов. Первый подозреваемый?
- Подтверждение на каждом адресе — линия прижата кем-то постоянно, а не на девятом такте. Отключайте модули по одному; вторая версия — зависший слейв после оборванной транзакции. Линия SDA придавлена к земле
- Почему цикл сканера начинается с 8, а не с 0?
- NXP держит 0x00–0x07 под общий вызов и служебные форматы, старшие 0x78–0x7F — под десятибитную адресацию. Живых дверей остаётся 112. Нижние номера резервирует стандарт: общий вызов и служебные форматы
- Мастер пишет второй байт в EEPROM во время цикла записи чипа (до 5 мс). Что вернёт endTransmission?
- Занятый чип подтверждает адрес, но отклоняет байт записи — выходит код 3. Честный мастер выдерживает паузу до 5 мс между страницами записи. 3: адрес подтвердили, байт отклонили