Here are some routines I have used successfully:
#include <Wire.h>
//redefine highByte and lowByte to return byte, not int
#define loByte (byte)lowByte
#define hiByte (byte)highByte
#define opcode B0100000 // address = 000; 7 bits!
#define ioconbyte B00100000 // disable sequential operation mode;
#define iocon 0x0A
#define iodira 0x00
#define gpioa 0x12
#define gppua 0x0C //pullups
#define gpintena 0x04
#define defvala 0x06
#define intcona 0x08
#define intfa 0x0E
#define intcapa 0x10
#define ipola 0x02
void MCPstore (byte i2caddress, byte i2cregister, word i2cdata) // Store 16 bits in specified register of MCP23017
{
byte controlbyte = B0100000 | i2caddress; // note that bit 0 not used b/c 7-bit opcode for Wire library
Wire.beginTransmission(controlbyte);
Wire.send(i2cregister);
Wire.send(loByte(i2cdata));
Wire.send(hiByte(i2cdata));
Wire.endTransmission();
}
word MCPread (byte i2caddress, byte i2cregister) // Read 16 bits from MCP23017
{
byte controlbyte = B0100000 | i2caddress; // note that bit 0 not used b/c 7-bit opcode for Wire library
word tempdata;
Wire.beginTransmission(controlbyte);
Wire.send(i2cregister);
Wire.endTransmission();
Wire.requestFrom(controlbyte,(byte)2); // otherwise function gets one byte and one int & chokes
while (Wire.available() < 2) { }
tempdata = Wire.receive(); // get lower 8 bytes
tempdata |= (Wire.receive() << 8); // get upper 8 bytes
return tempdata;
}
void setup()
{
Serial.begin(9600);
Wire.begin();
Wire.beginTransmission(opcode);
Wire.send(iocon);
Wire.send(ioconbyte);
Wire.endTransmission();
MCPstore (0,iodira,0xffff); // all input
MCPstore (0,gppua,0xffff); // all pullups on
}
void loop()
{
word mcpdata = MCPread(0,gpioa);
byte firstbyte = hiByte(mcpdata);
byte secondbyte = loByte(mcpdata);
Serial.println(firstbyte,HEX);
Serial.println(secondbyte,HEX);
}
The program just displays the input state of GPIOA & GPIOB, but the 16-bit read and store routines should be portable. I have used them to set outputs as well.
Note that with the sequential address mode disabled (or maybe even with it enabled) if you write 8 bits to an A register, the address pointer automatically increments to the B register. So if you set the address pointer to the A register, you can read or write to the B register without a restart just by requesting or sending 16 bits.
Unfortunately the Wire routines do not report whether there was an I2C acknowledge; this would be very helpful in troubleshooting.