mcp23016 pin connections

Interfacing the Arduino to the MCP23016 IO Expander

by Lewis Loflin

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:

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

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

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.

  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.beginTransmission(0x20);  // setup out direction registers
  Wire.send(0x06);  // pointer
  Wire.send(0x00);  // DDR Port0 all output
  Wire.send(0xFF);  // DDR Port1 all input

void loop() {
    Wire.beginTransmission(0x20);  // set mcp23016 for all output
    Wire.send(0x00); // begin here
    if (i > 255) i = 0;
    delay(1000); // }// end loop

The address for the MCP23016 is 0x20.
This routine reads 8 port1 switches and transfers the value to port0  

#include <Wire.h> // specify use of Wire.h library.

int i; 

void setup()   {

  Wire.beginTransmission(0x20);  // set mcp23016 output
  Wire.send(0x00);  // DDR Port0 all output
  Wire.send(0xFF);  // DDR Port1 all input
  // invert input polarity on port 1
  Wire.beginTransmission(0x20);  // set mcp23016 output
  Wire.send(0xFF);  // DDR Port0 all output
  }  // end setup

void loop() {
  // read port 1
    Wire.send(0x01); // must act as a position pointer?
    Wire.requestFrom(0x20, 1);    // request 1 byte
    i = Wire.receive(); // receive a byte

   // send value to port 0
    Wire.send(0x00); // begin here
    // only 2 switches connected
    Wire.send(i); // send to port 0
    // Note: noise problem on floating pins. 
// Tie to GND through a 2.2k resistor


} // end loop

Added June 7, 2013:

You Tube Arduino Microcontroller Video Series March 2012: