diff --git a/src/devices/Mcp23xxx/Bank.cs b/src/devices/Mcp23xxx/Bank.cs
new file mode 100644
index 0000000000..d05c1f14e1
--- /dev/null
+++ b/src/devices/Mcp23xxx/Bank.cs
@@ -0,0 +1,25 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Iot.Device.Mcp23xxx
+{
+ ///
+ /// The MCP28XXX family has an address mapping concept for accessing registers.
+ /// This provides a way to easily address registers by group or type.
+ ///
+ public enum Bank
+ {
+ ///
+ /// This mode is used specifically for 16-bit devices where it causes the
+ /// address pointer to toggle between associated A/B register pairs.
+ ///
+ Bank0 = 0,
+ ///
+ /// This mode is used to group each port's registers together.
+ /// This mode is the default since 8-bit devices only have one port and
+ /// 16-bit devices are initialized in this state.
+ ///
+ Bank1 = 1
+ }
+}
diff --git a/src/devices/Mcp23xxx/Mcp23xxx.cs b/src/devices/Mcp23xxx/Mcp23xxx.cs
new file mode 100644
index 0000000000..bd8f409bc9
--- /dev/null
+++ b/src/devices/Mcp23xxx/Mcp23xxx.cs
@@ -0,0 +1,78 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Device.Gpio;
+using System.Device.Spi;
+
+namespace Iot.Device.Mcp23xxx
+{
+ public class Mcp23xxx : IDisposable
+ {
+ private readonly SpiDevice _spiDevice;
+
+ private enum CommunicationProtocol
+ {
+ I2c,
+ Spi
+ }
+
+ public Mcp23xxx(SpiDevice spiDevice)
+ {
+ _spiDevice = spiDevice;
+ }
+
+ public void Dispose()
+ {
+ }
+
+ public byte Read(int deviceAddress, Register.Address registerAddress, Port port = Port.PortA, Bank bank = Bank.Bank1)
+ {
+ byte opCode = OpCode.GetOpCode(deviceAddress, true);
+ byte mappedAddress = Register.GetMappedAddress(registerAddress, port, bank);
+ byte[] writeBuffer = new byte[] { opCode, mappedAddress, 0 };
+ byte[] readBuffer = new byte[3];
+
+ _spiDevice.TransferFullDuplex(writeBuffer, readBuffer);
+ return readBuffer[2];
+ }
+
+ public byte[] Read(int deviceAddress, Register.Address startingRegisterAddress, byte byteCount, Port port = Port.PortA, Bank bank = Bank.Bank1)
+ {
+ byte opCode = OpCode.GetOpCode(deviceAddress, true);
+ byte mappedAddress = Register.GetMappedAddress(startingRegisterAddress, port, bank);
+
+ byteCount += 2; // Include OpCode and Register Address.
+ byte[] writeBuffer = new byte[byteCount];
+ writeBuffer[0] = opCode;
+ writeBuffer[1] = (byte)startingRegisterAddress;
+ byte[] readBuffer = new byte[byteCount];
+
+ _spiDevice.TransferFullDuplex(writeBuffer, readBuffer);
+ return readBuffer.AsSpan().Slice(2).ToArray(); // First 2 bytes are from sending OpCode and Register Address.
+ }
+
+ public void Write(int deviceAddress, Register.Address registerAddress, byte data, Port port = Port.PortA, Bank bank = Bank.Bank1)
+ {
+ byte opCode = OpCode.GetOpCode(deviceAddress, false);
+ byte mappedAddress = Register.GetMappedAddress(registerAddress, port, bank);
+ byte[] writeBuffer = new byte[] { opCode, mappedAddress, data };
+
+ _spiDevice.Write(writeBuffer);
+ }
+
+ public void Write(int deviceAddress, Register.Address startingRegisterAddress, byte[] data, Port port = Port.PortA, Bank bank = Bank.Bank1)
+ {
+ byte opCode = OpCode.GetOpCode(deviceAddress, false);
+ byte mappedAddress = Register.GetMappedAddress(startingRegisterAddress, port, bank);
+
+ byte[] writeBuffer = new byte[data.Length + 2]; // Include OpCode and Register Address.
+ writeBuffer[0] = opCode;
+ writeBuffer[1] = (byte)startingRegisterAddress;
+ data.CopyTo(writeBuffer, 2);
+
+ _spiDevice.Write(writeBuffer);
+ }
+ }
+}
diff --git a/src/devices/Mcp23xxx/Mcp23xxx.csproj b/src/devices/Mcp23xxx/Mcp23xxx.csproj
new file mode 100644
index 0000000000..20f1a92874
--- /dev/null
+++ b/src/devices/Mcp23xxx/Mcp23xxx.csproj
@@ -0,0 +1,22 @@
+
+
+
+ netcoreapp2.1
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/devices/Mcp23xxx/OpCode.cs b/src/devices/Mcp23xxx/OpCode.cs
new file mode 100644
index 0000000000..24455436c6
--- /dev/null
+++ b/src/devices/Mcp23xxx/OpCode.cs
@@ -0,0 +1,21 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Iot.Device.Mcp23xxx
+{
+ public class OpCode
+ {
+ public static byte GetOpCode(int deviceAddress, bool isReadCommand)
+ {
+ int opCode = 0b0100_0000 | (deviceAddress << 1);
+
+ if (isReadCommand)
+ {
+ opCode |= 0b000_0001; // Set read bit.
+ }
+
+ return (byte)opCode;
+ }
+ }
+}
diff --git a/src/devices/Mcp23xxx/Port.cs b/src/devices/Mcp23xxx/Port.cs
new file mode 100644
index 0000000000..6ed1e13562
--- /dev/null
+++ b/src/devices/Mcp23xxx/Port.cs
@@ -0,0 +1,12 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Iot.Device.Mcp23xxx
+{
+ public enum Port
+ {
+ PortA,
+ PortB
+ }
+}
diff --git a/src/devices/Mcp23xxx/README.md b/src/devices/Mcp23xxx/README.md
new file mode 100644
index 0000000000..158e6332fd
--- /dev/null
+++ b/src/devices/Mcp23xxx/README.md
@@ -0,0 +1,44 @@
+# Microchip Mcp23xxx
+
+## Summary
+The MCP23XXX device family provides 8/16-bit, general purpose parallel I/O expansion for I2C or SPI applications. These devices include a range of addressing schemes and I/O configurations including pull-up resistors, polarity inverting, and interrupts.
+
+## Device Family
+MCP23XXX devices contain different markings to distinguish features like interfacing, packaging, and temperature ratings. For example, MCP23017 contains an I2C interface and MCP23S17 contains a SPI interface. Please review specific datasheet for more information.
+
+**MCP23X08**: http://ww1.microchip.com/downloads/en/DeviceDoc/21919e.pdf
+**MCP23X09**: http://ww1.microchip.com/downloads/en/DeviceDoc/20002121C.pdf
+**MCP23X17**: http://ww1.microchip.com/downloads/en/DeviceDoc/20001952C.pdf
+**MCP23X18**: http://ww1.microchip.com/downloads/en/DeviceDoc/22103a.pdf
+
+**NOTE**: MCP23X16 contains different internal circuitry and is not compatible with this binding.
+
+## Binding Notes
+
+### Register Banking
+The number of ports vary between Mcp23xxx devices depending if it is 8-bit (1 port) or 16-bit (2 ports). The internal circuitry has a banking concept to group by port registers or by register type. This enables different configurations for reading/writing schemes.
+
+To allow this binding to work across the device family, you must use the provided arguments when using Reading/Writing methods.
+
+#### Example for 16-bit device
+The MCP23X17 has registers defaulted to Bank 1, which group port registers by type. It is recommended to use the optional arguments for port and bank when addressing the correct register.
+
+``` csharp
+// Read Port B's Input Polarity Port Register (IPOL) from device 3.
+byte data = mcp23xxx.Read(3, Register.Address.IPOL, Port.PortB, Bank.Bank0);
+
+// If the device is configured for Bank 1, you can ignore the optional argument.
+byte data = mcp23xxx.Read(3, Register.Address.IPOL, Port.PortB);
+```
+#### Example for 8-bit device
+The MCP23X08 only contains 1 port so you must use Bank 1 when addressing the correct register. In this case, the optional arguments can be ignored.
+
+``` csharp
+// Read port A's GPIO Pull-Up Resistor Register (GPPU) from device 1.
+byte data = mcp23xxx.Read(1, Register.Address.GPPU);
+// or..
+byte data = mcp23xxx.Read(1, Register.Address.GPPU, Port.PortA, Bank.Bank1);
+```
+
+## References
+https://www.adafruit.com/product/732
diff --git a/src/devices/Mcp23xxx/Register.cs b/src/devices/Mcp23xxx/Register.cs
new file mode 100644
index 0000000000..3d309947b3
--- /dev/null
+++ b/src/devices/Mcp23xxx/Register.cs
@@ -0,0 +1,207 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace Iot.Device.Mcp23xxx
+{
+ public class Register
+ {
+ public enum Address
+ {
+ ///
+ /// Controls the direction of the data I/O.
+ /// When a bit is set, the corresponding pin becomes an input.
+ /// When a bit is clear, the corresponding pin becomes an output.
+ ///
+ IODIR = 0b0000_0000,
+ ///
+ /// Configures the polarity on the corresponding GPIO port bits.
+ /// When a bit is set, the corresponding GPIO register bit will reflect the inverted value on the pin.
+ ///
+ IPOL = 0b0000_0001,
+ ///
+ /// Controls the interrupt-on-change feature for each pin.
+ /// When a bit is set, the corresponding pin is enabled for interrupt-on-change.
+ /// The DEFVAL and INTCON registers must also be configured if any pins are enabled for interrupt-on-change.
+ ///
+ GPINTEN = 0b0000_0010,
+ ///
+ /// Configures the default comparison value.
+ /// If enabled (via GPINTEN and INTCON) to compare against the DEFVAL register,
+ /// an opposite value on the associated pin will cause an interrupt to occur.
+ ///
+ DEFVAL = 0b0000_0011,
+ ///
+ /// Controls how the associated pin value is compared for the interrupt-on-change feature.
+ /// When a bit is set, the corresponding I/O pin is compared against the associated bit in the DEFVAL register.
+ /// When a bit value is clear, the corresponding I/O pin is compared against the previous value.
+ ///
+ INTCON = 0b0000_0100,
+ ///
+ /// Contains several bits for configuring the device. See respective datasheet for more details.
+ ///
+ IOCON = 0b0000_0101,
+ ///
+ /// Controls the pull-up resistors for the port pins.
+ /// When a bit is set and the corresponding pin is configured as an input,
+ /// the corresponding port pin is internally pulled up with a 100 kΩ resistor.
+ ///
+ GPPU = 0b0000_0110,
+ ///
+ /// Reflects the interrupt condition on the port pins of any pin that is enabled for interrupts via the GPINTEN register.
+ /// A 'set' bit indicates that the associated pin caused the interrupt.
+ /// This register is read-only. Writes to this register will be ignored.
+ ///
+ INTF = 0b0000_0111,
+ ///
+ /// The INTCAP register captures the GPIO port value at the time the interrupt occurred.
+ /// The register is read-only and is updated only when an interrupt occurs.
+ /// The register will remain unchanged until the interrupt is cleared via a read of INTCAP or GPIO.
+ ///
+ INTCAP = 0b0000_1000,
+ ///
+ /// Reflects the value on the port. Reading from this register reads the port.
+ /// Writing to this register modifies the Output Latch (OLAT) register.
+ ///
+ GPIO = 0b0000_1001,
+ ///
+ /// Provides access to the output latches.
+ /// A read from this register results in a read of the OLAT and not the port itself.
+ /// A write to this register modifies the output latches that modify the pins configured as outputs.
+ ///
+ OLAT = 0b0000_1010
+ }
+
+ public static byte GetMappedAddress(Address address, Port port = Port.PortA, Bank bank = Bank.Bank1)
+ {
+ byte mappedAddress;
+
+ if (bank == Bank.Bank1)
+ {
+ mappedAddress = GetMappedAddressBank1(address, port);
+ }
+ else
+ {
+ mappedAddress = GetMappedAddressBank0(address, port);
+ }
+
+ return mappedAddress;
+ }
+
+ private static byte GetMappedAddressBank0(Address address, Port port)
+ {
+ byte mappedAddress;
+
+ if (port == Port.PortA)
+ {
+ mappedAddress = GetMappedAddressBank0PortA(address);
+ }
+ else
+ {
+ mappedAddress = GetMappedAddressBank0PortB(address);
+ }
+
+ return mappedAddress;
+ }
+
+ private static byte GetMappedAddressBank0PortA(Address address)
+ {
+ byte mappedAddress = 0;
+
+ switch (address)
+ {
+ case Address.IODIR:
+ mappedAddress = 0b0000_0000;
+ break;
+ case Address.IPOL:
+ mappedAddress = 0b0000_0010;
+ break;
+ case Address.GPINTEN:
+ mappedAddress = 0b0000_0100;
+ break;
+ case Address.DEFVAL:
+ mappedAddress = 0b0000_0110;
+ break;
+ case Address.INTCON:
+ mappedAddress = 0b0000_1000;
+ break;
+ case Address.IOCON:
+ mappedAddress = 0b0000_1010;
+ break;
+ case Address.GPPU:
+ mappedAddress = 0b0000_1100;
+ break;
+ case Address.INTF:
+ mappedAddress = 0b0000_1110;
+ break;
+ case Address.INTCAP:
+ mappedAddress = 0b0001_0000;
+ break;
+ case Address.GPIO:
+ mappedAddress = 0b0001_0010;
+ break;
+ case Address.OLAT:
+ mappedAddress = 0b0001_0100;
+ break;
+ }
+
+ return mappedAddress;
+ }
+
+ private static byte GetMappedAddressBank0PortB(Address address)
+ {
+ byte mappedAddress = 0;
+
+ switch (address)
+ {
+ case Address.IODIR:
+ mappedAddress = 0b0000_0001;
+ break;
+ case Address.IPOL:
+ mappedAddress = 0b0000_0011;
+ break;
+ case Address.GPINTEN:
+ mappedAddress = 0b0000_0101;
+ break;
+ case Address.DEFVAL:
+ mappedAddress = 0b0000_0111;
+ break;
+ case Address.INTCON:
+ mappedAddress = 0b0000_1001;
+ break;
+ case Address.IOCON:
+ mappedAddress = 0b0000_1011;
+ break;
+ case Address.GPPU:
+ mappedAddress = 0b0000_1101;
+ break;
+ case Address.INTF:
+ mappedAddress = 0b0000_1111;
+ break;
+ case Address.INTCAP:
+ mappedAddress = 0b0001_0001;
+ break;
+ case Address.GPIO:
+ mappedAddress = 0b0001_0011;
+ break;
+ case Address.OLAT:
+ mappedAddress = 0b0001_0101;
+ break;
+ }
+
+ return mappedAddress;
+ }
+
+ private static byte GetMappedAddressBank1(Address address, Port port)
+ {
+ byte mappedAddress = (byte)address;
+
+ if (port == Port.PortB)
+ {
+ mappedAddress |= 0b0001_0000;
+ }
+
+ return mappedAddress;
+ }
+ }
+}
diff --git a/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.fzz b/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.fzz
new file mode 100644
index 0000000000..4d82ebc5a2
Binary files /dev/null and b/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.fzz differ
diff --git a/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.png b/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.png
new file mode 100644
index 0000000000..b2fb10794b
Binary files /dev/null and b/src/devices/Mcp23xxx/samples/Mcp23S17_I2c_ReadSwitches_WriteLeds.png differ
diff --git a/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.fzz b/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.fzz
new file mode 100644
index 0000000000..4a6be5e364
Binary files /dev/null and b/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.fzz differ
diff --git a/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.png b/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.png
new file mode 100644
index 0000000000..a677824611
Binary files /dev/null and b/src/devices/Mcp23xxx/samples/Mcp23S17_Spi_ReadSwitches_WriteLeds.png differ
diff --git a/src/devices/Mcp23xxx/samples/Mcp23xxx.Sample.cs b/src/devices/Mcp23xxx/samples/Mcp23xxx.Sample.cs
new file mode 100644
index 0000000000..3290ecdc9a
--- /dev/null
+++ b/src/devices/Mcp23xxx/samples/Mcp23xxx.Sample.cs
@@ -0,0 +1,108 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Device.Spi;
+using System.Device.Spi.Drivers;
+using System.Threading;
+
+namespace Iot.Device.Mcp23xxx.Samples
+{
+ class Program
+ {
+ private static readonly int s_deviceAddress = 0;
+
+ static void Main(string[] args)
+ {
+ Console.WriteLine("Hello Mcp23xxx!");
+
+ Mcp23xxx mcp23xxx = GetMcp23xxxWithSpi();
+
+ // Uncomment sample to run.
+ ReadSwitchesWriteLeds(mcp23xxx);
+ // ReadAllRegisters(mcp23xxx);
+ // WriteSequentialBytes(mcp23xxx);
+ }
+
+ private static Mcp23xxx GetMcp23xxxWithSpi()
+ {
+ Console.WriteLine("Using SPI protocol");
+
+ var connection = new SpiConnectionSettings(0, 0)
+ {
+ ClockFrequency = 1000000,
+ Mode = SpiMode.Mode0
+ };
+
+ var spi = new UnixSpiDevice(connection);
+ var mcp23xxx = new Mcp23xxx(spi);
+ return mcp23xxx;
+ }
+
+ private static void ReadSwitchesWriteLeds(Mcp23xxx mcp23xxx)
+ {
+ Console.WriteLine("Read Switches & Write LEDs");
+
+ using (mcp23xxx)
+ {
+ // Input direction for switches.
+ mcp23xxx.Write(s_deviceAddress, Register.Address.IODIR, 0b1111_1111, Port.PortA, Bank.Bank0);
+ // Output direction for LEDs.
+ mcp23xxx.Write(s_deviceAddress, Register.Address.IODIR, 0b0000_0000, Port.PortB, Bank.Bank0);
+
+ while (true)
+ {
+ // Read switches.
+ byte data = mcp23xxx.Read(s_deviceAddress, Register.Address.GPIO, Port.PortA, Bank.Bank0);
+ // Write data to LEDs.
+ mcp23xxx.Write(s_deviceAddress, Register.Address.GPIO, data, Port.PortB, Bank.Bank0);
+ Console.WriteLine(data);
+ Thread.Sleep(500);
+ }
+ }
+ }
+
+ private static void ReadAllRegisters(Mcp23xxx mcp23xxx)
+ {
+ // This assumes the device is in default Sequential Operation mode.
+ Console.WriteLine("Read All Registers");
+
+ using (mcp23xxx)
+ {
+ // Start at first register. Total of 22 registers for MCP23x17.
+ byte[] data = mcp23xxx.Read(s_deviceAddress, 0, 22, Port.PortA, Bank.Bank0);
+
+ for (int index = 0; index < data.Length; index++)
+ {
+ Console.WriteLine($"0x{index:X2}: 0x{data[index]:X2}");
+ }
+ }
+ }
+
+ private static void WriteSequentialBytes(Mcp23xxx mcp23xxx)
+ {
+ // This assumes the device is in default Sequential Operation mode.
+ Console.WriteLine("Write Sequential Bytes");
+
+ using (mcp23xxx)
+ {
+ void SequentialRead(Mcp23xxx mcp)
+ {
+ byte[] dataRead = mcp23xxx.Read(s_deviceAddress, 0, 2, Port.PortA, Bank.Bank0);
+ Console.WriteLine($"\tIODIRA: 0x{dataRead[0]:X2}");
+ Console.WriteLine($"\tIODIRB: 0x{dataRead[1]:X2}");
+ }
+
+ Console.WriteLine("Before Write");
+ SequentialRead(mcp23xxx);
+
+ byte[] dataWrite = new byte[] { 0x12, 0x34 };
+ mcp23xxx.Write(s_deviceAddress, 0, dataWrite, Port.PortA, Bank.Bank0);
+
+ Console.WriteLine("After Write");
+ SequentialRead(mcp23xxx);
+ }
+ }
+ }
+}
diff --git a/src/devices/Mcp23xxx/samples/Mcp23xxx.Samples.csproj b/src/devices/Mcp23xxx/samples/Mcp23xxx.Samples.csproj
new file mode 100644
index 0000000000..16c9c17431
--- /dev/null
+++ b/src/devices/Mcp23xxx/samples/Mcp23xxx.Samples.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ netcoreapp2.1
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/devices/Mcp23xxx/samples/README.md b/src/devices/Mcp23xxx/samples/README.md
new file mode 100644
index 0000000000..351cc8341e
--- /dev/null
+++ b/src/devices/Mcp23xxx/samples/README.md
@@ -0,0 +1,13 @@
+# Microchip Mcp23xxx Samples
+
+## Read switches and write value to LEDs
+
+This example shows how to read from an 8-pin DIP switch and write the value out to 8 LEDs using an MCP23X17 device and a RPi3.
+
+### SPI Interface
+
+
+
+### I2C Interface
+
+
diff --git a/src/devices/Mcp23xxx/tests/Mcp23xxx.Tests.csproj b/src/devices/Mcp23xxx/tests/Mcp23xxx.Tests.csproj
new file mode 100644
index 0000000000..50858a4a81
--- /dev/null
+++ b/src/devices/Mcp23xxx/tests/Mcp23xxx.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.1
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/devices/Mcp23xxx/tests/OpCodeTests.cs b/src/devices/Mcp23xxx/tests/OpCodeTests.cs
new file mode 100644
index 0000000000..00f57d0241
--- /dev/null
+++ b/src/devices/Mcp23xxx/tests/OpCodeTests.cs
@@ -0,0 +1,36 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace Iot.Device.Mcp23xxx.Tests
+{
+ public class OpCodeTests
+ {
+ [Theory]
+ // Writing
+ [InlineData(0, false, 0b0100_0000)]
+ [InlineData(1, false, 0b0100_0010)]
+ [InlineData(2, false, 0b0100_0100)]
+ [InlineData(3, false, 0b0100_0110)]
+ [InlineData(4, false, 0b0100_1000)]
+ [InlineData(5, false, 0b0100_1010)]
+ [InlineData(6, false, 0b0100_1100)]
+ [InlineData(7, false, 0b0100_1110)]
+ // Reading
+ [InlineData(0, true, 0b0100_0001)]
+ [InlineData(1, true, 0b0100_0011)]
+ [InlineData(2, true, 0b0100_0101)]
+ [InlineData(3, true, 0b0100_0111)]
+ [InlineData(4, true, 0b0100_1001)]
+ [InlineData(5, true, 0b0100_1011)]
+ [InlineData(6, true, 0b0100_1101)]
+ [InlineData(7, true, 0b0100_1111)]
+ public void Get_OpCode(int deviceAddress, bool isReadCommand, byte expectedOpCode)
+ {
+ byte actualOpCode = OpCode.GetOpCode(deviceAddress, isReadCommand);
+ Assert.Equal(expectedOpCode, actualOpCode);
+ }
+ }
+}
diff --git a/src/devices/Mcp23xxx/tests/RegisterTests.cs b/src/devices/Mcp23xxx/tests/RegisterTests.cs
new file mode 100644
index 0000000000..95a9934f4d
--- /dev/null
+++ b/src/devices/Mcp23xxx/tests/RegisterTests.cs
@@ -0,0 +1,66 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Xunit;
+
+namespace Iot.Device.Mcp23xxx.Tests
+{
+ public class RegisterTests
+ {
+ [Theory]
+ // Port A; Bank 0
+ [InlineData(Register.Address.IODIR, Port.PortA, Bank.Bank0, 0b0000_0000)]
+ [InlineData(Register.Address.IPOL, Port.PortA, Bank.Bank0, 0b0000_0010)]
+ [InlineData(Register.Address.GPINTEN, Port.PortA, Bank.Bank0, 0b0000_0100)]
+ [InlineData(Register.Address.DEFVAL, Port.PortA, Bank.Bank0, 0b0000_0110)]
+ [InlineData(Register.Address.INTCON, Port.PortA, Bank.Bank0, 0b0000_1000)]
+ [InlineData(Register.Address.IOCON, Port.PortA, Bank.Bank0, 0b0000_1010)]
+ [InlineData(Register.Address.GPPU, Port.PortA, Bank.Bank0, 0b0000_1100)]
+ [InlineData(Register.Address.INTF, Port.PortA, Bank.Bank0, 0b0000_1110)]
+ [InlineData(Register.Address.INTCAP, Port.PortA, Bank.Bank0, 0b0001_0000)]
+ [InlineData(Register.Address.GPIO, Port.PortA, Bank.Bank0, 0b0001_0010)]
+ [InlineData(Register.Address.OLAT, Port.PortA, Bank.Bank0, 0b0001_0100)]
+ // Port A; Bank 1
+ [InlineData(Register.Address.IODIR, Port.PortA, Bank.Bank1, 0b0000_0000)]
+ [InlineData(Register.Address.IPOL, Port.PortA, Bank.Bank1, 0b0000_0001)]
+ [InlineData(Register.Address.GPINTEN, Port.PortA, Bank.Bank1, 0b0000_0010)]
+ [InlineData(Register.Address.DEFVAL, Port.PortA, Bank.Bank1, 0b0000_0011)]
+ [InlineData(Register.Address.INTCON, Port.PortA, Bank.Bank1, 0b0000_0100)]
+ [InlineData(Register.Address.IOCON, Port.PortA, Bank.Bank1, 0b0000_0101)]
+ [InlineData(Register.Address.GPPU, Port.PortA, Bank.Bank1, 0b0000_0110)]
+ [InlineData(Register.Address.INTF, Port.PortA, Bank.Bank1, 0b0000_0111)]
+ [InlineData(Register.Address.INTCAP, Port.PortA, Bank.Bank1, 0b0000_1000)]
+ [InlineData(Register.Address.GPIO, Port.PortA, Bank.Bank1, 0b0000_1001)]
+ [InlineData(Register.Address.OLAT, Port.PortA, Bank.Bank1, 0b0000_1010)]
+ // Port B; Bank 0
+ [InlineData(Register.Address.IODIR, Port.PortB, Bank.Bank0, 0b0000_0001)]
+ [InlineData(Register.Address.IPOL, Port.PortB, Bank.Bank0, 0b0000_0011)]
+ [InlineData(Register.Address.GPINTEN, Port.PortB, Bank.Bank0, 0b0000_0101)]
+ [InlineData(Register.Address.DEFVAL, Port.PortB, Bank.Bank0, 0b0000_0111)]
+ [InlineData(Register.Address.INTCON, Port.PortB, Bank.Bank0, 0b0000_1001)]
+ [InlineData(Register.Address.IOCON, Port.PortB, Bank.Bank0, 0b0000_1011)]
+ [InlineData(Register.Address.GPPU, Port.PortB, Bank.Bank0, 0b0000_1101)]
+ [InlineData(Register.Address.INTF, Port.PortB, Bank.Bank0, 0b0000_1111)]
+ [InlineData(Register.Address.INTCAP, Port.PortB, Bank.Bank0, 0b0001_0001)]
+ [InlineData(Register.Address.GPIO, Port.PortB, Bank.Bank0, 0b0001_0011)]
+ [InlineData(Register.Address.OLAT, Port.PortB, Bank.Bank0, 0b0001_0101)]
+ // Port B; Bank 1
+ [InlineData(Register.Address.IODIR, Port.PortB, Bank.Bank1, 0b0001_0000)]
+ [InlineData(Register.Address.IPOL, Port.PortB, Bank.Bank1, 0b0001_0001)]
+ [InlineData(Register.Address.GPINTEN, Port.PortB, Bank.Bank1, 0b0001_0010)]
+ [InlineData(Register.Address.DEFVAL, Port.PortB, Bank.Bank1, 0b0001_0011)]
+ [InlineData(Register.Address.INTCON, Port.PortB, Bank.Bank1, 0b0001_0100)]
+ [InlineData(Register.Address.IOCON, Port.PortB, Bank.Bank1, 0b0001_0101)]
+ [InlineData(Register.Address.GPPU, Port.PortB, Bank.Bank1, 0b0001_0110)]
+ [InlineData(Register.Address.INTF, Port.PortB, Bank.Bank1, 0b0001_0111)]
+ [InlineData(Register.Address.INTCAP, Port.PortB, Bank.Bank1, 0b0001_1000)]
+ [InlineData(Register.Address.GPIO, Port.PortB, Bank.Bank1, 0b0001_1001)]
+ [InlineData(Register.Address.OLAT, Port.PortB, Bank.Bank1, 0b0001_1010)]
+ public void Get_Mapped_Address(Register.Address address, Port port, Bank bank, byte expectedMappedAddress)
+ {
+ byte actualMappedAddress = Register.GetMappedAddress(address, port, bank);
+ Assert.Equal(expectedMappedAddress, actualMappedAddress);
+ }
+ }
+}
diff --git a/src/devices/Mcp3008/samples/Mcp3008.Sample.csproj b/src/devices/Mcp3008/samples/Mcp3008.Sample.csproj
new file mode 100644
index 0000000000..679851d330
--- /dev/null
+++ b/src/devices/Mcp3008/samples/Mcp3008.Sample.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ netcoreapp2.1
+
+
+
+
+
+
+
+
+
+
+