In this page, we will talk about the MCP23008 I/O Expander IC. This IC is used to expand the
available number of I/O pins. It also has a number of features that allow us to configure how
pins, when set to input, react and otherwise behave.
SCL: This pin is the clock line for I2C.
SDA: This pin is the data line for I2C.
A2, A1, A0: These pins are used to configure the last three bits of the
device's slave address. This allows 8 different MCP23008 ICs to exist on the same network
RESET: This pin resets the
MCP23008 by setting the registers back to their defaults.
INT: This pin is used to signal that the I/O expander has had an interrupt
triggered by one of its input pins.
VSS: This pin should be connected to ground.
GP 0-7: These pins are the general purpose I/O pins. They can be configured to
be input or output.
VDD: This pin should be connected to the voltage supply.
Most of these registers have a one-to-one mapping of bit position to GP pin. So unless
mentioned otherwise, assume that bit position 0 effects GP0, bit 1 effects GP1, and so on.
IODIR: This register sets the direction of the GP pins to either input or
IPOL: This register sets whether or not the logic state of the input pin is the
same or opposite of the logic of that pin. In other words, if the input pin is HIGH and
the corresponding bit in IPOL is 1, reading that pin will give you LOW, since it is the
opposite of HIGH.
GPINTEN: This register enables or disables interrupt-on-change events for the
pins. So if a pin is set as input and its logic level changes from LOW to HIGH or from
HIGH to LOW, an interrupt is triggered (what happens when an interrupt is triggered and
how to clear it is covered below).
DEFVAL: This register, when GPINTEN and INTCON are configured correctly, will
trigger an interrupt whenever the input pin is the opposite value of DEFVAL. That is, if
DEFVAL is 0 for pin 0, and pin 0 becomes HIGH, then interrupts will be triggered until pin
0 goes back LOW.
INTCON: This register sets whether a pin generates an interrupt based on DEFVAL
or its previous value (interrupt on change). The pin uses its previous value if the
corresponding bit position is set to 0, and it uses DEFVAL if the corresponding bit
position is set to 1.
IOCON: This register is different from the others since it configures the
overall operation rather than just how a pin acts. Bit 5 enables or disables sequential
operation (SEQOP), which increments the address pointer (points at one of the
registers) every time a byte is transferred (either read or write). Bit 4 enables or
disables the slew rate (DISSLW). If enabled, the SDA slew rate is controlled when driving
from a HIGH to a LOW. Bit 3 is not used on the MCP23008, but is used on the MCP23S08,
which is the SPI version of this chip. Bit 2 is the open-drain control bit (ODR) which
enables or disables the INT pin for open-drain configuration. Bit 1 is the interrupt
polarity control bit (INTPOL), which sets the polarity of the INT pin. If ODR is set, then
this bit is not functional.
GPPU: This register is used to enable or disable internal pull-up resisters
(100 kΩ). This only affects the pin if it is configured as an input.
INTF: This register tracks which pins have triggered an interrupt. A set bit
indicates that an interrupt was triggered by the corresponding pin. This register is also
read-only, so any attempted writes to it will be ignored.
INTCAP: This register captures the GPIO value of the pin that triggers an
interrupt. This register is also read-only. Interrupts are cleared by reading this
register or the GPIO register.
GPIO: This register is the general interface to the IO pins. Reading this
register gives the current state of the GP pins. Writing to this port modifies the OLAT
register which sets the GP pin set to OUTPUT.
OLAT: This register provides access to the output latches. Any writes to this
register will modify any pins configured as OUTPUTs. A read from this register will only
reflect OLAT and not the port itself.
When the I/O expander is powered on for the first time or is reset, the registers are set to
their default values. For all but one register, the default value is 0 for all the bits. The
one register that is different is the IODIR register, which is defaulted to all bits set to 1.
This means that all the pins are set to inputs on reset.
Interrupts are configured by 4 registers:
GPINTEN: Enables interrupts on individual pins.
DEFVAL: Holds a value to compare against the associated input values.
INTCON: Controls whether an interrupt is triggered by using DEFVAL or by the
previous value on the port.
IOCON: Configures the INT output pin as push-pull, open-drain, or active-level.
Of course, only pins set as inputs and enabled by GPINTEN will cause interrupts. When an
interrupt is triggered, the value of the port is stored in INTCAP. The first interrupt copies
the value of the port register to INTCAP, until the original interrupt is cleared (by reading
INTCAP or GPIO), any other interrupt events are effectively ignored.
An interrupt is triggered one of two ways depending on how the I/O expander is configured.
Using default configuration values, an interrupt is triggered when a pin changes logic state.
So in the case of a button press, two interrupts will be triggered; one from when the button
is pressed and the second from when the button is released. When configured to use DEFVAL, an
interrupt is triggered whenever the input pin is opposite of the DEFVAL value for that pin.
Even when the interrupt is cleared, if the input pin is still opposite of DEFVAL, another
interrupt will be triggered.