Картинка BRGFX, Freepik

Прямо сейчас занимаюсь достаточно интересным проектом, который задействует большое количество пинов микроконтроллера, и, наверное, впервые количество требующихся пинов превзошло количество имеющихся у микроконтроллера. Так что же делать в таком случае?

Решил я это всё своеобразным образом, который и описан ниже. Возможно, кому-то ещё будет интересно и полезно...

Дело в том, что этот проект я строю на базе esp32 ввиду того, что данные микроконтроллеры у меня валяются по дому коробками — и это не фигура речи, а буквально так.

Однако проект требует достаточно большого количества подключаемых устройств — восемь цифровых датчиков Холла (KY-003), один аналоговый датчик расстояния (тоже Холла, KY-035), три шаговых двигателя Nema 17, один коллекторный двигатель.

Дело с двигателями осложняется также тем, что каждый из шаговых двигателей требует по три контакта (pulse, direction, enable), так как мне пришлось вынужденно использовать драйвер ТВ6600. Хотел изначально использовать также имеющиеся в наличии L298N, однако они не потянули шаговые двигатели Nema 17 — работали на пределе и раскалялись буквально за 6 секунд работы с последующим температурным отказом.

Да, многие хают ТВ6600, и советуют выбирать более профессиональные версии, тем не менее его цена в моём случае решает: 500 руб. против 2500 руб. Да и задача у меня не такая уж сложная и не требующая круглосуточной работы, наподобие фрезерования 3D-картин, так что мне сойдёт…

Итак, у нас проблема — не хватает пинов (весьма наглядное описание пинов есть здесь). Как её решать? Наверное, можно поискать какую-либо плату расширения, допускающую подключение большого количества периферии — например, у меня с незапамятных времён лежит такая для Arduino:


Но что-то не захотелось мне с этим возиться и разбираться, и пришла в голову мысль сделать всё гораздо более простым способом: с помощью соединения микроконтроллеров через шину I2C.

Природная лень заставила меня поискать готовые решения, однако здесь пришлось натолкнуться на практически полное их отсутствие (связь двух и более esp32 между собой), таким образом, «беглый гуглинг» практически ничего не дал (что, однако, не исключает их наличия где-либо — это говорит только о том, что я их не нашёл). Ну ок, начинаем вникать…

Шина I2C требует для реализации связи всего двух проводов: SCL (Serial Clock) — сигнал тактирования и SDA (Serial Data) — передаваемые данные (но, вообще говоря, в реальности больше, так как требуется ещё как минимум заземление, а ещё желательно и питание).

Эта шина относительно низкоскоростная (стандартно — 100 кбит/сек, на современных устройствах — до 400 кбит/сек), тем не менее она полностью удовлетворяет моим требованиям, так как рамки моего проекта не требуют передачи каких-то тяжёлых данных.

Работа в рамках I2C подразумевает, что любое подключённое к этим линиям (SCL, SDA) устройство может занимать одну из двух позиций: ведущую (master) или ведомую (slave). Обычно, говоря об этой шине, подразумевают работу в ней одного master-a и ряда slave-ов, хотя не запрещается и работа нескольких master-ов (насколько я понял, реализовано это через использующуюся master-ом функцию endTransmission(), которая, если вызывается с параметром true (endTransmission(true)), после передачи сообщения посылает стоп-сообщение, освобождая линию I2C, и она по сути свободна для использования каким угодно ещё master-ом).

Общение между устройствами в рамках этой шины всегда инициирует master, вызывая соответствующее ведомое устройство, обращаясь по его адресу, который является семибитным, и в сети одновременно могут присутствовать 128 устройств с адресами, обозначенными числами от 0 до 127, где 0 согласно спецификации — адрес общего вызова, а ведомым можно назначать адреса от 1 до 127 (это последнее уже не по спецификации, а «по факту», если использовать библиотеку wire). Хотя на самом деле всё несколько сложнее, и если строго следовать спецификации (ссылка на которую выше), то это оставляет нам только 112 адресов:


Но если вы сами проектируете систему и вообще «правила не для вас», то 127. И, вроде как при сильном желании даже 128 :-) Но тут сделаю ремарку: используя библиотеку wire, я пытался обращаться на нулевой адрес. С «нулевым» же результатом. Видимо, если только самому библиотеку писать, так как люди вроде как используют и его… Но тут «дальше мои полномочия всё»… :-0)

