mcp23016 pin connections


Interfacing the Arduino to the MCP23016 IO Expander

by Lewis Loflin


The MCP23016 is available in a DIP package from www.mouser.com.

Mfr. Part # MCP23016-I/SP, Mouser Part # 579-MCP23016-I/SP, Cost: Qty. 1: $2.37 as of March 2023.

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:

mcp23016 clock pin connections.


Also MCP23016 I2C I/O Expander spec sheet.

Download the schematic here.

Download the Arduino code arduino_mcp23016.txt.

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.

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 connection
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.

Download the schematic here.

Download the Arduino code arduino_mcp23016.txt.

Stepper Motors

Serial LCD Display and assorted Sensors

Web site Copyright Lewis Loflin, All rights reserved.
If using this material on another site, please provide a link back to my site.