Каталог товаров
Клиенту
Тема сайта:
+38 (066) 305-77-25
Наш адрес
Україна, времено - только доставка Новой Почтой, УкрПочтой, МистЕкспрес, ROZETKA Delivery
Телефоны:
Время работы
  • Пн-Пт: с 9 до 18
  • Сб.: с 10 до 17
  • Вс: с 11 до 16
E-mail
Мы в соцсетях
Перейти в контакты
0 0
Каталог
Главная
Закладки
0
Сравнить
0
Контакты

PCF8574 без магии: как работают квазидвунаправленные входы-выходы и что должно правильно делать Arduino-библиотека

Чи була корисною ця стаття?
PCF8574 без магии: как работают квазидвунаправленные входы-выходы и что должно правильно делать Arduino-библиотека

I²C · GPIO · Arduino

PCF8574 без магии: как работают квазидвунаправленные входы-выходы и что должна правильно делать Arduino-библиотека

У этой микросхемы нет привычного регистра направления и нет симметричного push-pull HIGH. Поэтому самое важное слово в её API — не «включить», а «отпустить».

PCF8574 между шиной I²C и восемью линиями, для которых HIGH означает отпущенное состояние
PCF8574 переводит команды I²C не в привычные режимы GPIO, а в восемь квазидвунаправленных линий со своей электрической логикой.

PCF8574 часто появляется в мастерской как маленькая плата, обещающая «ещё восемь GPIO всего двумя проводами». Формально — да. Практически — эти GPIO ведут себя так, будто в договоре мелким шрифтом написано: «HIGH предоставляется на особых условиях». Если их не прочитать, кнопка вдруг перестаёт быть входом, байт 0xFF превращается в «ошибку», а невинный старт библиотеки включает все active-low нагрузки.

Содержание статьи
  1. Один latch-бит вместо INPUT и push-pull HIGH
  2. Какое состояние должна хранить библиотека
  3. Адреса и правильная проверка ACK
  4. Что реально делает jm_PCF8574 v2.0.0
  5. Нагрузка и конфликт выходов
  6. LCD-backpack без универсального pinout
  7. Почему 4,7 кОм не универсально
  8. Чек-лист правильной библиотеки
  9. Технические источники

1. Один latch-бит вместо INPUT и push-pull HIGH

У обычного GPIO микроконтроллера мы ожидаем отдельный выбор направления и выходной latch. PCF8574 устроена иначе: каждая P-линия квазидвунаправленная, а доступного программе регистра направления нет. После power-on I/O находятся в HIGH/released состоянии; записанный бит непосредственно определяет, активно ли притягивание LOW.[1][2]

Что означает записанный бит для одного вывода Pn
Latch-бит Физическое действие PCF8574 Что может прочитать MCU Практическая трактовка
0 Выходной транзистор активно притягивает Pn к LOW. Физический LOW, пока нагрузка не выведет узел за нормированные условия. Активный LOW-выход.
1 Сильный LOW-драйвер отключён; остаётся слабый источник тока к питанию. HIGH без внешнего притягивания или LOW, если внешняя схема притянула Pn. Released HIGH или вход.
Два состояния квазидвунаправленной ячейки PCF8574: активный LOW при latch 0 и отпущенная линия со слабым источником при latch 1
Главная модель: 0 активно тянет вниз, 1 отпускает сильный LOW-драйвер и позволяет внешнему сигналу определить уровень.

HIGH здесь не push-pull

Когда в Pn записан 1, PCF8574 не включает обычный сильный high-side выход. Статический HIGH поддерживается слабым внутренним источником тока. NXP описывает его как источник примерно 100 мкА, а TI на функциональной схеме также обозначает 100 мкА, но в таблице электрических характеристик задаёт диапазон тока при конкретных условиях. Поэтому превращать этот узел в «внутренний резистор 100 кОм» не следует: даташит нормирует источник тока, а не постоянное линейное сопротивление.[1][2]

Во время записи HIGH дополнительно включается более сильное кратковременное подтягивание. Оно помогает быстрее зарядить ёмкость линии и поднять фронт до HIGH, после чего отключается по фазе SCL. Это стартовый толчок, а не постоянный push-pull HIGH и точно не ускоритель перехода к LOW.[2]

Правильная короткая фраза: digitalWrite(Pn, HIGH) для PCF8574 означает прежде всего «прекрати активно притягивать Pn». Вывод становится пригодным как вход, но не превращается в полностью отключённый CMOS-вход.

Рабочие параметры — не то же самое, что Absolute Maximum

PCF8574 значительно сильнее поглощает ток в LOW, чем отдаёт его в HIGH. Но цифры из даташита живут в разных «ящиках». Electrical Characteristics описывают гарантированные или типовые точки при заданных условиях. Recommended Operating Conditions задают область штатной работы. Absolute Maximum Ratings — это стрессовые пределы, превышение которых может повредить микросхему; они не являются рецептом проектирования.[2]

Например, у TI Rev. K 50 мА continuous output-low current приведены именно среди Absolute Maximum Ratings, тогда как рекомендуемый предел IOL достигает 25 мА. И это ещё не обещание «по 25 мА с каждого из восьми выводов»: надо одновременно проверять VOL при нужном токе, суммарный ток корпуса, питание и температуру.[2]

2. Какое состояние должна хранить библиотека

Ключевое правило даташита сформулировано без мистики: перед использованием Pn как входа этот I/O должен быть HIGH. В терминах latch это означает: сначала записать 1, потом читать. Если бит остался 0, микросхема сама удерживает линию LOW, и внешний переключатель не получит права голоса.[1][2]

Отсюда вытекает требование к библиотеке: она не должна смешивать три разные сущности.

  • output_latch — байт, который библиотека хочет записать в PCF8574;
  • input_sample — байт физических уровней, только что прочитанный с P0–P7;
  • status — результат I²C-транзакции.
Архитектура Arduino-библиотеки с отдельными output latch, input sample, input mask и статусом I²C
Безопасная библиотечная модель не позволяет физически прочитанному LOW незаметно превратиться в новую команду активно тянуть линию вниз.

Почему read-modify-write может испортить вход

Представим, что P0 отпущен записанной единицей, а кнопка снаружи притянула его к LOW. Чтение справедливо возвращает 0 для P0. Если библиотека сохранит этот sample как «тень выхода», затем изменит только P7 и запишет весь байт назад, P0 получит latch=0. Кнопку уже можно отпустить, но PCF8574 продолжит сама держать P0 в LOW. Это не прихоть I²C — это следствие потери разницы между командой и наблюдением.[1][2]

Эскиз корректной семантики API

Это не код конкретной библиотеки, а контракт, который не противоречит электрике:

bool releaseInput(uint8_t pin);       // записывает 1 в latch
bool writePin(uint8_t pin, bool low); // изменяет output_latch
bool readPort(uint8_t& sample);       // data отдельно, success отдельно
bool probeAddress();                  // address-only ACK probe

// Перед каждой полной записью:
byte safe = output_latch | input_mask;

Название метода может быть другим. Важна не косметика, а гарантия: переход к входу физически записывает 1, а ошибка шины не маскируется под один из 256 вполне законных байтов.

3. Адреса и правильная проверка ACK

PCF8574 и PCF8574A имеют разные диапазоны

Arduino Wire принимает 7-битный адрес устройства. Для PCF8574 три входа A2:A0 дают диапазон 0x20–0x27; для PCF8574A — 0x38–0x3F. Следовательно, 0x27 не означает «A-версия»: это PCF8574 со всеми тремя адресными входами HIGH. Маркировка микросхемы и реальные перемычки платы надёжнее, чем «типовой адрес» из чужого скетча.[1]

7-битные адреса, которые следует передавать Arduino Wire
Вариант A2:A0 = 000 A2:A0 = 111 Полный диапазон
PCF8574 0x20 0x27 0x20–0x27
PCF8574A 0x38 0x3F 0x38–0x3F

Для практики можно перейти к проверенному в каталоге модулю расширения портов PCF8574. Это внутренняя товарная ссылка, а не доказательство адреса или токовых пределов конкретного экземпляра.

beginTransmission() не отвечает на вопрос «устройство есть?»

Wire.beginTransmission(address) лишь подготавливает локальный TX-буфер и адрес. Официальный Arduino API возвращает nothing, а в AVR core реальная сигнатура — void TwoWire::beginTransmission(uint8_t address). Поэтому конструкция if (Wire.beginTransmission(...)) не проверяет ACK и вообще не является корректным использованием API.[3][5]

Результат передачи возвращает Wire.endTransmission(): код 0 означает успех, 2 — NACK адреса, 3 — NACK данных; другие коды описывают другие ошибки реализации. Для проверки только ответа по адресу не добавляем ни одного data byte.[4][5]

bool i2cAddressAck(TwoWire& bus, uint8_t address)
{
  bus.beginTransmission(address);
  return bus.endTransmission() == 0;
}
ACK не является паспортом устройства. Address-only probe подтверждает, что кто-то ответил по этому адресу. Он не доказывает, что это именно PCF8574, не определяет ревизию платы и не проверяет исправность всех P0–P7.

Почему инициализационный 0x00 опасен

Запись 0x00 — не нейтральный ping. Он приказывает всем восьми P-линиям активно перейти в LOW. На неизвестной плате это может включить active-low нагрузки, переключить управляющие сигналы LCD или создать конфликт с внешним драйвером. Если нужен только ACK, TX-буфер должен оставаться пустым.[1][2][5]

На противоположном конце байтовой шкалы 0xFF тоже нельзя резервировать как универсальную «ошибку чтения». Это вполне корректное физическое состояние: все восемь линий прочитаны HIGH. Данные и статус должны идти отдельными каналами.[1][8]

4. Что реально делает jm_PCF8574 v2.0.0

Рассмотрим не воображаемую «правильную библиотеку», а конкретный tagged release. jm_PCF8574 v2.0.0 опубликован 4 июля 2025 года; release описывает переработанный API и выбор I²C bus.[6] Ниже разбираем фактический код этого релиза.

Реальные публичные сигнатуры

В tagged header v2.0.0 объявлены именно такие основные конструкторы и методы:[7]

jm_PCF8574();
jm_PCF8574(byte i2c_address);
jm_PCF8574(byte i2c_address, TwoWire & wire);

operator bool();

byte i2c_address();
bool connected();

bool begin();
bool begin(byte port_mode, byte port_shadow);
bool begin(byte i2c_address);
bool begin(byte i2c_address, byte port_mode, byte port_shadow);
bool end();

byte port_input();
void port_output(byte value);
void port_output(const byte *data, size_t quantity);

void pinMode(byte pin, byte mode);
int digitalRead(byte pin);
void digitalWrite(byte pin, byte value);

Header также содержит wire(), port_mode(), port_shadow(), pin-level helper-ы и wait(). Важнее другое: overload-а begin(TwoWire*) здесь нет.

Произвольный TwoWire передают в конструктор

Выбор Wire, Wire1 или другого объекта, унаследованного/совместимого с TwoWire, реализован ссылкой в конструкторе: jm_PCF8574(byte, TwoWire&). Выбранный объект должен существовать в конкретном Arduino core, а пользователь должен сам инициализировать шину способом, предусмотренным этим core. Библиотека хранит TwoWire& и выполняет транзакции через него.[7][8]

// После инициализации Wire1 по правилам вашего Arduino core:
jm_PCF8574 io(0x20, Wire1);
const bool started = io.begin();

begin() изменяет порт, а не только проверяет адрес

Типовой begin() стартует с режимом all-input/released и тенью 0xFF. Внутренний _begin() формирует байт порта, записывает его устройству и устанавливает успех по endTransmission()==0. Это конфигурационная запись, хотя и более безопасная, чем 0x00; чистым address-only probe он не является. Если _connected уже true, публичный begin(...) возвращает false, а end() только сбрасывает внутренний флаг и не вызывает Wire.end().[8]

connected() и operator bool() — кеш, не live probe

connected() просто возвращает _connected; operator bool() делегирует этому же методу. Следовательно, обе формы сообщают последнее известное библиотеке состояние. Если кабель сняли после успешной операции, флаг не узнает об этом телепатически — нужна следующая I²C-транзакция. И наоборот, повторный begin() не является удобным health check, потому что при _connected=true он возвращает false.[7][8]

Где v2.0.0 теряет грань между latch и sample

При успешном port_input() реализация читает P-порт и записывает полученный байт в _port_shadow. При ошибке или disconnected она возвращает текущий _port_shadow, а не гарантированный 0xFF. Поэтому byte port_input() не имеет отдельного канала ошибки; любой возвращённый байт может быть и допустимым значением состояния.[8]

Ещё острее: pinMode(INPUT) в v2.0.0 изменяет только _port_mode в RAM и не выполняет I²C-записи, которая физически отпустила бы pin единицей. А после чтения внешний LOW уже может оказаться в _port_shadow; следующая бит-операция строит запись на этой тени. Именно так подтверждённая структура source создаёт риск опасного read-modify-write для released input bits.[8]

Граница вывода: это анализ фактического кода v2.0.0. Он показывает архитектуру и API релиза, но совместимость с конкретной комбинацией MCU, платы и I²C-реализации стоит проверять отдельно.

5. Нагрузка и конфликт выходов

Кнопка: released input, а не «плавающий pin»

Типичная кнопка между Pn и GND работает естественно: библиотека записывает в Pn единицу, слабый источник поддерживает HIGH, нажатие притягивает линию к LOW. Задержка может понадобиться для debounce механических контактов, но универсальная delay(1) не вытекает из даташита и не «лечит floating input» — released вход уже имеет слабый источник тока.[1][2]

Светодиод: active-low часто естественнее

Из-за асимметрии токов светодиод с резистором часто подключают так, чтобы PCF8574 поглощала ток в состоянии LOW. Но номинал резистора, допустимый VOL, суммарный ток и условия даташита всё равно надо рассчитать. Релейную катушку не следует подключать непосредственно: нужны соответствующий транзистор или драйвер, flyback path и проверка тока и тепла.[2]

MCU GPIO и P7 нельзя просто соединить «для PWM»

Аппаратный PWM/LEDC микроконтроллера возникает на GPIO самого MCU, а не внутри PCF8574. Чтобы он повлиял на сеть подсветки, нужен физический путь. Если просто соединить push-pull GPIO MCU с P7 и P7 когда-то получит latch=0, MCU может подать HIGH навстречу активному LOW-драйверу расширителя. Это конфликт выходов с потенциально чрезмерным током, а не способ «передать PWM через I²C». Электрическая структура P-порта не даёт оснований считать такое прямое объединение безопасным.[1][2]

Безопасное направление: управлять отдельным входом транзисторного драйвера, разорвать или изолировать связь с P7 по проверенной схеме либо спроектировать open-drain/wired-AND узел с анализом всех состояний, включая старт и reset. Без схемы конкретного backpack универсальной инструкции по переделке нет.

6. LCD-backpack без универсального pinout

PCF8574 знает только P0–P7. Она не знает, что рядом припаян HD44780-compatible дисплей. Соответствие P-битов сигналам RS, R/W, E, D4–D7 и подсветке определяет плата адаптера, а не даташит расширителя.

Конфигурируемый New-LiquidCrystal прямо принимает номера En, Rw, Rs, D4–D7, pin подсветки и его полярность; в репозитории есть пример с одной конкретной раскладкой. Это демонстрирует гибкость software-модели, но не доказывает pinout неизвестной физической платы.[10][11]

Варианты LCD-backpack: разные соответствия P0–P7, управляемый или недоступный R/W и разная полярность транзистора подсветки
Для LCD-backpack универсальны названия сигналов HD44780, но не их привязка к P0–P7 и не полярность каскада подсветки.

R/W: управляемый или зафиксированный в режиме записи

Контроллер HD44780U имеет сигнал R/W, циклы чтения и busy flag.[9] Конкретный backpack может провести R/W к одному из P-битов или сделать его недоступным программе, зафиксировав режим записи. Во втором случае библиотека не читает busy flag и должна выдерживать документированные задержки. Какой вариант перед нами, показывают схема, трассировка или continuity test, а не адрес 0x27.

Подсветка: pin и полярность зависят от платы

Каскад подсветки может управляться отдельным P-битом через транзистор, иметь другую инверсию или вообще не предоставлять программного управления. То, что библиотека просит параметры backlight pin и polarity, — полезное напоминание: «active LOW всегда» здесь не существует.[10]

В каталоге MyProject есть адаптер I²C для LCD1602/LCD2004 и LCD1602 с I²C-модулем. Эти страницы полезны для выбора форм-фактора, но без схемы и точной ревизии не устанавливают P0–P7 map, R/W или полярность подсветки.

Тайминги HD44780 зависят от условий

Один магический набор задержек для всех HD44780-compatible контроллеров — плохая инженерная привычка. В оригинальном HD44780U минимальная ширина E HIGH составляет 450 нс при VCC=2,7–4,5 В и 230 нс при 4,5–5,5 В; минимальный enable cycle — соответственно 1000 нс и 500 нс. Стартовая последовательность и время выполнения команд также зависят от питания и частоты внутреннего генератора; clear display и return home значительно дольше обычных операций.[9]

Примеры условных, а не универсальных таймингов HD44780U
Параметр VCC 2,7–4,5 В VCC 4,5–5,5 В Практический вывод
E HIGH, минимум 450 нс 230 нс Не отрывать число от диапазона питания.
Enable cycle, минимум 1000 нс 500 нс Каждый nibble требует корректного E-strobe.
Завершение команды Зависит от команды и oscillator Зависит от команды и oscillator delayMicroseconds(40) не является универсальным для clear/home.

7. Почему 4,7 кОм не универсальное I²C-подтягивание

В шине I²C линии SDA и SCL формируют HIGH через pull-up, а устройства тянут их к LOW открытым стоком/коллектором. Значение резистора должно попасть в окно между двумя ограничениями: слишком малое сопротивление создаёт слишком большой sink current и мешает гарантированному LOW; слишком большое вместе с ёмкостью шины делает фронт HIGH запаздывающим.[13]

Rp(min) = (VCC − VOL(max)) / IOL   ·   Rp(max) = tr / (0,8473 · Cb)

TI SLVA689 выводит нижнюю границу из VCC, допустимого VOL(max) и sink current, а верхнюю — из разрешённого rise time tr и ёмкости шины Cb.[12] Поэтому 4,7 кОм может быть хорошим значением в конкретной схеме, но не является универсальной константой природы.

Окно допустимого сопротивления I²C pull-up между границей тока LOW и границей времени нарастания через ёмкость шины
Допустимый pull-up лежит между токовой нижней границей и временной верхней; готовые модули могут уже содержать параллельные резисторы.

Отдельная ловушка модульных макетов — несколько готовых плат с собственными pull-up. Параллельные 4,7 кОм дают меньшее эквивалентное сопротивление, так что считать надо всю шину, её длину, ёмкость, напряжение и выбранный режим.[12] Если нужно введение в ACK, адреса и физики самой шины, пригодится материал «I2C, SPI, UART — что это такое и как работают интерфейсы микросхем».

8. Чек-лист правильной Arduino-библиотеки

  1. Не выдумывает direction register. API объясняет released input и не обещает обычный push-pull HIGH.
  2. Физически отпускает вход. Переход к input записывает 1 в соответствующий latch-бит.
  3. Разделяет состояния. Output latch, input sample, input mask и I²C status не живут в одном байте.
  4. Не резервирует 0xFF как ошибку. Данные возвращаются отдельно от success/error.
  5. Проверяет ACK через endTransmission(). beginTransmission() не используется как bool.
  6. Имеет безопасный probe. Address-only проверка не добавляет Wire.write(0x00) и честно говорит, что ACK не идентифицирует модель.
  7. Не называет кеш живой проверкой. connected() или аналог документирует момент обновления состояния.
  8. Принимает нужный TwoWire реальным API. Пользователь видит, кто и когда инициализирует шину.
  9. Не привязывает LCD к одному pinout. Карта сигналов и полярность подсветки конфигурируются или подтверждаются схемой конкретной платы.
  10. Документирует стартовый байт. Никакого «безопасного» 0x00 без анализа нагрузок.
  11. Различает рабочие и аварийные пределы. Absolute Maximum не рекламируется как штатный ток.
  12. Не подменяет PWM. Частые I²C-записи не называются hardware PWM, а общая сеть MCU/PCF не допускается без изоляции.

Краткие ответы на типовые вопросы

Можно ли прочитать LOW, который сама PCF8574 выставила?
Да. Чтение возвращает физический уровень P-порта. Просто отдельного читаемого регистра latch, который объяснил бы причину LOW, нет.[1][2]
Достаточно ли записать 0xFF один раз?
Для всех released inputs — это правильный старт. Но каждая следующая полная запись должна сохранять единицы в input-битах; иначе библиотека снова превратит их в активные LOW.
Можно ли делать PWM частыми записями по I²C?
Программное переключение возможно, но это не timer-backed PWM: частота, jitter и загруженность шины зависят от I²C-транзакций и Arduino core. Без измерений конкретной системы не следует обещать отсутствие мерцания.[1][2] Для основ PWM смотрите отдельный материал о ШИМ.
Где почитать про VIH, VIL, VOH и VOL?
В статье «Тайны логических уровней» — она объясняет, почему логическое слово HIGH не заменяет электрические условия даташита.
Финальная мысль: PCF8574 не «странная». Она последовательна — просто её единица означает отпущенную линию со слабым источником тока. Магия начинается лишь тогда, когда библиотека скрывает эту разницу вместо того, чтобы аккуратно её моделировать.

Технические источники

  1. NXP Semiconductors — “PCF8574; PCF8574A Remote 8-bit I/O expander for I²C-bus with interrupt” — Rev. 5, 27 May 2013.Квазидвунаправленный порт, слабый источник тока, power-on state, чтение/запись и адреса обоих вариантов.
  2. Texas Instruments — “PCF8574 Remote 8-Bit I/O Expander for I2C Bus” — SCPS068K, Rev. K, September 2024.Функциональная схема P-порта, статическое и transient подтягивание, I/O currents и классы электрических пределов.
  3. Arduino — “Wire.beginTransmission()” — revision 24.01.2026.Синтаксис, 7-битный адрес и отсутствие return value.
  4. Arduino — “Wire.endTransmission()” — revision 24.01.2026.Завершение транзакции и коды success/NACK/error.
  5. Arduino — “ArduinoCore-avr / Wire.cpp” — master.Сигнатура void beginTransmission, TX-буфер и address-only передача в AVR Wire.
  6. Jean-Marc Paratte — “jm_PCF8574 v2.0.0” release — 4 July 2025, commit 53cfd20.Существование релиза, API refactor и selectable I²C bus.
  7. Jean-Marc Paratte — “src/jm_PCF8574.h” — v2.0.0, commit 53cfd20.Реальные constructors, TwoWire&, operator bool, connected, begin/end и port/pin API.
  8. Jean-Marc Paratte — “src/jm_PCF8574.cpp” — v2.0.0, commit 53cfd20.Реальное поведение begin/end, _connected, port input/output, _port_shadow и pinMode.
  9. Hitachi — “HD44780U (LCD-II) Dot Matrix Liquid Crystal Display Controller/Driver” — доступное зеркало SparkFun.RS/RW/E, busy flag, 4-bit initialization, execution times и зависящие от VCC bus timings.
  10. Francisco Malpartida — “New-LiquidCrystal / LiquidCrystal_I2C.h” — master.Конфигурируемые En/Rw/Rs/D4–D7, backlight pin и polarity; это software API, не схема конкретной платы.
  11. Francisco Malpartida / contributor — “HelloWorld_byVac.ino” — repository master.Пример конкретно настроенной LCD map; не идентифицирует все физические ревизии backpack.
  12. Texas Instruments, Rajan Arora — “I2C Bus Pullup Resistor Calculation” — SLVA689, February 2015.Формулы Rp(min), Rp(max) и зависимость от тока, rise time и bus capacitance.
  13. NXP Semiconductors — “UM10204 I²C-bus specification and user manual” — Rev. 7.0, 1 October 2021.Open-drain структура шины, pull-up, rise-time и bus-capacitance requirements.

Материал опирается на документацию производителей, официальную Arduino-документацию и tagged source библиотеки. 
© 2026 Мій Проект. Автор: Jazzzman. Використання матеріалів дозволено лише з посиланням на джерело.

Рекомендуемые товары
Похожие статьи
Таємниці логічних рівнів
Таємниці логічних рівнів
Блог, Электроника для начинающих
25 мая 2025
I2C, SPI, UART - что это такое и как работают интерфейсы микросхем
I2C, SPI, UART - что это такое и как работают интерфейсы микросхем
Блог, Электроника для начинающих
26 мая 2025
Переводчики реальности: Как АЦП и ЦАП соединяют наш мир с цифровым
Переводчики реальности: Как АЦП и ЦАП соединяют наш мир с цифровым
Блог, Электроника для начинающих
25 декабря 2025

Продовжимо солов'їною?

Оберіть мову