И кстати говоря, почему стандарт такой странный — семибитный? А вот почему, всё оттуда же, из спецификации:


А почему 127 устройств? Для этого нам всего лишь нужно открыть стандартный калькулятор Windows и переключить его в режим «программист»:


Затем мы переключимся в десятичный режим (показано ниже стрелкой слева) и забьём число 127. После чего мы увидим в битовом выражении наверху справа, что число 127 для компьютера кодируется семью знаками:


Теперь, если мы попробуем стереть это число и забить туда 128, то увидим наверху справа, что в битовом выражении это число кодируется уже восемью знаками. А наша шина — семибитовая (т. е. можно сказать семизнаковая), и этот адрес просто не поместится в рамках её стандарта (тут следует сделать оговорку — возможно, это не шина семибитовая, а библиотека wire, но я глубже не копал, честно признаюсь):


Кстати говоря, это подробное описание я привёл не просто так — оно может быть полезно для тех, кто не дружит с шестнадцатеричной системой счисления (потому что в стандартных примерах библиотеки wire.h показано использование шестнадцатеричных чисел в качестве адресов).

То есть если вы захотите, так же как и я, увеличить количество пинов, соединив между собой одну или более плат esp32, то вам понадобится назначить каждой из них адрес в рамках этой шины (всем, кроме master-устройства(в), так как, насколько я понимаю, им система неявно сама выдаёт master-адрес при инициализации).

Делается это крайне просто:

  1. Запускаете калькулятор Windows.
  2. Переключаетесь в десятичный режим (Dec).
  3. Вбиваете любое число от 1 до 127.
  4. Переключаетесь в шестнадцатеричный режим (Hex).
  5. Копируете получившееся число из окошка наверху и вставляете с припиской «0х...» в свой код (например, 0х7F).


И это всё! Теперь, когда у вашего ведомого устройства есть адрес, к нему легко может обратиться ведущее…

Кстати говоря, это (программное назначение адреса) касается только моего случая, так как, насколько мне известно, имеющиеся в продаже устройства (I2C ЖК-экраны и т. д.) уже содержат жёстко прошитые I2C-адреса (у некоторых их можно менять перемычкой).

Что ещё нам нужно знать о шине I2C? Один из самых важных моментов состоит в том, что она требует для своей работы подтягивающие к питанию (pull-up) резисторы:

Картинка: randomnerdtutorials.com

Об этом даже упоминает и производитель esp32:


Хорошая статья на тему, что такое подтягивающие резисторы, и зачем они нужны для esp32, есть вот здесь.

Тем не менее, насколько я знаю, ряд пинов esp32 уже содержит подтягивающие резисторы. Обратимся к документации производителя esp32:


И мы увидим, что если явным образом программно не определено, пины, имеющие подтягивающие резисторы (45 кОм при подтягивании к питанию, и такой же на 45 кОм при подтягивании к земле — это я уже в даташите глянул), находятся в высокоимпедансном состоянии. Разбираемся, что это за состояние… Вот что нам говорит Вики по этому поводу:

Высокоимпедансное состояние, высокоомное состояние, Z-состояние или состояние «Выключено» — состояние выхода цифровой микросхемы, при котором он «отключается» от сигнальной шины, к которой подключены несколько передатчиков сигналов. Таким образом, сопротивление между её внутренней схемой, формирующей выходной сигнал, и внешней схемой, очень большое. Вывод микросхемы, переведённый в состояние «Выключено», ведёт себя как не подключённый к ней. Внешние устройства (микросхемы), подключённые к этому выводу, могут изменять напряжение на нём по своему усмотрению (в некоторых рамках), не влияя на работу микросхемы. И наоборот — схема не мешает внешним устройствам менять напряжение на выводе микросхемы.

И ещё: Состояние «Выключено» применяется, когда устройству приходится временно отключаться от шины — например, в программаторах, мультиплексорах, многоточечных интерфейсах передачи данных наподобие JTAG, I2C или USB и т. д.

То есть получается, что такая «подтяжка» важна для работы в рамках шины I2C. А как её реализовать, учитывая, что у нас вроде как уже есть резисторы на борту платы?

Реализуется это с помощью функции pinMode. Например, так:

pinMode (21, INPUT_PULLUP)

pinMode (22, INPUT_PULLUP)

Хорошая статья про функции подтягивания есть здесь. В ходе экспериментов пробовал вешать эти функции и на master, и на slave (несмотря на то, что это не совсем входы, а двунаправленное общение как бы). Разницы не увидел никакой.

Приходилось видеть рассуждения в сети, что на пинах интерфейса I2C такая подтяжка (программная) не работает, и при желании использовать именно внутренние подтягивающие резисторы люди включали подтягивание на других пинах и соединяли их с I2C-пинами с помощью перемычек. Забегая вперёд – у меня всё хорошо работает и без этого (возможно, это связано с тем, что у меня весьма малое расстояние – всего 30 см между платами). Или же оно реализовано аппаратно платой (на пинах, определяемых для I2C).

По крайней мере, из того, что мне удалось выяснить:

  1. Если расстояния между устройствами малы, то особого смысла в подтягивающих резисторах нет, и все устройства используют один и тот же источник питания.
  2. Внутренние встроенные в плату pull-up резисторы слишком большого номинала. Да, их можно использовать, но это не позволит достаточно быстро осуществлять подтягивание: «Передача/Приём сигналов осуществляется прижиманием линии в 0, в единичку устанавливается сама за счёт подтягивающих резисторов. Их ставить обязательно всегда! Стандарт! Резисторы на 10к оптимальны. Чем больше резистор, тем дольше линия восстанавливается в единицу (идёт перезаряд паразитной ёмкости между проводами), и тем сильней заваливаются фронты импульсов, а значит скорость передачи падает. Именно поэтому у I2C скорость передачи намного ниже, чем у SPI. Обычно IIC работает либо на скорости 10кбит/с — в медленном режиме, либо на 100кбит/с в быстром. Но в реальности можно плавно менять скорость вплоть до нуля.»

В любом случае, было бы интересно прочитать в комментах, если вы знаете что-то больше по этому вопросу…

Мы же рискнём соединить платы просто напрямую. Теперь — куда нам необходимо подключаться?

Если мы посмотрим на распиновку, то увидим, что интерфейс расположен на 22 (SCL) и 21 (SDA) пинах:

Картинка: circuits4you.com

И именно сюда же и предлагает подключаться производитель:

Картинка: docs.espressif.com

Тем не менее, плата поддерживает два таких интерфейса, они могут быть конфигурированы на любом из универсальных портов ввода-вывода.

Для работы с этой шиной будем использовать стандартную библиотеку wire.h.

Здесь есть один любопытный момент, который касается назначения пинов под I2C: если мы обратимся к описанию библиотеки wire.h, на сайте Arduino, то среди функций этой библиотеки
мы не увидим важнейших, которые мы могли бы использовать (о них ниже).


И даже на сайте espressif упоминается о возможности назначения любых пинов, только для master-устройств:


Приходилось видеть и другой вариант, но также для master-устройств, для назначения на любые пины, например 18-19:

Wire.begin(18,19)

Тем не менее, существует и ещё один вариант, который гораздо более универсален и подходит как для master-a, так и для slave-a — мне он кажется самым оптимальным и нравится больше всего:


Итак, попробуем соединить платы, как было описано выше, кроме того, соединим у обеих плат GND и Vin (пробовал и без этого, тоже работает, но идёт сильная потеря пакетов).

Загрузим на каждую из esp32 свою часть примера библиотеки wire.h: на левую — сканер адресов подключённых устройств, на правую — slave. И мы видим, что slave-устройство отлично обнаруживается:


А теперь попробуем установить на ведущее устройство код master-части. Всё работает ОК, потерь пакетов не наблюдается:


Теперь переключим монитор порта на slave-устройство и посмотрим, что пишет оно. Тоже потерь пакетов не наблюдается:


UPD: после долгого тестирования обнаружил, что в некоторых ситуациях всё-таки наблюдаются потери пакетов, однако от этого удалось избавиться очень простым способом: провода I2C и провода питания (Vin, GND) смотал друг с другом, наподобие витой пары. После этого потери пакетов прекратились. Видно, как отчитывается функция endTransmission(), где 0 означает успешную передачу:


Теперь остаётся только для спортивного интереса повесить на одну esp32 датчик, а на другую попробуем повесить шаговый двигатель, чтобы он запускался от срабатывания датчика.

В качестве такого датчика возьмём уже упомянутый цифровой (то есть который выдаёт только значения LOW и HIGH) датчик Холла KY-003, а в качестве шагового двигателя также упомянутый выше Nema 17 и драйвер двигателя ТВ6600:

