Перейти к содержимому

Добрый день, уважаемый читатель! В предыдущей статье я рассказывал о I2C расширителе портов PCF8574, а сегодня я хотел бы рассказать о более интересном варианте расширителя портов ввода-вывода для микроконтроллера – на микросхеме MCP23017.

Первоначальным производителем этой микросхемы, судя по даташиту, является фирма Microchip, но на ali, скорее всего, предлагаются клоны (которые, впрочем, неплохо работают). Как и в случае с PCF8574, китайцы предлагают большое разнообразие как микросхем, так и плат на их основе:

Расширитель GPIO MCP23017
Расширитель GPIO MCP23017
Расширитель GPIO MCP23017

 


Технические характеристики

Микросхема MCP23017 – это расширитель GPIO на 16 портов для шины I2C с полноценными двунаправленными портами. Выпускается вариант для более высокоскоростной шины SPI – MCP23S17. Однако и обычная MCP23017 не лыком шита – она поддерживает не только стандартную скорость шины 100 kHz, но и высокоскоростные режимы 400 kHz и даже 1.7 MHz (тут уже главное чтобы сам МК и остальные устройства на шине эту скорость потянули).

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

  • Напряжение питанияот 1.8В до 5.5В, то есть перекрывает весь диапазон “любительских” микроконтроллеров, поэтому не требуется применять стабилизаторы и согласователи логических уровней для шины.
  • Ток потребления собственно микросхемы – 1mA при частоте шины 1MHz, в режиме ожидания не более 3µA
  • Максимальный входной ток для любых GPIO – 20mA при работе в режиме входа
  • Максимальный выходной ток для любых GPIO – 25mA при работе в режиме выхода
  • Суммарный ток по всем 16 выводам однако не должен превышать 125mA
  • Суммарная рассеиваемая мощность – не более 700mW
  • Режимы работы GPIO: каждый вывод может быть настроен либо как входной, либо как выходной, вне зависимости от других (с помощью битовой маски, задаваемой при инициализации микросхемы).
  • Имеется встроенная “слабая” подтяжка. Микросхема имеет встроенные резисторы 100 кОм для подтяжки выводов портов к питанию, которые также можно активировать отдельно для каждого вывода. Но только к питанию и только в режиме входа.
  • Регистр “защелок” на каждом из выводов в режиме работы “на выход”. Кроме обычного регистра состояния GPIO микросхема содержит в себе регистр OLAT, который обеспечивает доступ к данным в режиме выхода.
    Запись в этот регистр изменяет состояние выходных защелок, которые в свою очередь, изменяют напряжение на выходах. Запись в регистр GPIO в режиме выхода всегда фактически приводит к записи в защелки OLAT. Чтение же из этого регистра приводит к чтению регистра OLAT, а не физического состояния порта GPIO. Но можно прочитать и фактическое состояние из обычного регистра GPIO, которое, впрочем, ничего интересного не вернёт.
  • Два вывода прерываний при изменении состояния уровней на входах. Для работы с прерываниями микросхема вообще имеет много возможностей. Во-первых, можно использовать оба выхода прерываний отдельно друг от друга – для первых 8 портов (группа A) и для других (группа B). Но можно и объединить сигналы прерываний и подключить к микроконтроллеру только один сигнал прерывания. А во-вторых, можно выбирать режим работы выхода прерывания: активный-высокий, активный-низкий или открытый сток. По умолчанию используется активный-низкий. Можно включать прерывания только для отдельных входов, а остальные входы только опрашивать периодически, без прерываний. Можно использовать режим сравнения с образцовым значением (низкий или высокий), а можно генерировать прерывания по изменению уровня.
  • Регистр снимка состояний входов в момент прерывания позволяет получить состояние входов в момент прерывания (а не в момент чтения GPIO), а регистр выводов прерывания позволяет легко определить, какие выводы были затронуты

Что касается электронной начинки, то MCP – микросхема очень скучная – ну выводы и выводы, ну полноценно работают в обе стороны (и на ввод и на выход), ну с прерываниями, в общем все как полагается для обычных GPIO микроконтроллеров. По сравнению c PCF8574, про которую я писал в предыдущей статье, даже и поговорить не о чем. Разве что допустимый ток в два раза выше.

Источник: datasheet

Источник: datasheet

Корпус и выводы

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

  • 28-pin SPDIP
  • 28-pin QFN
  • 28-pin SOIC
  • 28-pin SSOP

Схемы расположения выводов для разных вариантов легко найти в даташите:

Таблицы выводов MCP23017. Источник: datasheet

Таблицы выводов MCP23017. Источник: datasheet


 


Программный интерфейс

Совсем другое дело – если взглянуть на таблицу регистров микросхемы. Тут становится уже очень интересно, но ничего не понятно…. Какие-то банки, бутылки… Вроде бы одно и те же регистры, а адреса разные, И зачем столько таблиц?

Огромное количество таблиц с регистрами. Запутаться очень легко

Огромное количество таблиц с регистрами. Запутаться очень легко

Расширитель GPIO MCP23017

Расширитель GPIO MCP23017

Думаю, все знают этот старый мем... Источник: Яндекс Картинки

Думаю, все знают этот старый мем… Источник: Яндекс Картинки

На самом деле, все оказывается просто. Если разобраться.

Микросхема предоставляет два режима управления портами:

  • 16 битный – это BANK=0. В этом режиме вам предоставляется восемь 16-битных регистров, с помощью которых можно управлять сразу всеми выводами как единым целым устройством.
  • 8 битный – это BANK=1. Альтернативный режим, вам предоставляется два отдельных блока 8-битных регистров, каждый из которых управляет своей “половинкой” микросхемы. Независимо. Например это может быть удобно, если одна половина микросхемы используется “на вход”, а другая – “на выход”.

Расширитель GPIO MCP23017

Получается, что в 16-битном режиме нельзя одновременно использовать выводы на вход и на выход?

А вот и нет! Ещё как можно. И никаких неудобств это не доставляет. Регистры-то фактически остаются одними и теми-же, просто у них нумерация меняется. В одном случае они сгруппированы по 2 штуки как 16-битные, в другом – сгруппированы по “сторонам” микросхемы. Вот и все банки.

Я всегда использую только 16-битный режим – а смысл кодить в драйвере дополнительную кучку методов, которые делают то же самое?

Адреса MCP23017 на шине IIC

Как и в случае с PCF8574, MCP23017 поддерживает сразу несколько адресов на шине I2C, которые можно выбирать с помощью трех выводов – A0, A1 и A2. Адресные выводы можно соединять с выводами GND или Vсс напрямую, без использования резисторов. Эти выводы определяют адрес на шине следующим образом:

Расширитель GPIO MCP23017

Что в итоге дает адреса от 0x20h до 0x24h – то есть диапазон адресов фактически совпадает с диапазоном адресов PCF8574.

Расширитель GPIO MCP23017

Поэтому наращивать количество GPIO за счет комбинирования MCP и PCF не получится.

 

Регистры

В этом списке я буду использовать 16-битные регистры (режим BANK=0).

IODIR (ADDR 0x00): DIRECTION REGISTER : Регистр направления I/O.
Этот регистр управляет направлением ввода или вывода данных, то есть режимом работы порта. Когда бит установлен (1), соответствующий контакт становится входом. Когда бит очищен (0), соответствующий контакт становится выходом. По умолчанию (после запуска микросхемы) все порты настроены как входы.

IPOL (ADDR 0x02): INPUT POLARITY PORT REGISTER : Регистр инверсии входа.
Если бит установлен, соответствующий бит регистра GPIO будет отражать инвертированное значение на выводе. То есть вы устанавливаете высокий уровень на входе, а микросхема вам выдает “0”, устанавливаете низкий, а микросхема говорит “1”. Полезная функция в некоторых случаях.

GPINTEN (ADDR 0x04): INTERRUPT-ON-CHANGE PINS : Включение прерываний.
Этот регистр управляет прерываниями при изменении для напряжения на входе. Если бит установлен (1), соответствующий контакт будет генерировать прерывание при изменении напряжения на входе, иначе (0) ничего не происходит. Для корректной работы прерываний может потребоваться дополнительная настройка регистров DEFVAL и INTCON, управляющих дополнительными параметрами прерываний.

DEFVAL (ADDR 0x06): INTERRUPT DEFAULT VALUE REGISTER : Значение по умолчанию для прерываний.
Этот регистр используется для задания значений по умолчанию для прерываний. Если включены прерывания (через регистр GPINTEN) и настроено сравнение с регистром DEFVAL (через регистр INTCON), то противоположное значение на соответствующем выводе вызовет прерывание. Фактически это соответствует привычным для ESP режимам прерываний INT_LOW_EDGE “по низкому уровню” или INT_HIGH_EDGE “по высокому уровню“, но с инверсией.

INTCON (ADDR 0x08): INTERRUPT-ON-CHANGE CONTROL REGISTER : Режим сравнения прерываний.
Регистр INTCON управляет тем, как сравнивается значение соответствующего вывода для функции прерывания при изменении. Если бит установлен (1), соответствующее значение на порту ввода с учётом инверсии сравнивается с соответствующим битом в регистре DEFVAL. Если значение бита очищено (0), соответствующее значение на порту ввода сравнивается с предыдущим значением, фактически это соответствует более привычному INT_ANY_EDGE.

IOCON (ADDR 0x0A): CONFIGURATION REGISTER : Регистр конфигурации.
Регистр конфигурации хранит 8 бит для “общих” настроек микросхемы:

Расширитель GPIO MCP23017

Это единственный 8-битный регистр, который не зависит от параметра BANK. Поэтому этот регистр должен быть записан первым после включения питания микросхемы!

Биты регистра отвечают за следующие функции:

  • bit 7 BANK : адресация регистров
    Этот бит управляет тем, как будет выполняться адресация внутренних регистров микросхемы
    1 = регистры, связанные с PORTA и PORTB, разделены на разные банки
    0 = регистры находятся в одном банке (адреса следуют друг за другом)
  • bit 6 MIRROR : зеркалирование выходов прерываний
    Этот бит позволяет объединить сигналы прерывания PORTA и PORTB между собой, и таким образом, использовать только один вход GPIO на главном микроконтроллере
    1 = контакты INT логически объединены внутри микросхемы
    0 = контакты INT не объединены: INTA связан с PORTA, а INTB связан с PORTB
  • bit 5 SEQOP : последовательный режим чтения/записи регистров
    Последовательная передача регистров позволяет передавать по шине последовательно несколько регистров без указания адреса каждого из них. Указатель адреса регистра автоматически вернется на адрес 00h после обращения к последнему регистру.
    1 = Последовательная передача регистров отключена
    0 = Последовательная передача регистров включена.
  • bit 4 DISSLW: скорость нарастания для SDA
    Бит управления скоростью нарастания для выхода SDA. Если включено, скорость нарастания SDA будет контролироваться при переходе от высокой скорости к низкой.
    1 = скорость нарастания отключена
    0 = скорость нарастания включена
  • bit 3 HAEN: управление адресом для SPI
    Бит разрешения аппаратного адреса для шины SPI (только MCP23S17)
    1 = активирует адресные контакты MCP23S17
    0 = отключает адресные контакты MCP23S17
  • bit 2 ODR: режим работы выходов прерываний
    настраивает выводы INT как выходы с открытым стоком
    1 = выход с открытым стоком (бит INTPOL игнорируется)
    0 = активный выход (бит INTPOL устанавливает уровень)
  • bit 1 INTPOL: активный уровень выходов прерываний
    Этот бит устанавливает полярность выходного вывода INT
    1 = активный высокий уровень
    0 = активный низкий уровень
  • bit 0: зарезервировано
    всегда возвращает «0»

 

GPPU (ADDR 0x0С): GPIO PULL-UP RESISTOR REGISTER : Управление подтягивающими резисторами в режиме входа
Регистр GPPU управляет подтягивающими резисторами для выводов порта. Если бит установлен и соответствующий вывод сконфигурирован как вход, соответствующий вывод порта подтянут к питанию внутренним резистором 100 кОм.

INTF (ADDR 0x0E): INTERRUPT FLAG REGISTER : Регистр прерываний
Регистр INTF отражает состояние прерывания на выводах порта, установленный бит означает, что соответствующий вход вызвал прерывание. Это позволяет легко определить какие выводы были изменены без необходимости хранения данных на стороне мастера. Этот регистр доступен только для чтения. Записи в этот регистр будут игнорироваться.

INTCAP (ADDR 0x10): INTERRUPT CAPTURED VALUE FOR PORT REGISTER : снимок состояния входов в момент прерывания
Регистр INTCAP фиксирует состояния входов GPIO в момент возникновения прерывания. Регистр доступен только для чтения и обновляется только при возникновении прерывания. После этого регистр остается неизменным до тех пор, пока прерывание не будет очищено чтением одного из регистров INTCAP или GPIO. Это позволяет узнать состояние входов в момент прерывания, так как до момента чтения GPIO их состояние может измениться.

GPIO (ADDR 0x12): GENERAL PURPOSE I/O PORT REGISTER : Состояние входов и выходов
Основной регистр микросхемы, отражающий текущее состояние GPIO микросхемы. Чтение из этого регистра считывает физическое состояние входов. Запись в этот регистр изменяет регистр выходных защелок (OLAT).

OLAT (ADDR 0x14): OUTPUT LATCH REGISTER : Защелки выходов
Регистр OLAT обеспечивает доступ к защелкам выходов. Чтение из этого регистра приводит к чтению OLAT, а не самого порта. Запись в этот регистр изменяет выходные защелки, которые изменяют контакты, сконфигурированные как выходы.

 


Осторожно – помехи на шине!

При разработке устройств с PCF cледует учитывать, что данная микросхема довольно чувствительна к длине проводников шины и различным помехам. Происходит это потому, что она не имеет собственного микроконтроллера, а вся логика работы с шиной I2С реализована на конечном автомате и “простой” логике, которая работает по фронтам и спадам управляющих импульсов на линиях SCL и SDA. Если паразитная емкость проводников шины I2C слишком большая, то импульсы могут быть сильно искажены, что в рано или поздно приведет к нарушениям в логике работы конечного автомата.

Самое плохое в этой ситуации то, что программного сброса микросхемы в исходное состояние не предусмотрено, только аппаратный. Потому при входе в “запрещенное” состояние каждый новый сеанс связи с управляющим микроконтроллером будет начинаться с “неправильного” состояния конечного автомата, что может только усугублять ситуацию. В итоге микросхема становится полностью неуправляемой, причем программный сброс микроконтроллера и перезапуск управляющий программы может и не помочь, если вы не задействовали вывод для аппаратного сброса расширителя. Для решения данной проблемы необходимо использовать вывод RESET микросхемы для её управляемого перезапуска, но это потребует дополнительного GPIO микроконтроллера. Если RESET не используется, то поможет только кратковременное отключение питания.

Поэтому, при разработке устройств с MCP23017 (и всех аналогичных по принципу работы) следует избегать подключение к шине I2C датчиков и сенсоров с длинными проводами, по возможности их стоит перенести на другую шину (например на ESP32 их две).


Библиотека для работы с микросхемой

Несмотря на то, что микросхема имеет целую кучку регистров, ничего сложно в программировании обмена данными с ней нет. Для осуществления этого немудрёного процесса из ESP-IDF мной была написана библиотечка, которой я и пользуюсь: GitHub – kotyara12/reMCP23017: Драйвер раcширителя GPIO MCP23017 для ESP-IDF

Те, кто программирует в среде Arduino, думаю, легко найдут уже готовые библиотеки для работы с этой микросхемой в общем каталоге.

Итак, как же с ней работать?

Функции, начинающиеся с префикса “port“, работают сразу для всех 16 выводов микросхемы. Функции, начинающиеся с префикса “pin“, работают только для конкретного вывода

Настройка микросхемы

Для начала настраиваем регистр параметров микросхемы с помощью функции:

bool configSet(mcp23017_int_out_mode_t intOutMode, bool intOutMirror);

где указываем, какой режим прерываний mcp23017_int_out_mode_t intOutMode мы будем использовать и нужно ли зеркалировать выходы прерываний. Здесь я перевожу служебные биты в более удобное понимание:

// Interrupt outputs (INTA/INTB) mode
typedef enum {
  MCP23017_ACTIVE_LOW = 0,   // Low level on interrupt
  MCP23017_ACTIVE_HIGH,      // High level on interrupt
  MCP23017_OPEN_DRAIN        // Open drain
} mcp23017_int_out_mode_t;
bool reMCP23017::configSet(mcp23017_int_out_mode_t intOutMode, bool intOutMirror)
{
  uint8_t config;
  // Read current config
  if (read8(REG_IOCON, &config)) {
    // Set 16-bit mode (BANK = 0)
    config &= ~BIT_IOCON_BANK;
    // Set mode of the INT output pin
    if (intOutMode == MCP23017_ACTIVE_HIGH) {
      config &= ~BIT_IOCON_ODR;
      config |= BIT_IOCON_INTPOL;
    } else if (intOutMode == MCP23017_ACTIVE_LOW) {
      config &= ~BIT_IOCON_ODR;
      config &= ~BIT_IOCON_INTPOL;
    } else {
      config |= BIT_IOCON_ODR;
      config &= ~BIT_IOCON_INTPOL;
    };
    // Set INT pins are internally connected
    intOutMirror ? config |= BIT_IOCON_MIRROR : config &= ~BIT_IOCON_MIRROR;
    // Write config
    return write8(REG_IOCON, config);
  };
  return false;
}

