数年前に作成したライブラリ。今も一生懸命働いているのに存在すら忘れられていて可哀そうなので今更ながらの公開となった。(笑)
なお、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(); } |