Всё работает как и должно! Таким вот нехитрым образом вы можете увеличить количество пинов esp32, соединив между собой n-плат. В показанном выше видео код опроса датчика помещён прямо в цикле loop, что есть не совсем правильно и сделано это для ускорения демонстрации. В реальном проекте, чтобы не занимать процессорное время, датчик надо подключать через функцию attachInterrupt(interrupt, function, mode). В своём проекте собираюсь сделать именно так. Мало того — чтобы постоянно не занимать линию опросами датчиков, все датчики повешу на master, а все двигатели (ну или почти все) — на slave.

Здесь я выложил упрощённый пример показанного в видео, где код управления шаговым двигателем заменён на код светодиода (срабатывание датчика на master-e зажигает встроенный светодиод на slave-e).

Я не ставил целью строить распределённую систему (одна esp32 — в одной комнате, другая — в другой и т. д.), поэтому для моих целей (простое расширение количества пинов) всё работает хорошо. Если вам нужна будет именно распределённая система, возможно, вам придётся углубиться в изучение этой темы дальше. Этот пост не претендует на идеальное знание темы и «учебника» по ней. Скорее это история про то, как у меня возникла проблема и как я её решил.

Соединение между платами по беспроводным каналам (wifi, bluetooth) не рассматривал, так как в моём случае это будет в высшей мере странно — «забивание гвоздей микроскопом» :-)

Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх ????️

Комментарии (56)


  1. Pavel7
    24.08.2023 09:09
    +9

    Природная лень заставила меня поискать готовые решения, однако здесь пришлось натолкнуться на практически полное их отсутствие (связь двух и более esp32 между собой)

    А почему не взять какой-нибудь готовый extender, типа MCP23017?


    1. Ukrop88
      24.08.2023 09:09
      +5

      Согласен, вариантов расширителей портов целая куча. На том же I2C. Как автор искал готовые решения, не понятно )


      1. Pavel7
        24.08.2023 09:09
        +2

        Справедливости ради, сейчас посмотрел цены - распаянный MCP23017, оказывается, нынче идёт по $3, что сравнимо с ценой распаянного esp32. В 2020 MCP23017 стоил $1.


    1. DAN_SEA Автор
      24.08.2023 09:09
      +2

      Ну этож надо покупать, где то заказывать и т.д. Лень :-) А под рукой - просто куча esp32.

      А почему не взять какой-нибудь готовый extender, типа MCP23017?


      1. nikolz
        24.08.2023 09:09
        +2

        самый дешевый вариант будет этот:

        на него можно повесить еще 16 таких же, получим 256 пинов.


    1. iig
      24.08.2023 09:09
      +8

      74HC595 - классика же. Причем протокол управления здорово напоминает SPI.


      1. voldemar_d
        24.08.2023 09:09
        +2

        Он подходит для размножения выходных пинов. Для входных 74HC165.


    1. Klochko
      24.08.2023 09:09

      Намучался я как-то с MCP23S17 (SPI-версия).

      Если с записью в расширитель у меня проблем не было, то с последовательным чтением данных у меня так ничего и не вышло. Мне нужно было условно записать 1 в конкретный бит порта. Затем этот же пин настроить на вход и посмотреть, 1 там или 0. В общем мучился долго и плюнул на это дело. Вот все эти танцы с бубном нужны были под конкретную периферию. На её инициализацию.

      Поступил я аналогичным образом, взял другой дешевый МК на который возложил всю необходимую логику работы с периферией. А с центрального МК просто слал команды и данные для периферии.

      Да и ценник на эти MCP сопоставимы с ценой МК. Но на другом МК банально удобнее.


    1. Mike-M
      24.08.2023 09:09

      К тому же жесткая логика от надежного производителя заведомо надежнее китайского микроконтроллера с программным кодом.
      Впрочем, судя по статье, требования к надежности у автора минимальные.


      1. iig
        24.08.2023 09:09
        +3

        Впрочем, судя по статье, требования к надежности у автора минимальные.

        Автор торжественно забил гвоздь микроскоп другим микроскопом ;)


      1. uszer
        24.08.2023 09:09

        За более чем 20 лет работы в пром. секторе не встречал проектов, в которых бы раздел "расчет надежности" представлял из себя что-то лучшее, чем буквенно-цифровой мусор, собранный в кучку по принципу "на отшибись". Редчайшее это явление. В живой природе практически не встречающееся.


        1. iig
          24.08.2023 09:09

          Ну хоть кто-то не умеет в запланированное устаревание ;)


  1. Ukrop88
    24.08.2023 09:09
    +1

    В связке с тем же esp32 использую одновременно 4 расширителя "PCAL9535APW ,118" , что увеличило количество портов аж на 64 штуки. Количество портов можно увеличивать в разумных пределах.


    1. DAN_SEA Автор
      24.08.2023 09:09

      В связке с тем же esp32 использую одновременно 4 расширителя "PCAL9535APW ,118" , что увеличило количество портов аж на 64 штуки. Количество портов можно увеличивать в разумных пределах.

      Ну да, можно и так наверное...А если использовать соединение esp32-esp32 (39-пиновые) то, учитывая, что на каждую можно повесить 16 штук (реально больше - но я имею в виду пины без всяких нюансов). То тогда, чтобы выбрать весь диапазон адресов (127), потребуется 127/16 = 7,9 штук :-)

      И, в теории, можно ещё сделать систему распределённой.


      1. Ukrop88
        24.08.2023 09:09

        на каждую можно повесить 16 штук

        Не совсем понял идею ) 16 штук чего ?


      1. Serge78rus
        24.08.2023 09:09
        +5

        И, в теории, можно ещё сделать систему распределённой.

        Интерфейс I2C предназначен для построения коммуникаций между микросхемами в пределах печатной платы, в крайнем случае в пределах одного устройства, но никак не для построения распределенных систем. Если Вам нужно вытаскивать интерфейс за пределы устройства, то следует обратить свой взор на другие интерфейсы, для этого предназначенные. Все должно использоваться по своему назначению.


        1. evtomax
          24.08.2023 09:09

          В любительских поделках можно и вынести за пределы устройства. У меня i2c стабильно работал через четырёхметровый неэкранированный телефонный кабель. Правда, пришлось пропустить кабель через ферритовый фильтр. Никаких преимуществ специально предназначенный интерфейс не дал бы. Только недостатки в виде нагромождения преобразователей в какой-нибудь RS-485 и обратно.


          1. Serge78rus
            24.08.2023 09:09
            +4

            Помимо устойчивости связи есть еще защита от перенапряжений. И именно поэтому "нагромождения преобразователей" для RS-485 является не недостатком, а достоинством - найти микросхемы приемопередатчиков, устойчивые к статике не проблема. Для защиты же линий I2C готовых решений еще надо поискать - они, в принципе, существуют, но вся выпускаемая номенклатура ограничивается всего парой штук, и это именно потому, что I2C не предназначен для вывода наружу устройства.


            1. evtomax
              24.08.2023 09:09

              Мне сложно представить, что в домашних условиях может быть источником такой наводки на четырёхметровый кабель, что её энергии хватит на выжигание пина микроконтроллера. Если только удар молнии прямо в дом.

              По кабелю идут в том числе жилы с землёй и питанием, которые сильно ослабляют влияние всякой гадости.


              1. Serge78rus
                24.08.2023 09:09
                +1

                При подключении кабеля отнюдь не факт, что земляной проводник соединится раньше сигнального, если это не обеспечено специальной конструкцией разъема (типа как это сделано в USB). Выравнивание потенциалов корпусов устройств через сигнальный проводник вполне может приводить к выгоранию входов, если не при первом, то при тысячепервом подключении, собенно при подключениии "на горячую". Разность потенциалов может быть как от статики, так и от сетевой наводки.

                Даже специальные интерфейсные микросхемы каких-то 30 лет назад были не столь неубиваемые, как сегодня. Кто застал реализацию RS-232 на паре микросхем 75188/75189 поймет о чем я - "бизнес" по их замене просто процветал в то время.


          1. voldemar_d
            24.08.2023 09:09
            +2

            Для того и нужен RS485, чтобы можно было не на 4 места передавать, а на сотню. Если, конечно, задача того требует.


          1. Kudriavyi
            24.08.2023 09:09
            +1

            Достаточно эти четыре метра положить параллельно сетевому проводу 220В и порты микроконтроллера вылетят к чертовой матери. Не сразу, так в самый неприятный момент.


  1. DAN_SEA Автор
    24.08.2023 09:09

    Я имел в виду, что (теоретически) можно увеличить количество I2C -пинов у esp32 (не только 2 пары, как обычно). А скажем, написать свою библиотеку и "зеркалировать" данные. Скажем, каждую esp32 обвешать другими esp32 - и одну из них использовать как скажем так "магистральную" - через которую все кидают данные. Но это потребует уже самостоятельно это все реализовывать, насколько я понимаю. Но- почему бы и нет? :-)

    Но это не нужно - проще конечно просто к линиям присоединиться и все (а то "масло масляное" как бы).


    1. Ukrop88
      24.08.2023 09:09

      Да, по идее можно использовать 2 аппаратных шины I2C на esp32. повесить по 127 девайсов на каждую, плюс использовать софтовый i2c драйвер и поднять еще примерно (16/2) 8 софтварных I2C по 127 девайсов на каждом. Итого 1270 slave-ов на 1 esp повесить ) на каждый из которых так же можно повесить по 1270 штук и так до бескончености )


      1. checkpoint
        24.08.2023 09:09

        Вы не сможете повесить на шину даже 10 устройств - шина будет работать катастрофически нестабильно. Про 127 устройств можете смело забыть. Это всего лишь адресное пространство, а не максимальное количество устройств.


  1. mazkorulez
    24.08.2023 09:09
    +3

    Человек явно не ходил на схемотехнику. Тут же явно нужен мультиплексор, например такой CD74HC4067. Но вы решили решить вопрос по-другому - создавать протокол, передавать данные по I2C и прочее.


    1. DAN_SEA Автор
      24.08.2023 09:09

      Спасибо за коммент! :-) Любым хорошим идеям я всегда рад. С мультиплексорами сталкивался- но весьма шапочно и в своих проектах не применял.


    1. BareDreamer
      24.08.2023 09:09
      +1

      Я не специалист, но как я понимаю, мультиплексор гораздо слабее по своим возможностям чем предложенное решение.
      Если задача – опрос датчиков, то мультиплексор может переключаться между множеством источников. Таким образом можно последовательно опросить все датчики.
      Но если задача – управление какими-то реле или двигателями, то каждый выходной пин должен находиться в требуемом состоянии, пока это состояние не будет изменено управляющей программой. У мультиплексора (и демультиплексора) нет памяти, поэтому он не может так работать.


  1. aumi13
    24.08.2023 09:09
    +2

    Обычно IIC работает либо на скорости 10кбит/с — в медленном режиме, либо на 100кбит/с в быстром.

    какието странные цифры, подумал я, заглянул в дш, и точно:

    M24M01-R M24M01-DF
    1-Mbit serial I²C bus EEPROM

    • Compatible with all I2C bus modes:
    – 1 MHz
    – 400 kHz
    – 100 kHz


  1. anonymous
    24.08.2023 09:09

    НЛО прилетело и опубликовало эту надпись здесь


  1. nikolz
    24.08.2023 09:09
    +2

    Каждый ESP32 потребляет от 100 до 400 мА, а каждый расширитель портов PCF8575 на порядок меньше и стоит в 3-5 раз меньше.


  1. vk6677
    24.08.2023 09:09
    +3

    Обычный сдвиговый регистр с защелкой - просто, дешево и надёжно.


  1. VT100
    24.08.2023 09:09
    +2

    Желаю успешного запуска I2C рядом с моторами… даже если без "встроенные 45 кОм" и с соединением GND… ммм, это может быть жыр.
    Кстати, I2C через библиотеку wire — не програмный ли?


    1. DAN_SEA Автор
      24.08.2023 09:09
      +1

      Насчет движков - всё ок, даже видео приложил в конце статьи ;-) А вчера тестил I2C с коллекторным движком - тоже ок. Насчет реализации I2C в wire - не копал.


  1. Ivanii
    24.08.2023 09:09

    PCF8574 стоят на Али 450 - 800 руб./10 шт., имеют 8 IO, 8 выставляемых перемычками адресов, бывает 2 вида с разными группами адресов, такая уже стоит на экране 1602. В прочем и PCF8575 стоит примерно столько же.


  1. jonic
    24.08.2023 09:09
    +2

    Мягко скажем - так себе решение.


    1. DAN_SEA Автор
      24.08.2023 09:09

      Мягко скажем - так себе решение.

      В моём случае это оправданно, т.к. на slave повешены двигатели - а ими надо "рулить": генерить пульсации и т.д. по команде, пришедшей с I2C. Так как на мастере все пины заняты и подключить двигатели некуда. То есть, другими словами, в моём случае - slave обязательно должен быть "умным" а не просто уметь "висеть на том конце провода" :-)


  1. checkpoint
    24.08.2023 09:09
    +2

    I2C шина хороша только тем, что требует меньше проводов, на этом её достоинства заканчиваются и начинаются одни сплошные недостатки, а именно:

    • очень низкая скорость обмена, полудуплексный способ обмена и высокий оверхед;

    • зависимость всех устроойств на шине друг от друга, стоит одному из устройств перестать играть по правилам как наступает конец. Теоритически, можно попытаться развесить шину, но это не всегда возможно и не все устройства поддерживают такую фичу.

    • сложность поиска "глючного" устройства на шине;

    • необходимость опытным путем подбирать номиналы резисторов подтяжки если на шине много (более трех) устройств, при этом, очень часто получается эффект "то работает, то не работает, то вот опять работает";

    • конфликт адресов на шине - нельзя размести на шине массив из одинаковых датчиков если в самих датчиках не предусмотрена возможность задавать адрес;

    • сложность программирования протокола "в ручную" (если в МК нет или неоступен аппаратный интерфейс) - правильно запрограммировать и выдержать тайминги вам врядли удастся;

    • низкая устойчивость к ЭМ помехам - эл.моторы и эл.магниты в непосредственной близости с устройством часто приводят к зависанию шины;

    • не рекомендована для использования в межплатных коммуникациях;

    • общая ненадежность и нестабильность.

    Куда веселей обстоят дела с шиной SPI: на порядок (а местами и на два порядка) выше скорости, синхронный полнодуплексный характер обмена, большой спектр доступных устройств: многоканальные ЦАП и АЦП, расширители GPIO, Ж/К дисплеи и т.д.

    Но встает вопрос - а как подключить множество SPI устройств когда на хваленой и всеми любимой ESP32 доступно к использованию всего два SPI интерфейса, да и те одновременно использовать нельзя ? Решение есть - использовать ПЛИС в качестве коммутатора шины SPI. Выгладит это так: ESP32 (или аналогичный МК) подключен к ПЛИС по одной шине SPI и еще несколькими линиями GPIO по которым передает в ПЛИС адрес периферийного устройства с которым требуется осуществить обмен, а ПЛИС в свою очередь коммутирует входную шину от МК в сторону адресованного в данный момент устройства. Данное решение достаточно распространено в профессиональной среде, на подобном принципе часто строятся внутренние шины между ПЛК и модулями расширения.

    Для тех, кто боится или не желает использовать ПЛИС по каким-то причинам, существует серия микросхем ADGS1408/ADGS1409 от Аналоговых Девиц, которые представляют собой аналоговые многопортовые коммутаторы SPI интерфейса.

    И еще. С помощью современных ПЛИС шину SPI можно сделать в разы более устойчивой если использовать дифференциальные линии (LVDS) для передачи отдельных сигналов. Это позволяет вытаскивать шину на десятки метров без потери в скоростях.

    Удачи!


    1. voldemar_d
      24.08.2023 09:09
      +2

      У каждого устройства на шине SPI есть сигнал chip select. В чем проблема подключить хоть 10 устройств к одной шине SPI? Я два подключал, работает. Если их больше, будут проблемы?


      1. aumi13
        24.08.2023 09:09
        +2

        насколько я помню спи-шные регистры и некоторые другие микросхемы можно подключать цепочкой последовательно


        1. voldemar_d
          24.08.2023 09:09
          +2

          Почему последовательно? Я подключал к Arduino Pro Micro к одному и тому же аппаратному SPI два устройства параллельно. Только сигналы CS у каждого из устройств были подключены к разным пинам Arduino. При инициализации библиотеки для каждого из устройств указывается нужный пин CS, и далее код библиотеки перед каждым обращением к устройству на этот пин правильный уровень подают. Работают устройства, конечно, последовательно (либо одно, либо другое в один момент времени), но подключены к одним и тем же сигналам шины SPI параллельно.


          1. solderman
            24.08.2023 09:09
            +1

            Это хорошо конечно, если все устройства умеют по высокому уровню CS переводить хотя бы MISO в третье состояние. Например есть SD модули с резисторами на интерфейсе - с ними вообще ничего не живет. Они же с буфером тоже специфические- буфер не управляется CS-ом, работать начинает только после доработки- заведения CS на управление соответствующим каналом буфера - MISO.


            1. voldemar_d
              24.08.2023 09:09

              У меня нет большого опыта в этой области. Тот SD shield, который я использовал, без проблем работает параллельно с чипом статической памяти SRAM, подключенному параллельно к тому же SPI на Arduino.


          1. aumi13
            24.08.2023 09:09

            последовательно выгодней - нужен только 1 сигнал СS а значения грузяца или читаюца последовательно хоть с 4х регистров, хоть со 127.


            1. voldemar_d
              24.08.2023 09:09

              Это каскадное подключение сдвиговых регистров. В моей статье по ссылке такое тоже есть. Но я говорю про подключение к одной шине SPI двух разнородных устройств. В моей статье это адаптер SD-карты и статическая память SRAM.


              1. aumi13
                24.08.2023 09:09

                в общем случае устройства подключаемые на шину не должны мешать в неактивном состоянии. а вопрос множественного подключения можно решать как последовательно так и паралельно в зависимости от задач и возможностей.


                1. voldemar_d
                  24.08.2023 09:09

                  Про каскадное подключение сдвиговых регистров понятно - они в принципе разработаны с учетом такой возможности. А что, можно, например, адаптер SD-карты и сдвиговый регистр подключить к одной шине SPI последовательно? Честно, не понимаю, как такое возможно.


            1. sami777
              24.08.2023 09:09

              Тут уж под каждую задачу нужно выбирать свою и желательно правильную реализацию. Беда последовательно в том, что вы каждый раз вынуждены управлять всеми устройствами сразу. Слать кучу байт в шину, даже если вы хотите обратиться только к одному устройству. К тому же, если из строя выйдет более верхний регистр у Вас вся цепочка ниже отвалится. Да и отдельный "CS" это все таки дополнительная уверенность в том, что вы не дергаете то, что не надо.


      1. checkpoint
        24.08.2023 09:09

        У шины есть нагрузочная способность, два-три или даже четыре устройства в параллель Вы подключите, больше - уже будут проблемы. И еще, Вам придется выделить по отдельному GPIO для каждого CSn (что если Вам необходимо подключить 16 периферийных сутройств ?) и управлять ими программно, что создает проблемы при использовании аппаратного контроллера внутри МК. По этому, вариант с мостом на ПЛИС (или с аналоговым коммутатором) более предпочтителен, так как сигналом CSn управляет контроллер напрямую, а программа перед началом обмена выставляет адрес устройства на три или четыре линии GPIO.


  1. smart_alex
    24.08.2023 09:09

    Общение между устройствами в рамках этой шины всегда инициирует master, вызывая соответствующее ведомое устройство

    Так это... насколько я понял тут принципиально «односторонняя» связь. Чтобы слейв что-то передал мастеру нужно чтобы мастер сначала инициировал соединение.

    То есть если слейв захотел что-то передать, то он не может это сделать пока мастер не инициирует соединение. Или как?

    Если это так, то это не очень интересно — хотелось бы иметь возможность передачи данных в оба конца в любой момент.


    1. DAN_SEA Автор
      24.08.2023 09:09

      То есть если слейв захотел что-то передать, то он не может это сделать пока мастер не инициирует соединение. Или как?

      Если это так, то это не очень интересно — хотелось бы иметь возможность передачи данных в оба конца в любой момент.

      Насколько мне известно - именно так. Благодаря этому обеспечивается "порядок" в шине скажем так :-)

      Скажем, надо слейву прередать состояние датчиков мастеру - мастер должен их опрашивать сам.


      1. smart_alex
        24.08.2023 09:09

        Порядок на шине это хорошо, но я предпочту другой интерфейс связи (не I2C), лишь бы главный MCU и ведомый MCU могли свободно общаться, не ожидая «милости» мастера.


  1. andreishe
    24.08.2023 09:09

    Копируете получившееся число из окошка наверху и вставляете с припиской «0х...» в свой код 

    А можно без приписки писать сразу десятичные. Прямо в коде, представляете? И с конвертацией возиться не надо.


    1. DAN_SEA Автор
      24.08.2023 09:09

      Видел на финальном этапе - но забыл в статье поправить.


  1. Bobovor
    24.08.2023 09:09

    Позвольте, но если уж взять 2 есп, то можно и по wifi связь построить, ну или блютуз


  1. zEHOTz
    24.08.2023 09:09

    Интересная статья (пост). С удовольствием почитал. Но внесу поправку. 7-бит (127) в связке с 16-ой системой запутают не знающего читателя. Железке - 7 бит 2-ой, для ПО - 1 байт в 16-ой.