Если вы планируете использовать микросхему только “на выход”, то настраивать её, собственно, не требуется.

Настаиваем режим работы выводов

Для того, чтобы определить, какие выводы в каком режиме будут работать, есть функции:

Для того, чтобы прочитать текущие настройки, есть другая пара функций:

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

// GPIO mode
typedef enum {
  MCP23017_GPIO_OUTPUT = 0,
  MCP23017_GPIO_INPUT
} mcp23017_gpio_mode_t;
bool reMCP23017::portGetMode(uint16_t * value)
{
  return read16(REG_IODIRA, value);
}

bool reMCP23017::portSetMode(uint16_t value)
{
  return write16(REG_IODIRA, value);
}

bool reMCP23017::pinGetMode(uint8_t pin, mcp23017_gpio_mode_t *mode)
{
  uint16_t buf;
  if (read16(REG_IODIRA, &buf)) {
    if ((buf & (1 << pin)) > 0) {
      *mode = MCP23017_GPIO_INPUT;
    } else {
      *mode = MCP23017_GPIO_OUTPUT;
    };
    return true;
  };
  return false;
}

bool reMCP23017::pinSetMode(uint8_t pin, mcp23017_gpio_mode_t mode)
{
  uint16_t buf;
  if (read16(REG_IODIRA, &buf)) {
    (mode == MCP23017_GPIO_INPUT) ? buf |= (1 << pin) : buf &= ~(1 << pin);
    return write16(REG_IODIRA, buf);
  };
  return false;
}

Если требуется активировать встроенную подтяжку к питанию, то сделать это можно с помощью других похожих функций:

Чтение и запись в порт

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

 

bool reMCP23017::portRead(uint16_t * value)
{
  return read16(REG_GPIOA, value);
}

bool reMCP23017::portWrite(uint16_t value)
{
  return write16(REG_GPIOA, value);
}

bool reMCP23017::pinRead(uint8_t pin, bool *level)
{
  uint16_t buf;
  if (read16(REG_GPIOA, &buf)) {
    *level = (buf & (1 << pin)) > 0;
    return true;
  };
  return false;
}

bool reMCP23017::pinWrite(uint8_t pin, bool level)
{
  uint16_t buf;
  if (read16(REG_GPIOA, &buf)) {
    level ? buf |= (1 << pin) : buf &= ~(1 << pin);
    return write16(REG_GPIOA, buf);
  };
  return false;
}

Можно использовать portRead(uint16_t * value) для проверки состояния входов, но все-таки правильнее будет использовать для этой цели прерывания…

 

Используем прерывания

Если вы планируете использовать прерывания, то после настройки порта следует настроить режим прерываний. Чтобы не использовать отдельные биты для настройки режима прерываний, я сделал по аналогии с ESP32:

// Interrupt inputs mode
typedef enum {
  MCP23017_INT_DISABLED = 0, // No interrupt
  MCP23017_INT_LOW_EDGE,     // Interrupt on low edge
  MCP23017_INT_HIGH_EDGE,    // Interrupt on high edge
  MCP23017_INT_ANY_EDGE      // Interrupt on any edge
} mcp23017_gpio_intr_t;

Для настройки режима прерываний используйте функцию:

bool portSetInterrupt(uint16_t mask, mcp23017_gpio_intr_t intr);

Где mask – это битовая маска, указывающая на то, к каким выводам будет применятся режим прерывания, а intr – собственно режим прерывания:

bool reMCP23017::portSetInterrupt(uint16_t mask, mcp23017_gpio_intr_t intr)
{
  // GPINTEN: INTERRUPT-ON-CHANGE PINS (ADDR 0x02)
  // General purpose I/O interrupt-on-change bits. 
  // 1 = Enables GPIO input pin for interrupt-on-change event
  // 0 = Disables GPIO input pin for interrupt-on-change event
  uint16_t int_en;
  if (read16(REG_GPINTENA, &int_en)) {
    if (intr == MCP23017_INT_DISABLED) {
      // Disable interrupts
      int_en &= ~mask;
    } else {
      // Enable interrupts
      int_en |= mask;

      // INTCON: INTERRUPT-ON-CHANGE CONTROL REGISTER (ADDR 0x04)
      // Controls how the associated pin value is compared for interrupt-on-change
      // 1 = Pin value is compared against the associated bit in the DEFVAL register
      // 0 = Pin value is compared against the previous pin value.
      uint16_t int_con;
      if (read16(REG_INTCONA, &int_con)) {
        if (intr == MCP23017_INT_ANY_EDGE) {
          // Pin value is compared against the previous pin value
          int_con &= ~mask;
        } else {
          // Pin value is compared against the associated bit in the DEFVAL register
          int_con |= mask;

          // DEFVAL: DEFAULT VALUE REGISTER (ADDR 0x03)
          // Sets the compare value for pins configured for interrupt-on-change from defaults
          // If the associated pin level is the opposite from the register bit, an interrupt occurs. 
          uint16_t int_def;
          if (read16(REG_DEFVALA, &int_def)) {
            if (intr == MCP23017_INT_LOW_EDGE) {
              int_def |= mask;
            } else {
              int_def &= ~mask;
            };
            if (!write16(REG_DEFVALA, int_def)) return false;
          };
        };
        if (!write16(REG_INTCONA, int_con)) return false;
      };
    };
    return write16(REG_GPINTENA, int_en);
  };
  return false;
}

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

Ну а при возникновении прерывания вызываем другую функцию:

bool portOnInterrupt(bool useIntCap);

Аргумент useIntCap указывает на то, что нужно прочитать регистр “снимка на момент прерывания”, иначе будет прочитано текущее состояние входов.

bool reMCP23017::portOnInterrupt(bool useIntCap)
{
  uint16_t flags;
  // Read which PINs caused the interrupt
  if (read16(REG_INTFA, &flags)) {
    if (flags != 0) {
      // At least one PIN caused an interrupt
      uint16_t pins;
      if (useIntCap) {
        // Read PINs at the moment of interrupt generation
        if (!read16(REG_INTCAPA, &pins)) {
          return false;
        };
      } else {
        // Read PINs for the current moment
        if (!read16(REG_GPIOA, &pins)) {
          return false;
        };
      };
      
      // Prepare data for events
      gpio_data_t data;
      data.bus = (uint8_t)_numI2C + 1;
      data.address = _addrI2C;

      // Send events only for pins that have changed
      for (uint8_t i = 0; i < 16; i++) {
        if ((flags & (1 << i)) > 0) {
          data.pin = i;
          data.value = (uint8_t)((pins & (1 << i)) > 0);
          if (_callback) {
            _callback((void*)this, data, 0);
          } else {
            eventLoopPost(RE_GPIO_EVENTS, RE_GPIO_CHANGE, &data, sizeof(data), portMAX_DELAY);
          };
        };
      };
    };
    return true;
  };
  return false;
}

Для чего это нужно? Ну например для того, чтобы подавить дребезг контактов – в этом случае стоит на некоторое время заблокировать реакцию на прерывания и прочитать входы с этой задержкой.

Ну а если вам важно не пропустить ни одного импульса, даже самого короткого – используйте “снимок” INTCAP.

 


Практическое применение

Я очень часто использую эту микросхему в своих поделках. Нормальные GPIO позволяют не задумываться о том, как подключать входы, какие выбирать реле, а достаточно большой допустимый ток выводов позволяет управлять светодиодами напрямую, без дополнительных транзисторов.

Текущая конструкция на этой микросхеме
Расширитель GPIO MCP23017
Расширитель GPIO MCP23017

 

Как выглядит код в этом случае. Замечу, что в данном конкретном случае не все выводы используется на вход, несколько использует на выход:

Расширитель GPIO MCP23017

Инициализация микросхемы:

Расширитель GPIO MCP23017

gpioIsrInputs1 в данном случае – сторонний класс, который отвечает за обработку прерываний на любом входе, он используется для отлова прерываний от данной микросхемы на самом МК. Интересно про него почитать?

Поскольку вместо классических callback-ов я использую события, то мне нужно обработать эти события:

Расширитель GPIO MCP23017

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

На этом всё. Есть вопросы – задавайте!

 


Ссылки

  1. MCP23017 datasheet
  2. Библиотека MCP23017 для ESP-IDF

 

Если вам понравилась статья и вы желаете отблагодарить автора – просто кликните по любому рекламному объявлению.

💠 Полный архив статей вы найдете здесь

 


Пожалуйста, оцените статью:
[ 5 из 5, всего 2 оценок ]

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *