The MCP23017 is a 16-bit I/O expander with I²C interface, enabling the expansion of input/output pins on microcontrollers. It provides 16 GPIO pins, which can be configured as inputs or outputs, and supports multiple devices on the same I²C bus via unique addresses. The chip offers features like internal pull-ups, interrupt generation, and programmable polarity inversion, making it ideal for applications requiring additional I/O in space-constrained designs.
Operating Voltage | 1.8V to 5.5V |
---|---|
Maximum Clock Speed | 1.7 MHz |
Max. Current of the IC | 150mA |
Max. Current per Pin | 25mA |
I²C Addresses | 0x20 to 0x27 |
Operating Temperature | -40°C to +125°C |
Pin Name | Pin No | Pin type | Function |
---|---|---|---|
GPB0 | 1 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB1 | 2 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB2 | 3 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB3 | 4 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB4 | 5 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB5 | 6 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB6 | 7 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
GPB7 | 8 | I/O | Bidirectional I/O pin. Can be enabled for interrupt-on-change and/or internal weak pull-up resistor. |
VDD | 9 | P | Power |
VSS | 10 | P | Ground |
NC/CS | 11 | I | NC (MCP23017), Chip Select (MCP23S17) |
SCL/SCK | 12 | I | Serial clock input |
SDA/SI | 13 | I/O | Serial data I/O (MCP23017), Serial data input (MCP23S17) |
NC/SO | 14 | O | NC (MCP23017), Serial data out (MCP23S17) |
A0 | 15 | I | Hardware address pin. Must be externally biased. |
A1 | 16 | I | Hardware address pin. Must be externally biased. |
A2 | 17 | I | Hardware address pin. Must be externally biased. |
RESET | 18 | I | Hardware reset. Must be externally biased |
INTB | 19 | O | Interrupt output for PORTB. Can be configured as active-high, active-low or open-drain. |
INTA | 20 | O | Interrupt output for PORTA. Can be configured as active-high, active-low or open-drain. |
GPA0 | 21 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA1 | 22 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA2 | 23 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA3 | 24 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA4 | 25 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA5 | 26 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA6 | 27 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
GPA7 | 28 | I/O | Bidirectional I/O pin. Can be enabledfor interrupt-on-change and/or internal weak pull-up resistor. |
The I²C address of the MCP23017 can be varied using the A0, A1, and A2 pins:
A0 | A1 | A2 | Address |
---|---|---|---|
0 | 0 | 0 | 0x20 |
1 | 0 | 0 | 0x21 |
0 | 1 | 0 | 0x22 |
1 | 1 | 0 | 0x23 |
0 | 0 | 1 | 0x24 |
1 | 0 | 1 | 0x25 |
0 | 1 | 1 | 0x26 |
1 | 1 | 1 | 0x27 |
The I²C interface requires two lines through which data and the clock signal are transmitted. Data is sent over SDA from the master to the slave and vice versa. For this reason, the pins on this data line must be open-drain outputs.
To ensure that the lines still have a defined voltage level, pull-up resistors (here: 4.7 kΩ) are required. If these pull-up resistors are not used, only the internal pull-ups of the microcontroller (e.g. Arduino) are active. As a result, the signal may no longer be transmitted correctly.
The following schematic shows the basic circuit between the Arduino UNO and the MCP23017. The I/O pins are not used at this point.
GPA0 to GPA7 (=A register) and GPB0 to GPB7 (=B register) each form a register that can be accessed
using the Wire.h library. To set all pins of the A and B registers to OUTPUT, you can use
the following code (typically in setup()
):
Wire.beginTransmission(0x20);
Wire.write(0x00); // IODIRA register
Wire.write(0x00); // set all of port A to outputs
Wire.endTransmission();
Wire.beginTransmission(0x20);
Wire.write(0x01); // IODIRB register
Wire.write(0x00); // set all of port B to outputs
Wire.endTransmission();
If you now want to control some of the output pins in loop()
, use the following code:
Wire.beginTransmission(0x20);
Wire.write(0x12); // address port A
Wire.write(??); // value to send
Wire.endTransmission();
Wire.beginTransmission(0x20);
Wire.write(0x13); // address port B
Wire.write(??); // value to send
Wire.endTransmission();
Replace the ??
with the desired binary, hexadecimal, or decimal values.
You can think of each pin as a bit that can be set to 0 (LOW) or 1 (HIGH).
All pins combined as one number are then sent to the MCP23017. For example,
if you want to set pin 7 and pin 1 to HIGH, the value must be 10000010,
which is 0x82 in hexadecimal and 130 in decimal.
The MCP23017 is set to input mode by default, so no additional configuration is needed.
In the following example, we use the basic circuit described above and extend it with 4 push-buttons
connected to pins GPB0, GPB1, GPB2, and GPB3.
#include "Wire.h"
byte inputs=0;
void setup() {
Serial.begin(9600);
Wire.begin(); // wake up I²C bus
}
void loop() {
Wire.beginTransmission(0x20);
Wire.write(0x13); // set MCP23017 memory pointer to GPIOB address
Wire.endTransmission();
Wire.requestFrom(0x20, 1); // request one byte of data from MCP23017
inputs = Wire.read(); // store the incoming byte into "inputs"
if (inputs > 0) {
// if a button was pressed
Serial.println(inputs, BIN); // display the contents of the GPIOB register in binary
delay(200); // for debounce
}
}
Since all pins are read at once, pressing multiple buttons at the same time can also be detected.
The following sketch uses the Adafruit_MCP23017 library and makes all 16 pins blink alternately:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
Adafruit_MCP23017 mcp;
void setup() {
mcp.begin(0); // Init MCP23017 at address 0x20
for (byte i=0; i<16; i++) {
mcp.pinMode(i, OUTPUT);
}
}
void loop() {
for (byte i=0; i<16; i++) {
mcp.digitalWrite(i, HIGH);
}
delay(500);
for (byte i=0; i<16; i++) {
mcp.digitalWrite(i, LOW);
}
delay(500);
}
It is possible to control multiple MCP23017 ICs per Arduino using different addresses. The following schematic and sketch demonstrate this. Again, all output pins will blink — this time from two MCP chips:
#include <Wire.h>
#include <Adafruit_MCP23017.h>
Adafruit_MCP23017 mcp1;
Adafruit_MCP23017 mcp2;
void setup() {
mcp1.begin(0); // Init MCP23017 at address 0x20
mcp2.begin(1); // Init MCP23017 at address 0x21
for (byte i=0; i<16; i++) {
mcp1.pinMode(i, OUTPUT);
mcp2.pinMode(i, OUTPUT);
}
}
void loop() {
for (byte i=0; i<16; i++) {
mcp1.digitalWrite(i, HIGH);
mcp2.digitalWrite(i, HIGH);
}
delay(500);
for (byte i=0; i<16; i++) {
mcp1.digitalWrite(i, LOW);
mcp2.digitalWrite(i, LOW);
}
delay(500);
}