Using i2c MCP23008 to control shift registers

Hi,

I am currently on a I2C mission and after my success with creating the I2CLcd module from the playground I was encouraged to use a MCP23008 to try and control a shift register (74hc595) to extend outputs.

The shift register has three pins that control it namely latch, clock and data. Latch goes low to start transmission, data pin is set accordingly and clock is taken high and then low to 'shift'. Transmission complete is signalled by latch pin going high. These pins are normally controlled by the Arduino.

The circuit I built works with three of the MCP23008 output pins connected to the three control pins on the 74hc595 but I am not having any joy.

The shift register part of the circuit works when I connect the three control pins to the arduino and run the Shifout sample and the MCP23008 does flash LEDs on the corresponding data pins when tested so I believe the circuit is good.
I have triple verified that the output pins from the MCP23008 correspond with the correct control pins on the 74hc595.

My guess is that the MPC23008 output pins 'float' every time it's register is set which confuses the 74hc595 since the latch pin must stay low for the duration of the transmission. :-?

Anyone got any ideas on this?

Here is the code:

#include <Wire.h>


int ADDR = 0xA6;
int ledPin = 3;

/*
 I2C Implementation of Shifout uses MCP23008 and 74HC595
 Last Binary Digit Controls Latch on 74hc595
 Second Last Binary Digit Controls Clock on 74hc595
 Third Last Binary Digit Controls Data on 74hc595
 --
 Use first Bindary Digit to Flash a MCP23008 connected LED to indicate transmission - Debug only
 --
 Wiring of 74hc595 as per Shiftout example 
 Wiring MCP23008 as per 12cLCD example 
*/


void setup()
{
  Serial.begin(9600);
  Wire.begin(); // join i2c bus (address optional for master)
  setMCPReg(ADDR,0x05,0x0C); // set CONFREG (0x05) to 0 initialise MCP
  delay(5);
  setMCPReg(ADDR,0x00,0x00); // set IOREG (0x00) to 0 initialise MCP
  delay(5);
  setMCPReg(ADDR,0x0A,0x00); // set all output pins to low
  delay(5);

  setMCPReg(ADDR,0x0A,B00000001); // set latch high 
  delay(500); 

  // Test starts here, should light up 3 leds on data pins 1,2,3 on 74hc595
  shiftOutI2C(B00001000);
  delay(800);
  shiftOutI2C(B00001010);
  delay(800);
  shiftOutI2C(B00001110);
// debug - set all high
  delay(800);
  shiftOutI2C(B11111111);
// debug - set all low
  delay(800);
  shiftOutI2C(B00000000);
     
  
}



void loop()
{
  
  
}

void setMCPReg( byte deviceAddr, byte reg, byte val ) {
  Wire.beginTransmission(deviceAddr);
  Wire.send(reg);
  Wire.send(val);
  Wire.endTransmission();
}

void shiftOutI2C(byte myDataOut) {
  // Adaptation of example Shiftout from Arduino Wiki
  //internal function setup
    int i=0;
    byte pinState;
    byte pinState2;

//  digitalWrite(myDataPin, 0);
//  digitalWrite(myClockPin, 0);
    setMCPReg(ADDR,0x0A,B10000000); // set latch low , clock and data low  ** start of transmission
    delay(5);
  
  //for each bit in the byte myDataOut?
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights. 
  for (i=7; i>=0; i--)  {
   // digitalWrite(myClockPin, 0);
      setMCPReg(ADDR,0x0A,B10000000);//  latch stays low,  clock and data low 
      delay(5);

    if ( myDataOut & (1<<i) ) {
      pinState= B10000100; //  latch stays low - clock pin low - data pin high
      pinState= B10000110; //  latch stays low - clock pin high to 'shift' register - data pin high
    }
    else {      
      pinState= B10000000;  //  latch stays low - clock pin low - data pin low
      pinState2= B10000010; //  latch stays low - clock pin high to 'shift' register - data pin low
    }

    //Sets the pin to HIGH or LOW depending on pinState
    //digitalWrite(myDataPin, pinState);
    setMCPReg(ADDR,0x0A,pinState);
    delay(5);
    //register shifts bits on upstroke of clock pin  
    //  digitalWrite(myClockPin, 1);
    setMCPReg(ADDR,0x0A,pinState2);  // clock pin goes high, with whatever was on datapin - latch still low ** register shifts
    delay(5);
  
   //zero the data pin after shift to prevent bleed through
  //  digitalWrite(myDataPin, 0); 
    setMCPReg(ADDR,0x0A,B10000010); // latch stays low - clock stays high - data pin low
    delay(5);
  }

  //stop shifting
  //digitalWrite(myClockPin, 0);
  setMCPReg(ADDR,0x0A,B10000000); //  latch stays low - clock moves to low - data pin stays low
  delay(5);

  setMCPReg(ADDR,0x0A,B00000001); // latch changes to high - clock stays low - data stays low ** end of transmission - commit register
  delay(5);


  
}

Note I have left as comments most of the original 'digitalwrite' statement from the Shiftout function as reference..
Any help is greatly appreciated. :slight_smile:

I haven't had time to look deeply into the code but:-

My guess is that the MPC23008 output pins 'float' every time it's register is set

Is not correct, the output pins are solid when updating them.

Thanks G_Mike, that removes a lot of doubt I had about the feasibility of this, and some other I2C circuits I had in mind.

In the meantime I'll double check specsheets to verify that the current requirements of the control pins on the shift register match the output of the mpc23008 but I doubt very much that could be it .. perhaps there is a specific timing requirement also by the shift register?

No it's not the timing.
Have you got the device address correct? You code shows this as 0xA6 I am not sure how you get this. The data sheet shows the address as being 0 1 0 0 A2 A1 A0 r/w
so I can't see this being 0xA6 this is to my mind 0x20 if all the external inputs are set to zero. Have you tried this device just by it's self lighting some LEDs?

Yip I changed the address to 0xA6 because my LCD already runs on a mcp23008 on address 0xA7. The lcd mp23008 combo is not connected while testing this though ..

Indeed I did test with LEDs and they flicker away happily (if I add some delays to make it noticeable..) in the correct sequence e.g. latch off, data off, clock off, data on , clock on , data off , clock off etc and eventually latch on again :-/

Thanks for the help Mike. :slight_smile:

I would do the sequence:-

  1. latch low, clock low

  2. Data set & clock low

  3. clock high

  4. Repeat 2 and 3 till you have done it 8 times.

  5. Latch high

Also make sure Pin 10 is high, and pin 13 is low. Maybe these should be wired in this state.

Thank you very much, this module shall henceforth be known as 'the GMike'!

I did it 'manually' step by step as you suggested and it worked. Here is the updated function which now works 100% :slight_smile:
There were some extra pulses in the original which must have bugged it out.

void shiftOutI2C(byte myDataOut) {
  // Adaptation of example Shiftout from Arduino Wiki, using i2c via MCP23008 GPO,GP1,GP2
    int i=0;
    byte pinState;
    byte pinState2;
    setMCPReg(ADDR,0x0A,B10000000); // set latch low , clock and data low  ** start of transmission
    delay(5);
  
  //for each bit in the byte myDataOut
  //NOTICE THAT WE ARE COUNTING DOWN in our for loop
  //This means that %00000001 or "1" will go through such
  //that it will be pin Q0 that lights. 
  for (i=7; i>=0; i--)  {
    if ( myDataOut & (1<<i) ) {
      pinState= B10000100; //  latch stays low - clock pin low - data pin high
      pinState2 = B10000110; //  latch stays low - clock pin high to 'shift' register - data pin high
    }
    else {      
      pinState= B10000000;  //  latch stays low - clock pin low - data pin low
      pinState2= B10000010; //  latch stays low - clock pin high to 'shift' register - data pin low
    }
    //Sets the data pin to HIGH or LOW depending on pinState
     setMCPReg(ADDR,0x0A,pinState); // clock pin goes low, data pin gets set - latch still low 
    //register shifts bits on upstroke of clock pin, note data pin retains its value  
     setMCPReg(ADDR,0x0A,pinState2);  // clock pin goes high, with whatever was on datapin - latch still low ** register shifts
  
  }

  //stop shifting
   setMCPReg(ADDR,0x0A,B00000001); // latch changes to high - clock goes low - data goes low ** end of transmission - commit register
  
}

Now to hang another few 74hc595 onto it :smiley: