
Interfacing the Arduino to the MCP23016 IO Expander
Tweet
The MCP23016 is a popular I/O expander integrated circuit manufactured by Microchip. It provides 16 individually programmable I/O pins that can source/sink 25 milliamps per pin up to about 250 milliamps total. Here we will explore how to use the MCP23016 with the ATMEGA168/Arduino.
Note that in this discussion I'll use hex numbers, a digital "1" is five volts and a digital "0" is zero volts. In binary a byte as individual bits (MSB) B00000000 (LSB). The "B' means this is a binary number to the compiler. All zeros is a decimal values of zero, all ones is a decimal value of 255. The MCP23016 can only handle bytes. (8 bits = 1 byte.) All the ports and registers in the MCP23016 use bytes. The programming examples will use the Wire.h library. For more examples of this see:
- Using the ATMEGA168/Arduino with a 24LC08 Serial EEPROM
- Using the ATMEGA168/Arduino with a Ds1307 Real Time Clock
Also MCP23016 I2C I/O Expander spec sheet.
Basics of the MCP23016
Two separate 8-bit I/O ports bit programmable.
Three hardware address pins (A0-A2 pins 16-18) can address up to eight devices at one time.
High-current drive capability per I/O: source/sink 25 milliamps.
- Maximum current sink 300 mA max.
- Maximum current source 250 mA max.
- Total power: 1 watt.
Open-drain interrupt output pin (6) on input change and interrupt port capture register. (Doesn't work very well.)
Internal Power-On Reset.
Polarity inversion register to configure the polarity of the input port data.
The MCP23016 has 12 general purpose registers as follows:
0x00 Port0; can be directly written/read.
0x01 Port1; can be directly written/read.
Defaults to input.
0x02 Output latch 0; write changes output Port0.
0x03 Output latch 1; write changes output Port1.
Write won't work if bit/port set for input.
0x04 Invert input polarity Port0 bit addressable. 1 invert bit, 0 no invert.
0x05 Invert input polarity Port1 bit addressable. 1 invert bit, 0 no invert.
All bits default to 0.
0x06 I/O direction register Port0 bit addressable. 1 = output, 0 = input.
0x07 I/O direction register Port1 bit addressable. 1 = output, 0 = input.
Defaults to all 0 for inputs.
0x08 Interrupt capture register Port0.
0x09 Interrupt capture register Port1.
These are read only, shows state of port during an interrupt.
Connect an LED/resistor to VCC (+5 volts) and to pin 6 of
MCP23016 and LED will blink. Can be used to signal IRQ on
on the ATMEGA168.
See Using Hardware Interrupts ATMEGA168/Arduino
0x0A I/O control register 1
0x0B I/O control register 1 (same thing as 0x0A)
A one-bit register that controls the sampling frequency of the 2 I/O ports.
A "0" results in a 32 millisecond sample rate.
A "1" results in a 200 microsecond sample rate, but uses more power in standby.

Basic I2C setup.
The MCP23016 I/O expander is an I2C slave device with a base address of 0x20. It has additional three-bit address pins A0, A1, A2 on pins 16, 17, and 18 respectively. This allows eight individual devices to be connected at one time with individual addresses of 0x20 through 0x27. (Hex numbers!)
Example 1. In the setup sections of the two sample programs the MCP23016 is setup as follows to make Port0 all output and Port1 all inputs by writing to the two I/O direction registers:
Wire.begin(); // connect the Arduino as a master
Wire.beginTransmission(0x20); // setup out direction registers
Wire.send(0x06); // pointer
Wire.send(0x00); // DDR Port0 all output
Wire.send(0xFF); // DDR Port1 all input 0xFF = B11111111
Wire.endTransmission();
Example 2. What if I wanted both ports to be inputs, but invert the input polarity (a 1 becomes a 0) on Port1 where my switches are connected?
Wire.begin(); // connect the Arduino as a master, setup data direction
Wire.beginTransmission(0x20); // setup out direction registers
Wire.send(0x04); // pointer
Wire.send(0x00); // DDR Port0 no invert Port0
Wire.send(0xFF); // DDR invert input polarity port1 0xFF = B11111111
Wire.send(0xFF); // DDR Port0 all input 0xFF = B11111111
Wire.send(0xFF); // DDR Port1 all input 0xFF = B11111111
Wire.endTransmission();
When the eight switches are open (see schematic) the normal input to Port1 pins are all "0". In program two below all the LEDs on Port0 will be on, will go off when switch is closed. As it is below all LEDs on Port0 will come on only when corresponding switch on Port1 is closed.
/* The address for the MCP23016 is 0x20. There are eight resistor/LED indicators on pins 21 - 28. This demo produces a binary count of 0 to 255 on Port0 on the MCP23016. */ #include <Wire.h> // specify use of Wire.h library. int i = 0; void setup() { Wire.begin(); Wire.beginTransmission(0x20); // setup out direction registers Wire.write(0x06); // pointer Wire.write(0x00); // DDR Port0 all output Wire.write(0xFF); // DDR Port1 all input Wire.endTransmission(); } void loop() { i++; Wire.beginTransmission(0x20); // set mcp23016 for all output Wire.write(0x00); // begin here Wire.write(i); Wire.endTransmission(); if (i > 255) i = 0; delay(1000); // } } // end loop
- Arduino Projects Revisited Revised
- Schematic for Following Projects
- Programming ADS1115 4-Channel I2C ADC with Arduino
- Arduino uses ADS1115 with TMP37 to Measure Temperature
- Connect Arduino to I2C Liquid Crystal Display
- Arduino Reads Temperature Sensor Displays Temperature on LCD Display
- Arduino with MCP4725 12-bit Digital-to-Analog Converter Demo
- Videos
- Arduino with ADS1115 4-Channel 16-bit Analog-to-Digital Converter
- Arduino with MCP4725 12-Bit DAC
- Testing the Keyes IR Sensor Module with Arduino
- Arduino with Serially Interfaced MAX7219 Operates 8X8 LED Matrix
- Arduino RTC Clock with MAX7219 8-Digit LED Display
- BCD Conversion with Arduino Displayed on MAX7219
- Connecting the ATMEGA168/Arduino to MCP23016 and LCD Display
- Display Time-Date with Arduino, LCD Display, and DS1307 RTC
- Controlling Low-Voltage Driveway Lights with the Arduino
- Hatching Chicken Eggs with ATMEGA168/Arduino
- TSL230R Light to Frequency Converter and Arduino
- Interfacing Arduino to the MCP23016 I/O Expander
- Arduino with a DS1307 Real Time Clock
- Using a Unipolar Stepper Motor with a Arduino
- Arduino with the TA8050 Motor Controller
- Arduino with a 24LC08 Serial EEPROM
- Hardware Interrupts Demo and Tutorial for Arduino
- Micro-controller AC Power Control Using Interrupts