Cornell University ECE4760
mcp23017 i2c Port Expander

Pi Pico rp2040/2350

The mcp23017 i2c description and wiring
The Microchip mcp23017 port expander adds 16 lines of dgital i/o, driven by an i2c connection to the Pico. The 16 lines are grouped into two 8-bit ports.  Each i/o line may be set to input or output. Each i/o line may turn on an internal pullup resistor (there are no pulldowns), if it is an input. The grouping of lines into two 8-bit ports means that one i2c transaction can affect the eight i/o lines of PortA or of PortB. For example, you can write a uint8_t number to a port with one i2c transaction. You can also set the data direction of each of the eight bits of a port by writing an 8-bit value with desired direction encoded as high for output and low for input. For example writing 0xf0 to the data direction register of PortA sets bits 0 to 3 as input and bits 4 to 7 as output. Similarly, writing eight bits to the pullup enable register turns on pullups on lines with a high bit. For example writing 0x03 to the pullup enable register of PortB turns on pullups on bits zero and one of PortB.

The mcp23017 we use is in 28-pin PDIP package as shown below.
For the code below the INTA and INTB pins are not connected, and the RESET pin is wired high.

The wiring for the PortA to PortB loop-back test. The convienient wiring of the port pins directly
across the package from PortA to PortB results in bit reversal when reading the loop-back results in the code.
The wiring details as a text table.
The image of he circuit is basically useless for wiring, but
does show the nice LEDs loaded with the pattern 0xa5.


The Port Expander command interface.
Adding
#include "mcp23017_rp2040.h"
to your program adds the port expander code.
In the following functions:

Functions:

The loop-back test code.
This program merely exercises the inteface routines to the port expander to that you can test connections and see the syntax.
There are two threads running on one core. The usual blinky thread, of course, blinks. The serial thread:

  1. Sets up the port expander
    PEinit() ;
    makes all 8 port A pins outputs by writing 0xff to PortA direction register
    PEgpioSetDir(PORTA, 0xff) ;
    makes port B all inputs by writing zeros to PortB direction register
    PEgpioSetDir(PORTB, 0x00) ;

  2. prompts for a 2-digit hexadecimal number and sends the number to output of PortA.
    sscanf(pt_serial_in_buffer,"%2x", &port_A_out) ;
    PEwritePort(PORTA, port_A_out) ;

    then reads PortB, reverses the bits and prints
    port_B_input = PEreadPort(PORTB) ;
    port_B_input_rev = __rev((uint32_t) port_B_input) >> 24; // shift 24 because __rev is 32 bit
    printf ("A_out=%02x B_in=%02x B_in_rev=%02x\n\r", PEreadPort(PORTA), port_B_input, port_B_input_rev)
    ;

  3. prompts for a bit number (0 to 7) and bit value (1/0) inserts the bit into PortA output
    sscanf(pt_serial_in_buffer,"%d %d", &bit_position, &bit_value) ;
    PEwriteBit(PORTA, bit_position, bit_value) ;

    then reads PortB as above.

  4. prompts for a bit position (0 to 7) then reads that bit from PortA.
    sscanf(pt_serial_in_buffer,"%d", &bit_position) ;
    printf ("A bit=%1d\n\r\n\r",PEreadBit(PORTA, bit_position));


  5. repeats from 2 above

Test Codemcp23017_rp2040.h,   Project ZIP


Copyright Cornell University August 7, 2025