数年前に作成したライブラリ。今も一生懸命働いているのに存在すら忘れられていて可哀そうなので今更ながらの公開となった。(笑)
なお、ESP32で動作するかは未検証であるがそれ以外のCPU向けにも移殖は比較的簡単にできるはず。と思う。
MCP23017(I2C)とMCP23S17(SPI)に対応するために基本部分を抽象クラス、インターフェースの違いを継承クラスとして実装している。基本仕様は、pinModeやdigitalRead,digitalWriteなどのArduino互換仕様であるが、pin引数に0xFFを指定すると全ポートが処理対象となる仕様となってるようだ...って、おいおい!(笑)
使い方として注意すべき点は、入出力の効率化と全ポート同時入出力可能とするため、digitalWriteのデータはflushにて出力、digitalReadのデータはrefreshにて読み込みされる点である。flush/refreshを忘れると入出力が行われない。
ちなみに初期設定は次の通り。
I2C 400KHz (SDA=2, SCL=14)※任意のポートに変更可能
SPI 10MHz  (MISO=12, MOSI=13, CLK=14, CS=15)※固定
【抽象クラスの概要】
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  | 
						class MCP23X17 {   public:     MCP23X17(uint8_t address);     ~MCP23X17();     void begin();     virtual void end() = 0;     virtual void setClock(uint32_t frequency) = 0;     MCP23X17& pinMode(uint8_t pin, uint8_t mode);     MCP23X17& pullupMode(uint8_t pin, uint8_t mode);     MCP23X17& inputInvert(uint8_t pin, uint8_t mode);     MCP23X17& digitalWrite(uint8_t pin, uint8_t value);     MCP23X17& flush();     uint8_t digitalRead(uint8_t pin);     MCP23X17& refresh(); }  | 
					
【サンプル・コードの例】
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | 
						#include "mcp23s17.h" //#include "mcp23017.h" #ifdef SPI MCP23S17 GPIO(0); #else MCP23017 GPIO(0, 13, 14); #endif void setup() {   GPIO.begin();   GPIO.pinMode(0, OUTPUT).digitalWrite(0, LOW).flush(); } void loop() {   GPIO.digitalWrite(0, !GPIO.refresh().digitalRead(0)).flush();   delay(1000); }  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66  | 
						#ifndef MCP23X17_H #define MCP23X17_H #include <Arduino.h> // // OPCODE // #define MCP_OPCODE    0x20 // // RESIGTER'S (IOCON[BANK, SEQOP] = 0) // #define MCP_IODIR     0x00 #define MCP_IPOL      0x02 #define MCP_GPINTEN   0x04 #define MCP_DEFVAL    0x06 #define MCP_INTCON    0x08 #define MCP_IOCON     0x0A #define MCP_GPPU      0x0C #define MCP_INTF      0x0E #define MCP_INTCAP    0x10 #define MCP_GPIO      0x12 #define MCP_OLAT      0x14 // // IOCON bits // #define IOCON_INTPOL  0x02 #define IOCON_ODR     0x04 #define IOCON_HAEN    0x08 #define IOCON_DISSLW  0x10 #define IOCON_SEQOP   0x20 #define IOCON_MIRROR  0x40 #define IOCON_BANK    0x80 class MCP23X17 {   private:     uint8_t  _opcode;     uint8_t  _updFlags;     uint16_t _iodirValue;     uint16_t _ipolValue;     uint16_t _gppuValue;     uint16_t _gpioValue;     virtual void init() = 0;     virtual void writeIO(uint8_t *buf, uint8_t len) = 0;     virtual void readIO(uint8_t *buf, uint8_t out_len, uint8_t in_len) = 0;     uint16_t getBit(uint8_t pin);   public:     MCP23X17(uint8_t address);     ~MCP23X17();     void begin();     virtual void end() = 0;     virtual void setClock(uint32_t frequency) = 0;     void writeReg(uint8_t reg, uint16_t val);     uint16_t readReg(uint8_t reg);     MCP23X17& pinMode(uint8_t pin, uint8_t mode);     MCP23X17& pullupMode(uint8_t pin, uint8_t mode);     MCP23X17& inputInvert(uint8_t pin, uint8_t mode);     MCP23X17& digitalWrite(uint8_t pin, uint8_t value);     MCP23X17& flush();     uint8_t digitalRead(uint8_t pin);     MCP23X17& refresh(); }; #endif // MCP23X17_H  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146  | 
						#include "mcp23x17.h" #define _IODIR 1 #define _GPIO  2 #define _GPPU  4 #define _IPOL  8 MCP23X17::MCP23X17(uint8_t address) {   _opcode = (MCP_OPCODE | (address & 0x07)); } MCP23X17::~MCP23X17() { } void MCP23X17::begin() {   //   // Init   //   init();   //   // default value of register   //   _iodirValue = 0xFFFF;   _gpioValue  = 0x0000;   _gppuValue  = 0x0000;   _ipolValue  = 0x0000;   _updFlags   = 0;   //   // Initial Set (POR/RST value)   //   writeReg(0x05, 0);                             // 0x05 = IOCON (BANK = 1)   uint8_t init[] = {_opcode, MCP_IODIR,     lowByte(_iodirValue), highByte(_iodirValue), // IODIR     lowByte(_ipolValue), highByte(_ipolValue),   // IPOL     0, 0,                                        // GPINTEN     0, 0,                                        // DEFVAL     0, 0,                                        // INTCON     IOCON_HAEN, IOCON_HAEN,                      // IOCON     lowByte(_gppuValue), highByte(_gppuValue),   // GPPU     0, 0,                                        // INTF     0, 0,                                        // INTCAP     lowByte(_gpioValue), highByte(_gpioValue)    // GPIO   };   writeIO(init, sizeof(init)); } void MCP23X17::writeReg(uint8_t reg, uint16_t val) {   uint8_t ctrl[] = {_opcode, reg, lowByte(val), highByte(val)};   writeIO(ctrl, sizeof(ctrl)); } uint16_t MCP23X17::readReg(uint8_t reg) {   struct   {     uint8_t  ctrl[2];     uint16_t data;   } x = {{_opcode, reg}, 0};   readIO(x.ctrl, sizeof(x.ctrl), sizeof(x.data));   return x.data; } uint16_t MCP23X17::getBit(uint8_t pin) {   uint16_t rv;   if (pin == 0xFF)     rv = 0xFFFF;   else if (pin & ~0x0F)     rv = 0;   else     rv = (1 << pin);   return rv; } MCP23X17& MCP23X17::pinMode(uint8_t pin, uint8_t mode) {   uint16_t bit = getBit(pin);   if (mode != OUTPUT)     _iodirValue |=  bit;   else     _iodirValue &= ~bit;   _updFlags |= _IODIR;   return *this; } MCP23X17& MCP23X17::pullupMode(uint8_t pin, uint8_t mode) {   uint16_t bit = getBit(pin);   if (mode)     _gppuValue |=  bit;   else     _gppuValue &= ~bit;   _updFlags |= _GPPU;   return *this; } MCP23X17& MCP23X17::inputInvert(uint8_t pin, uint8_t mode) {   uint16_t bit = getBit(pin);   if (mode)     _ipolValue |=  bit;   else     _ipolValue &= ~bit;   _updFlags |= _IPOL;   return *this; } MCP23X17& MCP23X17::digitalWrite(uint8_t pin, uint8_t value) {   uint16_t bit = getBit(pin) & ~_iodirValue;   if (value)     _gpioValue |=  bit;   else     _gpioValue &= ~bit;   _updFlags |= _GPIO;   return *this; } MCP23X17& MCP23X17::flush() {   if (_updFlags & _IODIR)     writeReg(MCP_IODIR, _iodirValue);   if (_updFlags & _GPPU)     writeReg(MCP_GPPU, _gppuValue);   if (_updFlags & _IPOL)     writeReg(MCP_IPOL, _ipolValue);   if (_updFlags & _GPIO)     writeReg(MCP_GPIO, _gpioValue);   _updFlags = 0;   return *this; } uint8_t MCP23X17::digitalRead(uint8_t pin) {   return _gpioValue & getBit(pin) ? HIGH : LOW; } MCP23X17& MCP23X17::refresh() {   _gpioValue = (_gpioValue & ~_iodirValue) | (readReg(MCP_GPIO) & _iodirValue);   return *this; }  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  | 
						#ifndef MCP23S17_H #define MCP23S17_H #include "mcp23x17.h" // // SPI Wiring // //   GPIO12 <--- MISO //   GPIO13 ---> MOSI //   GPIO14 ---> CLK //   GPIO15 ---> CS class MCP23S17 : public MCP23X17 {   private:     void init() override;     void writeIO(uint8_t *buf, uint8_t len) override;     void readIO(uint8_t *buf, uint8_t out_len, uint8_t in_len) override;   public:     MCP23S17(uint8_t address);     ~MCP23S17();     void end() override;     void setClock(uint32_t frequency) override; }; #endif // MCP23S17_H  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42  | 
						#include <SPI.h> #include "mcp23s17.h" MCP23S17::MCP23S17(uint8_t address) : MCP23X17(address) { } MCP23S17::~MCP23S17() { } void MCP23S17::init() {   //   // SPI Init   //   SPI.begin();   SPI.setHwCs(true);   SPI.setDataMode(SPI_MODE0);  // Mode 0   setClock(10000000);          // 10MHz } void MCP23S17::end() {   SPI.end(); } void MCP23S17::setClock(uint32_t frequency) {   SPI.setFrequency(frequency); } void MCP23S17::writeIO(uint8_t *buf, uint8_t len) {   buf[0] = (buf[0] << 1) | 0;   SPI.writeBytes(buf, len); } void MCP23S17::readIO(uint8_t *buf, uint8_t out_len, uint8_t in_len) {   buf[0] = (buf[0] << 1) | 1;   SPI.transferBytes(buf, buf, out_len + in_len); }  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28  | 
						#ifndef MCP23017_H #define MCP23017_H #include "mcp23x17.h" // // I2C Wiring // //   GPIO14 ---> SCL //   GPIO2  <--> SDA class MCP23017 : public MCP23X17 {   private:     uint8_t _sda;     uint8_t _scl;     void init() override;     void writeIO(uint8_t *buf, uint8_t len) override;     void readIO(uint8_t *buf, uint8_t out_len, uint8_t in_len) override;   public:     MCP23017(uint8_t address, uint8_t sda = 2, uint8_t scl = 14);     ~MCP23017();     void end() override;     void setClock(uint32_t frequency) override; }; #endif // MCP23017_H  | 
					
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51  | 
						#include <Wire.h> #include "mcp23017.h" MCP23017::MCP23017(uint8_t address, uint8_t sda, uint8_t scl) : MCP23X17(address) {   _sda = sda;   _scl = scl; } MCP23017::~MCP23017() { } void MCP23017::init() {   //   // I2C Init   //   Wire.begin(_sda, _scl);   setClock(400000); // 400KHz } void MCP23017::end() {   pinMode(_sda, INPUT);   pinMode(_scl, INPUT); } void MCP23017::setClock(uint32_t frequency) {   Wire.setClock(frequency); } void MCP23017::writeIO(uint8_t *buf, uint8_t len) {   Wire.beginTransmission(buf[0]);   Wire.write(buf + 1, len - 1);   Wire.endTransmission(); } void MCP23017::readIO(uint8_t *buf, uint8_t out_len, uint8_t in_len) {   Wire.beginTransmission(buf[0]);   Wire.write(buf + 1, out_len - 1);   Wire.endTransmission(false);   Wire.requestFrom(buf[0], in_len);   while (Wire.available() < in_len)     yield();   for (buf += out_len; in_len > 0; --in_len)     *buf++ = (uint8_t)Wire.read(); }  | 
					
