Interfacing MCP23018 io expander via Arduino.

hi all,

I'm having some problems here with my code.

I'm trying to get my 16-bit io expander with open-drain output works on my Arduino board.

i was trying to light up 2 LED which is connected between two resistors to 5v and two output pins separately.

I'm a beginner to Arduino wire.library.
this is the data sheet of MCP23018. ww1.microchip.com/downloads/en/DeviceDoc/22103a.pdf
below is the code i wrote.

#include <Wire.h>

#define I2Cadd (0x20) //address of slave device
#define IOCON (0x05) //IO expander configuration register address
#define IODIRA (0x00) //this is address of IO direction register
#define GPIOA (0x09) //general purpose io port register's address
#define OLATA (0x0a) //output latch register's address
#define GPPUA (0x06) //pull-up resistor register's address

void setup()
{
Wire.begin();
Wire.beginTransmission(I2Cadd);
Wire.send(IOCON);
Wire.send(0xA4);
Wire.send(IODIRA);
Wire.send(0x00);
Wire.send(OLATA);
Wire.send(0xF0);
Wire.send(GPPUA);
Wire.send(0xFF);
Wire.endTransmission();
}

void loop()
{
Wire.beginTransmission(I2Cadd);
Wire.send(GPIOA);
Wire.send(0xF0); //trying to connect 2 led with diff pin to test.
Wire.endTransmission();
delay(2000);
}

please help, i've been working on this for 2 weeks.
thanks.
chen

Actually it looks like the problem might be pretty obvious. In I2C you can't just keep sending addresses and then data, the chip has no idea what you are trying to send it. The MCP23018 will auto increment addresses after each byte (unless the SEQOP register is disabled). That means you're writing register addresses into the MCP23018 registers instead of addressing the registers and putting your intended data in them. Either start with one address and then write a long stream of sequential registers, or break each register access into a separate I2C conversation.

You can also use the Centipede Library and example code as a start, it'll work with MCP23017 or MCP23018 chips, not just the Centipede Shield: centipede_shield [macetech documentation]

I think several other people have made libraries for these chips too. Search around the forums....

To simplify what macgr is saying, you need to call Wire.endTransmission after every pair of register/data writes, and then Wire.beginTransmission again. Unless you are writing data in order to sequential registers.

An MCP library:
MCP23017.h

// MCP23017 library

#ifndef MCP23017_h
#define MCP23017_h

#include "Wprogram.h"
#include "Wire.h"

#define loByte (byte)lowByte
#define hiByte (byte)highByte

#define IOCON 0x0A
#define IODIRA 0x00
#define GPIOA 0x12
#define GPPUA 0x0C      
#define GPINTENA 0x04
#define DEFVALA 0x06
#define INCTCONA 0x08
#define INTFA 0x0E
#define INTCAP 0x10
#define IPOLA 0x02

class MCP23017 {
     
  public:

    byte i2cAddress;
  
    byte Read8 (byte mcpregister);
    word Read16 (byte mcpregister);
    
    void Store8 (byte mcpregister, byte mcpdata);
    void Store16 (byte mcpregister, word mcpdata);
    
};

#endif

MCP23017.cpp

#include "Wprogram.h"
#include "MCP23017.h"

byte MCP23017::Read8 (byte mcpregister) {  // read 8 bits from MCP register;
  byte controlbyte  = B0100000 | i2cAddress;  // note that bit 0 not used b/c 7-bit opcode for Wire library
  byte tempdata;
  
  Wire.beginTransmission(controlbyte);
  Wire.send(mcpregister);
  Wire.endTransmission();
  
  Wire.requestFrom(controlbyte,(byte)1);  // otherwise function gets one byte and one int & chokes
  while (Wire.available() < 1) { }
  tempdata = Wire.receive();      // get lower 8 bytes
  return tempdata;
}

word MCP23017::Read16 (byte mcpregister) {
  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(mcpregister);
  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 MCP23017::Store8 (byte mcpregister, byte mcpdata)    // Store 8 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(mcpregister);
  Wire.send(mcpdata);
  Wire.endTransmission();
}

void MCP23017::Store16 (byte mcpregister, word mcpdata)    // 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(mcpregister);
  Wire.send(loByte(mcpdata));
  Wire.send(hiByte(mcpdata));
  Wire.endTransmission();
}

Declare an instance of MCP23017 and set its i2cadress to use. I.e.:

MCP23017 expander;

expander.i2caddress = 0x20;
expander.Store8(IOCON,0xA4);
expander.Store8(IODIRA,0x00);
etc.

Of course call Wire.begin() first.

Yes, that's exactly what I meant. :wink: