// MCP23017 I2C GPIO Expander Services. // Copyright (C)2020-2023, Philip Munts dba Munts Technologies. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: // // * Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. namespace IO.Devices.MCP23017 { /// /// Encapsulates the MCP23017 I2C I/O GPIO Expander. /// public class Device { private const byte IODIRA = 0x00; private const byte IODIRB = 0x01; private const byte IPOLA = 0x02; private const byte IPOLB = 0x03; private const byte GPINTENA = 0x04; private const byte GPINTENB = 0x05; private const byte DEFVALA = 0x06; private const byte DEFVALB = 0x07; private const byte INTCONA = 0x08; private const byte INTCONB = 0x09; private const byte IOCONA = 0x0A; private const byte IOCONB = 0x0B; private const byte GPPUA = 0x0C; private const byte GPPUB = 0x0D; private const byte INTFA = 0x0E; private const byte INTFB = 0x0F; private const byte INTCAPA = 0x10; private const byte INTCAPB = 0x11; private const byte GPIOA = 0x12; private const byte GPIOB = 0x13; private const byte OLATA = 0x14; private const byte OLATB = 0x15; private const byte IOCON = IOCONA; private const byte IODIR = IODIRA; private const byte IPOL = IPOLA; private const byte GPINTEN = GPINTENA; private const byte DEFVAL = DEFVALA; private const byte INTCON = INTCONA; private const byte GPPU = GPPUA; private const byte INTF = INTFA; private const byte INTCAP = INTCAPA; private const byte GPIODAT = GPIOA; private const byte GPIOLAT = OLATA; private readonly IO.Interfaces.I2C.Device dev; // Preallocate I2C transaction buffers private byte[] outbuf = { 0, 0, 0 }; private byte[] inbuf = { 0, 0 }; private byte ReadRegister8(byte reg) { outbuf[0] = reg; dev.Transaction(outbuf, 1, inbuf, 1); return inbuf[0]; } private void WriteRegister8(byte reg, byte data) { outbuf[0] = reg; outbuf[1] = data; dev.Write(outbuf, 2); } private uint ReadRegister16(byte reg) { outbuf[0] = reg; dev.Transaction(outbuf, 1, inbuf, 2); return (uint)(inbuf[0] + (inbuf[1] << 8)); } private void WriteRegister16(byte reg, uint data) { data &= 0xFFFF; outbuf[0] = reg; outbuf[1] = (byte)(data & 0xFF); outbuf[2] = (byte)(data >> 8); dev.Write(outbuf, 3); } /// /// Minimum I/O channel number. /// public const int MinChannel = 0; /// /// Maximum I/O channel number. /// public const int MaxChannel = 15; /// /// Constructor for a single MCP23017 device. /// /// I2C bus controller object. /// I2C slave address. public Device(IO.Interfaces.I2C.Bus bus, int addr) { // Create an I2C device object dev = new IO.Interfaces.I2C.Device(bus, addr); // Configure the MCP23017 WriteRegister8(IOCON, 0x00); WriteRegister16(IODIR, 0xFFFF); WriteRegister16(IPOL, 0x0000); WriteRegister16(GPINTEN, 0x0000); WriteRegister16(DEFVAL, 0x0000); WriteRegister16(INTCON, 0x0000); } /// /// Data Direction Property (16 bits). /// Bits 0 to 7 correspond to PORT A and bits 8 to 15 correspond to PORT B. /// For each bit, 0=input and 1=output. /// /// /// This property follows the industry standard convention for data /// direction bit polarity (1=output) rather than the MCP23017 /// IODIR register polarity (0=output). /// public uint Direction { get { return ~ReadRegister16(IODIR) & 0xFFFF; } set { WriteRegister16(IODIR, ~value); } } /// /// Data Polarity Property (16 bits). /// Bits 0 to 7 correspond to PORT A and bits 8 to 15 correspond to PORT B. /// For each bit, 0=input and 1=output. /// public uint Polarity { get { return ReadRegister16(IPOL); } set { WriteRegister16(IPOL, value); } } /// /// Input Pullup Property (16 bits). /// Bits 0 to 7 correspond to PORT A and bits 8 to 15 correspond to PORT B. /// For each bit, 0=high impedance and 1=100k pullup. /// public uint Pullups { get { return ReadRegister16(GPPU); } set { WriteRegister16(GPPU, value); } } /// /// Port Data Property (16 bites). /// Bits 0 to 7 correspond to PORT A and bits 8 to 15 correspond to PORT B. /// public uint Port { get { return ReadRegister16(GPIODAT); } set { WriteRegister16(GPIODAT, value); } } /// /// Port A Data Direction Property (8 bits). /// For each bit, 0=input and 1=output. /// /// /// This property follows the industry standard convention for data /// direction bit polarity (1=output) rather than the MCP23017 /// IODIRA register polarity (0=output). /// public byte DirectionA { get { return (byte) ~ReadRegister8(IODIRA); } set { WriteRegister8(IODIRA, (byte) ~value); } } /// /// Port A Data Polarity Property (8 bits). /// For each bit, 0=input and 1=output. /// public byte PolarityA { get { return ReadRegister8(IPOLA); } set { WriteRegister8(IPOLA, value); } } /// /// Port A Input Pullup Property (16 bits). /// For each bit, 0=high impedance and 1=100k pullup. /// public byte PullupsA { get { return ReadRegister8(GPPUA); } set { WriteRegister8(GPPUA, value); } } /// /// Port A Data Property (8 bits). /// public byte PortA { get { return ReadRegister8(GPIOA); } set { WriteRegister8(OLATA, value); } } /// /// Port B Data Direction Property (8 bits). /// For each bit, 0=input and 1=output. /// /// /// This property follows the industry standard convention for data /// direction bit polarity (1=output) rather than the MCP23017 /// IODIRA register polarity (0=output). /// public byte DirectionB { get { return (byte)~ReadRegister8(IODIRB); } set { WriteRegister8(IODIRB, (byte)~value); } } /// /// Port B Data Polarity Property (8 bits). /// For each bit, 0=input and 1=output. /// public byte PolarityB { get { return ReadRegister8(IPOLB); } set { WriteRegister8(IPOLB, value); } } /// /// Port B Input Pullup Property (16 bits). /// For each bit, 0=high impedance and 1=100k pullup. /// public byte PullupsB { get { return ReadRegister8(GPPUB); } set { WriteRegister8(GPPUB, value); } } /// /// Port B Data Property (8 bits). /// public byte PortB { get { return ReadRegister8(GPIOB); } set { WriteRegister8(OLATB, value); } } /// /// Create an MCP23017 GPIO pin object. /// /// MCP23017 channel number (0 to 15). /// GPIO pin data direction. /// Initial GPIO output state. /// GPIO pin object. public IO.Interfaces.GPIO.Pin GPIO_Create(int channel, IO.Interfaces.GPIO.Direction dir, bool state = false) { return new IO.Devices.MCP23017.GPIO.Pin(this, channel, dir, state); } } }