Собирал панель аквариума: свет, компрессор, нагреватель, кормушка — каждая линия включается отдельно, плюс восемь кнопок для сцен. Считаем: экран висит на шине, сама шина съела A4 и A5, дальномер забрал два пина, зуммер ещё один. Осталось шесть, а надо шестнадцать. Покупать клон Mega за тысячу с лишним рублей не хотелось, и я потратил восемьдесят рублей на микросхему PCF8574 — и получил недостающие линии, не заняв ни одного нового пина платы. Вживую вся переделка заняла минут десять: питание, земля, два сигнальных провода на A4 и A5 — и панель ожила.
Расширитель — это восемь линий ввода-вывода, живущие на шине I2C: мастер пишет один байт — и восемь пинов меняют состояние; мастер просит байт — и видит, что на линиях. В этом уроке подключим чип к Uno на голом Wire, разберём коварную фразу из даташита «квазидвунаправленные порты» и поймём, почему светодиод от расширителя зажигают, подавая ноль, а реле напрямую вообще не подключают. Симулятор уже поставил чип на шину — панель I2C покажет каждый байт. К концу урока у вас будет скетч, который пишет маску, читает её обратно и печатает по битам, что увидел.
Квазидвунаправленные порты: читаем даташит без слёз
Слово «квазидвунаправленные» — самое пугающее в даташите, а смысл у него простой. После подачи питания все восемь линий висят в HIGH через очень слабый внутренний источник — порядка 100 мкА. Сильным может быть только сток: записанный в линию ноль чип держит уверенно, до 25 мА на пин, но не больше 100 мА на всю микросхему разом. Единица на выходе — это та же слабая подтяжка, а не полноценное питание: потянуть ею нагрузку нельзя.
Из этого три практических вывода. Светодиод подключают «в минус»: +5 В → резистор 220 Ом → светодиод → пин расширителя, и горит он, когда в пине записан ноль; зачем светодиоду резистор, я подробности уже расписывал в уроке про светодиод на макетке. Кнопку вешают между пином и землёй — линию заранее готовят единицей, и нажатие прижимает её к нулю. А вот катушку реле или вход высокоуровневого релейного модуля слабый источник не вытянет — для этого нужен транзисторный ключ, к нему вернёмся в третьем уроке.
Отдельно про привычки с самой Arduino: подтяжка на входах Uno включается INPUT_PULLUP и тянет 20–50 мкА, у расширителя слабая подтяжка около 100 мкА — механика входов один в один, весь опыт с кнопками переносится без переделки. Разница прячется в выходе: пин Uno тянет и вверх, и вниз по 20–40 мА, а у расширителя сильна только нижняя половина — сток. Отсюда и вся обвязка «в минус», и транзисторы для всего остального.
| Параметр | PCF8574 | PCF8574A | Пин Uno |
|---|---|---|---|
| Адрес при A0–A2 на земле | 0x20 | 0x38 | нет |
| Диапазон адресов | 0x20–0x27 | 0x38–0x3F | нет |
| Сколько на одной шине | до 8 штук = 64 линии | то же | — |
| Ток: к земле / к питанию | 25 мА / около 100 мкА | то же | 20–40 мА в обе стороны |
| Что чип помнит после отключения | ничего: все линии снова HIGH | то же | ничего |
Программная сторона до смешного короткая: у расширителя нет ни регистров, ни команд — вся его жизнь это один байт. Хотите задать линии — Wire.beginTransmission(адрес), Wire.write(байт-маска), Wire.endTransmission(): единицы и нули байта разъезжаются по восьми пинам. Хотите прочитать — Wire.requestFrom(адрес, 1) и Wire.read(): вернётся байт, где каждый бит — состояние своей линии. Маска — это всё состояние чипа: ни конфигурации направлений, ни внутренних регистров у него нет, восемь бит — и всё.
С адресами ровно та же история, что с экраном: переходник LCD1602 — это тот же PCF8574 в обвязке, мы его уже видели на 0x27 в уроке про первый текст на LCD по I2C. Голая микросхема с A0–A2 на земле отвечает на 0x20, а версия с буквой А — на 0x38: в симуляторе сидит именно она. Джамперов на голой микросхеме нет — ножки просто втыкаются в макетку: единица — к питанию, ноль — на землю. Если на шине уже кто-то есть, поднимите одну из ножек адреса к питанию — чип сдвинется на соседний адрес. База та же, что в уроке про два провода на все устройства: адрес выбирается джамперами, шина терпит до восьми расширителей, то есть до 64 дополнительных линий.
Теперь соберём всё в один скетч: бегущий огонь по восьми линиям плюс опрос входов раз в цикл. Запись и чтение живут в нём бок о бок, как на настоящей панели.
#include <Wire.h> // PCF8574A с A0-A2 на земле: адрес 0x38 (голый PCF8574 ответил бы на 0x20)const byte ADDR = 0x38; void setup() { Wire.begin(); Serial.begin(9600); Serial.println("PCF8574: бегущий огонь + опрос входов");} byte t = 0; void loop() { // 1. Пишем маску: единица = линия отпущена, ноль = линия прижата к земле byte maska = 255 - (1 << t); // активный бит — нулём (LED «в минус») Wire.beginTransmission(ADDR); Wire.write(maska); Wire.endTransmission(); Serial.print("t="); Serial.print(t); Serial.print(" maska="); Serial.println(maska); delay(400); // 2. Читаем линии обратно Wire.requestFrom(ADDR, 1); if (Wire.available() > 0) { byte st = Wire.read(); Serial.print("vhod="); Serial.println(st); for (byte i = 0; i < 8; i++) { if (((st >> i) & 1) == 1) { // печатаем только единичные биты Serial.print("p"); Serial.print(i); Serial.println("=1"); } } } delay(400); t++; if (t > 7) t = 0;}Что видно на шине: читаем входы через requestFrom
Разбор прогона. Панель I2C на каждый шаг огня показывает START на 0x38, байт-маску и STOP, а раз в цикл — «Запрос 0x38: 1 байт». Чтение — это мастер даёт такт и вычитывает байт; сама механика запрос-ответ разобрана в уроке про две платы на одной шине, где слейв отвечал мастеру через onRequest(). На живой плате чип выдал бы текущее состояние линий; симуляторная заглушка честно признаётся, что она заглушка, и всегда отдаёт 42 — число 0b00101010. Раскладываем на биты: единицы на линиях 1, 3 и 5, нули — на 0, 2, 4, 6 и 7. На настоящей схеме с кнопками «в минус» нулевые биты и означали бы нажатые кнопки — этим раскладом займёмся в третьем уроке.
Кнопки — не единственный вход: на линию можно посадить геркон, концевик, выход датчика с открытым стоком. И есть приятная мелочь — ножка INT: как только состояние любого входа изменилось, она падает в LOW. Повесьте её на D2 и ловите прерыванием из урока про attachInterrupt — тогда опрашивать расширитель по кругу не нужно: шина молчит, пока кто-нибудь не нажмёт кнопку. Один нюанс: INT — открытый сток, подтяжку 4,7–10 кОм к питанию ставят снаружи, без неё мастер увидит мусор.
Ещё раз про честность симулятора: пощёлкать виртуальные кнопки на расширителе не получится, байт всегда 42. Но механика обмена — маска на запись, requestFrom на чтение, раскладка байта на биты — видна в панели I2C целиком, и она в точности та, что будет на живой плате.
Пара чисел для масштаба. Одна маска на запись — адрес плюс байт с подтверждениями, 18 бит шины, при 100 кГц это около 180 мкс; чтение — те же 18 бит. Даже восемь расширителей по кругу опрашиваются меньше чем за три миллисекунды — быстрее, чем дрожит палец на кнопке. Узким местом всегда будет ваша логика, а не шина: на скорости расширителя я не экономлю ни строчки.
Эксперименты для закрепления
Первое: посадите вторую микросхему на шину с подтянутой к питанию ножкой A0 — это адрес 0x39 — и устройте бегущий огонь, переливающийся с чипа на чип, по очереди восемь линий на каждом. Второе: инвертируйте огонь — пусть нулей станет семь, а единица одна, и эффект поползёт в обратную сторону. Третье: сократите паузы с 400 мс до 80 и посмотрите на панель I2C: посылки посыплются вплотную — на живой шине при 100 кГц каждая занимает около 700 мкс, и у плотного потока есть предел. Я однажды увлёкся и вывел паузы в ноль: на живой шине чипы стали пропускать подтверждения, и огонь заикался — вернул по сто миллисекунд, и всё устаканилось.
Четвёртое: замените бегущий огонь на дыхание — плавно наращивайте и сбрасывайте количество нулей в маске. И верните в закладки сканер: запустите его с этой же схемой и убедитесь, что расширитель отвечает на 0x38, — адресная дисциплина из урока про сканер I2C-адресов работает и для расширителей, а при двух чипах на шине без него вы будете гадать до вечера.
- PCF8574 добавляет 8 линий за два провода: на одной шине живут до 8 расширителей — до 64 линий
- Адреса: PCF8574 — 0x20–0x27, PCF8574A — 0x38–0x3F; ножки A0–A2 выбирают адрес внутри диапазона
- Ноль на линии — сильный сток до 25 мА; единица — слабая подтяжка около 100 мкА: светодиоды зажигают «в минус», реле — только через транзистор
- Запись: beginTransmission + write(маска) + endTransmission; чтение: requestFrom(адрес, 1) + available() + read()
- Ножка INT падает в LOW при изменении входов — кнопки можно ловить прерыванием, а не опросом
- 1. После подачи питания на PCF8574 все восемь линий:
- 2. Сколько тока линия расширителя выдаёт в состоянии HIGH?
- 3. Какой адрес у голого PCF8574 (без буквы А), если A0–A2 на земле?
- 4. Светодиод подключён: +5 В → резистор 220 Ом → светодиод → пин P3. Какая посылка зажжёт его?
- 5. Зачем расширителю ножка INT?
Частые вопросы по теме урока
- После подачи питания на PCF8574 все восемь линий:
- Даташит: после включения все линии в HIGH — это слабая подтяжка, поэтому входы и «минусовые» светодиоды готовят заранее записанной единицей. в HIGH через слабую подтяжку
- Сколько тока линия расширителя выдаёт в состоянии HIGH?
- Единица — это слабая подтяжка порядка 100 мкА; сильный только сток: ноль до 25 мА на пин. около 100 мкА
- Какой адрес у голого PCF8574 (без буквы А), если A0–A2 на земле?
- PCF8574 занимает 0x20–0x27, PCF8574A — 0x38–0x3F. Все ножки адреса на земле — нижняя граница диапазона. 0x20
- Светодиод подключён: +5 В → резистор 220 Ом → светодиод → пин P3. Какая посылка зажжёт его?
- Светодиод горит, когда пин прижат к земле: записанный ноль — сильный сток. Единица только подтянет линию слабым током. маска 0b11110111 — ноль в бите 3
- Зачем расширителю ножка INT?
- INT — открытый сток: при любом изменении входов линия падает в LOW, и мастер узнаёт о кнопке без постоянного опроса. сигнализировать об изменении входов — падает в LOW