IO Expander, MCP23016
Datasheet MCP23016
Other stuff
Purchase from Digikey, $1.90
First of all don't buy this chip. Buy this one instead:
Datasheet, MCP23018
Other stuff
Purchase from Digikey, $1.81
It's faster, cheaper, and does not need the external RC circuit to determine the internal clock speed, saving a resistor, capacitor, and board space. There is also a SPI version known as the MCP23S18
• 16-bit remote bidirectional I/O port
• Fast I2C™ bus clock frequency (0 - 400 kbits/s)
• Three hardware address pins allow use of up to eight devices
• Open-drain interrupt output on input change
• Interrupt port capture register
• Input port polarity inversion
• High-current drive capability per I/O: ±25 mA
• Operating Supply Voltage: 2.0V to 5.5V
• 28-pin PDIP, 300 mil
The most unexpected aspect of this project was the relative slowness of the chip. These days it is so common to talk about Gigahertz and Megahertz, that we forget how freaking fast some of this stuff really is. But this chip surprised me by how slow it is. With the default I2c bus, I was only able to toggle all the pins (On, then Off) at a rate of 595Hz.
This loop below operates at about 595Hz (1.68ms).
void loop2(void) { write_io(GP0,255); // all on port 0 write_io(GP1,255); // all on port 1 write_io(GP0,0); // all off port 0 write_io(GP1,0); // all off port 1 }
If I add trick code below, to the setup subroutine, to change the I2c rate, I can get up to about 980Hz (1.02ms).
It changes the I2c bus from the default rate of 100K to 275K. If I go any more, I start to get failures within a few seconds. I think the 275K is even a little too edgy, and unreliable for anything but speed tests.
#define CPU_FREQ 16000000L #define TWI_FREQ 275000L TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2;
I also tried to inline the function call. It was slightly faster but not significant, and certainly not worth the wordy code. For some reason I thought that the compiler would do that sort of thing by it's self. It's probably something they only do for PC that have crap load of memory to spare.
The loop below achieved a rate of 128kHz, using a standard 16MHz ATMEGA168 processor. That is like 128 times faster.
void loop(void) { digitalWrite(13, HIGH); digitalWrite(13, LOW); }
The datasheet describes the external RC circuit used to set the internal clock speed (about 1MHz). They specify a 3.9K resistor and a 33pf cap. The closest I had was a 36pf cap. I works for everything I played with but maybe the correct cap would speed things up a bit.
My test code:
// MCP23016 I/O Expander
// Can toggle both ports (16 pins) at rates up to 595Hz (1.68ms)
// Arduino analog input 5 - I2C SCL
// Arduino analog input 4 - I2C SDA
#include
//Address of MCP23016 IO Expander, 8 addresses available
#define io_address B00100000
// MCP23016 command byte to register relationship
#define GP0 0x00 // Data Port, Current status of pins
#define GP1 0x01 // Data Port, Current status of pins
#define OLAT0 0x02 // Output Latch, Current status of latched pins
#define OLAT1 0x03 // Output Latch, Current status of latched pins
#define IPOL0 0x04 // Input polarity, 0-normal, 1-inverted
#define IPOL1 0x05 // Input polarity, 0-normal, 1-inverted
#define IODIR0 0x06 // IO Direction, 0-output, 1-input
#define IODIR1 0x07 // IO Direction, 0-output, 1-input
#define INTCAP0 0x08 // Interrupt capture, Read-Only, value of port that generated the interrupt
#define INTCAP1 0x09 // Interrupt capture, Read-Only, value of port that generated the interrupt
#define IOCON0 0x0A // IO Expander Control, Sampling frequency ofGP pins, 0-normal, 1-fast
#define IOCON1 0x0B // IO Expander Control, Sampling frequency ofGP pins, 0-normal, 1-fast
//IO Bank 0
#define IO_gp00 B00000001
#define IO_gp01 B00000010
#define IO_gp02 B00000100
#define IO_gp03 B00001000
#define IO_gp04 B00010000
#define IO_pg05 B00100000
#define IO_gp06 B01000000
#define IO_gp07 B10000000
//IO Bank 1
#define IO_gp10 B00000001
#define IO_gp11 B00000010
#define IO_gp12 B00000100
#define IO_gp13 B00001000
#define IO_gp14 B00010000
#define IO_pg15 B00100000
#define IO_gp16 B01000000
#define IO_gp17 B10000000
///////////////////////////////////////////////////////////////////////////////////////////////////////
void setup(void) {
//Serial.begin(115200); // start serial for output
Wire.begin(); // join i2c bus (address optional for master)
// Special speedup of I2c bus from 100K to 275K
// Going from about 595Hz (1.68ms) to 980Hz (1.02ms)
#define CPU_FREQ 16000000L
#define TWI_FREQ 200000L
TWBR = ((CPU_FREQ / TWI_FREQ) - 16) / 2;
write_io(IODIR0,0);
write_io(IODIR1,0);
write_io(GP0,0);
write_io(GP1,0);
pinMode(13, OUTPUT);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void loop(void) {
write_io(GP0,0); // all off
write_io(GP1,0); // all off
write_io(GP0,255); // all on
write_io(GP1,255); // all on except the buzzer
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
int read_io(int cmd_reg) {
Wire.beginTransmission(io_address);
Wire.send(cmd_reg);
Wire.endTransmission();
Wire.requestFrom(io_address, 1);
return Wire.receive();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
void write_io(int cmd_reg, int push_this) {
Wire.beginTransmission(io_address);
Wire.send(cmd_reg);
Wire.send(push_this);
Wire.endTransmission();
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
created: Dec. 1, 2013, 1:01 a.m.
modified: April 14, 2019, 12:50 a.m.