Весовой пост в мастерской я собирал на двух АЦП ADS1115: один читал шунт солнечной панели, второй — ток заряда аккумулятора. Чипы приезжали одной партией, обе ножки ADDR прижаты к земле, и оба уверенно заняли адрес 0x48. Адресную ножку можно было развести — у ADS1115 она даёт 0x48–0x4B, — но на том же шлейфе уже жил экран 0x3C без единой перемычки, а список «хочу ещё» продолжал расти: датчик давления, компас, второй светильник. Четыре адреса ножкой ADDR проблему не закрывали. Спасением стала платка TCA9548A: механику переключения каналов и восемь одинаковых экранов разобрал базовый урок про мультиплексор, а здесь — то, о чём он сказал полстроки: жизнь с одинаковыми адресами, подтяжки подшин и диагностика каналов.
Базу повторять не буду: как пишется маска канала, откуда берутся адреса 0x70–0x77 и почему у каждой подшины свой лимит ёмкости — всё в базовом уроке. Мой фокус ниже по течению, когда перекрёсток уже впаян и по нему начинают жить датчики. Альтернативу — вторую мастер-плату — я отмёл ещё там: для неё нужны две прошивки и собственный протокол между платами, а здесь один чип и один байт перед посылкой. Контракт с симулятором честный с самого начала: самого чипа в виртуальной схеме нет, заглушка шины принимает всё, — зато трафик на панели I2C подлинный, маска на 0x70 и запросы по каналам видны так, как ушли бы с живой платы.
План такой: карта «канал → жилец» и почему уникальна пара, а не адрес; подтяжки — не только сколько, но и где, на конце подшины; обход каналов с чтением ADS1115 как рабочая диагностика; и изоляция зависшего слейва одним байтом. Все примеры — с моего весового поста: два АЦП, экран и датчик давления на одном перекрёстке, и ни один из них не знал о существовании соседей.
Карта каналов: адрес повторяется, пара «канал, адрес» — нет
После мультиплексора уникальным именем перестаёт быть адрес — уникальна пара «канал, адрес». На канале 0 у меня живёт ADS1115 солнечного шунта на 0x48, на канале 1 — такой же чип батарейного входа, тоже 0x48, и путаницы между ними нет: активна в каждый момент только одна подшина, и отвечать на 0x48 физически некому, кроме выбранного. Вся дисциплина при этом сводится к одному правилу из базового урока: запись маски в 0x70 стоит вплотную к beginTransmission, а не «где-то в setup на будущее». Пропустили переключение — посылка уехала на прошлый канал, и отлаживать вы будете совсем другой датчик, чем думали.
Второй совет звучит скучно и экономит вечера: ведите карту «канал → жилец» прямо в шапке скетча, комментариями над константами. Сканер из урока про поиск устройств на шине на голой мастер-шине покажет только 0x70 — самого мультиплексора; каждый канал сканируется отдельно, уже после переключения маски, поэтому без карты каналов вопрос «а что у меня на третьей подшине» решается подключением к компьютеру. И важная граница: одинаковые чипы на разных каналах — законно, два одинаковых чипа на одной подшине — авария. Сканер покажет один адрес, но при чтении оба чипа полезут на SDA одновременно, и слово соберётся из половинок двух разных ответов.
Отдельный бонус архитектуры — изоляция ёмкостей. Лимит шины I2C — 400 пФ на линию, и без перекрёстка эта цифра складывалась из всех шлейфов сразу: один длинный хвост съедал запас у всей шины, и короткие соседи страдали за его длину. С мультиплексором 400 пФ считаются для каждой подшины отдельно: полутораметровый шлейф канала с экраном живёт своей жизнью, а датчики рядом — своей. Тот же механизм спасает от медленных устройств: капризный датчик, тормозящий шину долгими паузами, запирается на своём канале и больше не задерживает опрос остальных.
| Канал | Жилец | Адрес | Чем занят |
|---|---|---|---|
| 0 | ADS1115, шунт солнечной панели | 0x48 | ток до 5 А, узкий предел PGA |
| 1 | ADS1115, вход аккумулятора | 0x48 | тот же чип, тот же адрес |
| 2 | экран SSD1306 | 0x3C | без адресных ножек вообще |
| 3 | BMP280 | 0x76 | давление и температура |
Концевые подтяжки: резистор живёт на конце подшины
Про номиналы базовый урок дал границу «до полуметра живём на подтяжках мастера, дальше — по резистору на канал». Моё уточнение — не только сколько, но и где. Резистор ставится у дальнего конца подшины, у последнего устройства, а не у разъёма мультиплексора: линия от мастера до конца шлейфа пробегается сигналом целиком, и фронт, поднятый на конце, возвращается к мультиплексору уже целым, без двойного изгиба посередине. На хвостах до метра разницу я на глаз не увидел, а на полутораметровом шлейфе канала с экраном отдельные NACK на самых жирных кадрах исчезли после переноса пары резисторов с начала подшины на её конец. Дешёвая правка, которую неловко было делать полгода. Симптом слабой подтяжки узнаваемый: шина живёт минутами, а потом разом сыплет неудачами — фронты к этому моменту растянуты до предела, и любой жирный кадр добивает их окончательно.
Номиналы привычные: 4,7 кОм к напряжению своей подшины на 100 кГц; при разгоне до 400 кГц, которую чип паспортно тянет, или на хвосте длиннее метра — 2,2 кОм. Напряжение у подшины своё: 3,3-вольтовым датчикам подтяжка к 3,3 В, и мультиплексор спокойно сводит такие уровни с 5-вольтовой Uno — механика уровней разобрана в уроке про 3,3 и 5 В. Арифметику по ёмкости линии и току ног выводит урок про номиналы подтяжек; здесь достаточно правила «4,7 на конце, 2,2 на длинных и быстрых». Плотность тоже считается: каждое новое устройство прибавляет ёмкости, и та же подтяжка на подшине с пятью датчиками поднимает фронт слабее, чем с двумя, — у меня граница простая: подшина длиннее метра или плотнее трёх жильцов — сразу 2,2 кОм.
Вторая суперсила перекрёстка — изоляция. Залипший слейв, прижавший SDA к земле, укладывает только свою подшину: закрываю канал байтом 0x00 — и остальные семь живут как ни в чём не бывало. Без мультиплексора та же авария глушила шину целиком: умирающий провод экрана вешал линию, а вместе с ней часы и датчики, и виновника я искал по очереди во всех устройствах. Диагностика строится на кодах возврата endTransmission: двойка — «по адресу никого», тройка — чип жив, но занят; ноль на закрытом канале и двойка на открытом — законный способ проверить, что переключение маски дошло до чипа; расшифровку всех кодов я собирал отдельным уроком про endTransmission — там же про пинг пустым пакетом.
Для стенда, живущего сутками, в loop ставится дозор: раз в десять секунд обходим каналы с пингом, два молчания подряд — на экране появляется флаг «проверь шлейф». Вилка из двух молчаний выбрана не случайно: одна неудача случается и на живой линии, когда датчик занят пересчётом, а два подряд за двадцать секунд уже не оставляют вариантов. Именно такой дозор поймал отходящий контакт в шлейфе аккумуляторного канала через час после сборки, пока счётчик ещё не начал врать: без него я бы узнал о браке по кривому графику за неделю.
#include <Wire.h> // TCA9548A на 0x70: канал 0 - солнечный шунт, канал 1 - аккумулятор.// На каждом канале свой ADS1115, у обоих адрес 0x48 - и это законно.const byte MUX = 0x70;const byte ADS = 0x48; void tcaselect(int kanal) { Wire.beginTransmission(MUX); Wire.write(1 << kanal); // бит n открывает подшину n Wire.endTransmission();} void setup() { Wire.begin(); Serial.begin(9600); Serial.println("Обход каналов: ADS1115 на каждом");} void loop() { for (int k = 0; k < 2; k++) { tcaselect(k); // переключение вплотную к разговору Wire.beginTransmission(ADS); Wire.write(0x00); // указатель на регистр конверсии Wire.endTransmission(); int n = Wire.requestFrom(ADS, 2); if (n != 2) { Serial.print("kanal "); Serial.print(k); Serial.println(": молчит"); continue; } int hi = Wire.read(); int lo = Wire.read(); int raw = (hi << 8) | lo; // 16 бит со знаком Serial.print("kanal "); Serial.print(k); Serial.print(": "); Serial.println(raw); } delay(1000);}Перед чтением в чип пишется указатель 0x00 — регистр результата конверсии, — а собранное слово держит знак: дифференциальная пара ADS1115 выдаёт минус так же честно, как плюс. Вся настройка тракта — конфигурационное слово, пределы PGA, пересчёт отсчётов в вольты — осталась за кадром: сборку чипа показал урок про внешний АЦП, токовую пару через шунт с узким пределом — урок про дифференциальный режим. Здесь АЦП фигурирует как жилец канала: маска, указатель, два байта. Маску я не кэширую и не «оптимизирую»: одна лишняя запись в 0x70 стоит десятка микросекунд, а экономия на ней оборачивается поиском уехавшей посылки по чужому каналу.
В симуляторе заглушка шины подтверждает и 0x70, и 0x48, поэтому каждый канал выглядит живым: все ветки чтения срабатывают, слово 0x2A2A приходит на каждом проходе. На реальной плате пустой канал вернёт ноль байтов, счётчик молчаний накопится, и дозор объявит канал умершим — я проверял это, вынимая шлейф из разъёма под работающим стендом: ровно два пропущенных прохода, и на экране загорается вердикт. Ветку молчания я поэтому считаю страховкой живой платы, а не рабочим кодом виртуальной схемы: заглушке терять байты не с чего.
- 1. На канале 0 и канале 1 живут два ADS1115, оба с адресом 0x48. Что нужно перед разговором со вторым?
- 2. Где ставить подтяжку длинной подшины?
- 3. Сканер на мастер-шине с подключённым TCA9548A показывает:
- 4. Пустой канал, пинг 0x48 на живой плате. Сколько байтов вернёт requestFrom?
- 5. Завис слейв на канале 3 и держит SDA. Как спасти остальных?
- Уникальна пара «канал, адрес», а не адрес: одинаковые чипы живут на разных подшинах
- Маска 1 << n пишется вплотную к beginTransmission: пропущенный tcaselect отправит данные на прошлый канал
- Подтяжка длинной подшины ставится на конце линии: 4,7 кОм на 100 кГц, 2,2 кОм на длинных и 400 кГц
- Двойка из endTransmission - «пусто»: обход каналов с пингом раз в 10 секунд ловит отходящий контакт
- Байт 0x00 закрывает канал: залипший слейв отрезается, остальные семь продолжают работать
Частые вопросы по теме урока
- На канале 0 и канале 1 живут два ADS1115, оба с адресом 0x48. Что нужно перед разговором со вторым?
- Активна одна подшина, поэтому одинаковые адреса на разных каналах не мешают. Запись 1 << 1 = 0x02 в 0x70 открывает канал 1 — и вплотную к посылке, не заранее. открыть канал 1 записью 0x02 в 0x70
- Где ставить подтяжку длинной подшины?
- Фронт, поднятый на конце линии, приходит к мастеру целым. На полутораметровом шлейфе перенос резисторов на конец убрал редкие NACK на жирных кадрах. у дальнего конца, у последнего устройства
- Сканер на мастер-шине с подключённым TCA9548A показывает:
- Пока каналы не открыты, подшины от мастера отключены. Жильцов каждого канала сканер показывает отдельно, после записи маски. только 0x70 — самого мультиплексора
- Пустой канал, пинг 0x48 на живой плате. Сколько байтов вернёт requestFrom?
- По адресу никого — ноль байтов. В симуляторе заглушка всегда отвечает, поэтому ветка молчания проверяется только на живой плате. 0
- Завис слейв на канале 3 и держит SDA. Как спасти остальных?
- Ноль в маске размыкает ключи канала: авария остаётся запертой на своей подшине, остальные семь живут. Вернуть канал — той же записью маски. закрыть канал байтом 0x00