MCP23017 Causing Arduino to become unresponsive

Hey everyone,

I have some strange behaviour that I cannot figure out.

I've wired up an MCP23017 multiplexer to read inputs. I've created test code to read the inputs from General Purpose pins A and B. The code works flawelessly for all inputs.

/*
 Example 41.2 - Microchip MCP23017 with Arduino
 http://tronixstuff.wordpress.com/tutorials > chapter 41
 John Boxall | CC by-sa-nc
 */

// MCP23017 pins 15~17 to GND, I2C bus address is 0x20
// Clock goes to pin A5 //yellow->blue
// Data goes to pin A4 // green ->green
// Buttons have Red to 5V, and Black to general purpose pins
//     From each pin, have a 10k pull-down resistor to ground 
// Have a 4.7k pull-up resistor from clock and data pins to 5V

#include "Wire.h"
byte inputs=0;
byte P1=0;
byte P2=0;
int delayTime=150; //150 seems most responsive while still debouncing

void setup()
{
  Serial.begin(9600);
  Wire.begin(); // wake up I2C bus
}

void loop()
{
  
  Wire.beginTransmission(0x20); //Open comms with multiplexer
  Wire.write(0x13); // set MCP23017 memory pointer to GPIOB address
  Wire.endTransmission(); // Close comms with multiplexer
  
   // // info from pins 1-8
  Wire.requestFrom(0x20, 1); // request one byte of data from MCP20317
  
  // Parse the bytes out into something meaningful:
  P1 = Wire.read(); //First recieved byte stored here

  if (P1>0) // if a button was pressed
  {
    Serial.println(P1, BIN); // display the contents of the GPIOB register in binary
    delay(delayTime); // for debounce
  }
  

  Wire.beginTransmission(0x20); //Open comms with multiplexer
  Wire.write(0x12); // set MCP23017 memory pointer to GPIOA address
  Wire.endTransmission(); // Close comms with multiplexer
  
   // // info from pins 21-28
  Wire.requestFrom(0x20, 1); // request one byte of data from MCP20317
  
  // Parse the bytes out into something meaningful:
  P2 = Wire.read(); //First recieved byte stored here

  if (P2>0) // if a button was pressed
  {
    Serial.println(P2, BIN); // display the contents of the GPIOB register in binary
    delay(delayTime); // for debounce
  }

}

I've created a 16x16 matrix display consisting of 4 8x8 matrices, where I'm communicating via SPI, and again, I've created test code to light these up and see that they work.

// Define what pins to use

// NOTE: Can use ANY type of pin for outputs.
//  Did a test and tried using PWM pins and 
//  non-PWM pins for each connection and worked
//  for all cases
//Pin connected to ST_CP of 74HC595
int latchPin = 2; 
//Pin connected to SH_CP of 74HC595
int clockPin = 3; 
////Pin connected to DS of 74HC595
int dataPin1 = 4; 


//Initialize a byte array
byte matrix[4][8] = {{0,0,0,0,0,0,0,0}, //Diagonal line
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0} };

//holder for information you're going to pass to shifting function
int byteScan = 0; 
int byte1 = 0; 
int byte2 = 0; 
int byte3 = 0; 
int byte4 = 0; 

void setup() {
// put your setup code here, to run once:
  //set pins to output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);

// Initializer Serial Communication Speed for Reading Analog Inputs
Serial.begin(9600);
}



void loop() {
  // put your main code here, to run repeatedly: 
int i=0;
   
for (i=7; i>=0; i--)  {

  digitalWrite(latchPin, 0);

  delay(00); //Delay to get nice Display

  matrix[0][i] = pow(2,i-1+stepVar)+1; //Diagonal Line
  matrix[1][i] = pow(2,i-1+stepVar)+1; //Diagonal Line
  matrix[2][i] = pow(2,i-1+stepVar)+1; //Diagonal Line
  matrix[3][i] = pow(2,i-1+stepVar)+1; //Diagonal Line

  byteScan = 1<<i; //UDN, ROW SCANNER
  byte1 =matrix[0][i];
  byte2 = matrix[1][i];
  byte3 = matrix[2][i];
  byte4 = matrix[3][i];


  // Print Values to Serial Monitor for testing
  //Serial.println(byte1);
  //Serial.println(byteScan);

  // Send the Bytes to the shift registers
  shiftOut(dataPin1, clockPin, byte4); 
  shiftOut(dataPin1, clockPin, byteScan); 
  //
  shiftOut(dataPin1, clockPin, byte3); 
  shiftOut(dataPin1, clockPin, byteScan); 
  //
  shiftOut(dataPin1, clockPin, byte2); 
  shiftOut(dataPin1, clockPin, byteScan); 
  //
  shiftOut(dataPin1, clockPin, byte1); 
  shiftOut(dataPin1, clockPin, byteScan); 
  
  
  //return the latch pin to high to drop the bits from the register
  //  memory and into place to make the lasers do their thing
  digitalWrite(latchPin, 1);
 }
}
}


// Drop a byte into the shift register, one bit at a time
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  // This shifts 8 bits out MSB first, 
  //on the rising edge of the clock,
  //clock idles low

  //internal function setup
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

  //clear everything out just in case to
  //prepare shift register for bit shifting
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  //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--)  {
    // for i=7:-1:0
    digitalWrite(myClockPin, 0);
    // Me: I think the clock works like a click-counter, 
    // where each time you increment up by 1 you need to click.  So
    // it's zero here tells it hey, stay here, don't shift, and setting
    // it to 1 below acts to click the counter and shift over 1.

    //Them: if the value passed to myDataOut and a bitmask result 
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000 
    // and proceeds to set pinState to 1. (Delete this Later 
    // if my explanation below is right).
    //
    //Me: If the i'th Bit of the input Byte is 1, then tell the
    // shift register to be 1.  But if the i'th Bit of the input
    // Byte is 0, then tell the shift register to be 0.
    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {	
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(myDataPin, pinState);
    //register shifts bits on upstroke of clock pin  
    digitalWrite(myClockPin, 1);
    //zero the data pin after shift to prevent bleed through
    digitalWrite(myDataPin, 0);
  }

  //stop shifting
  digitalWrite(myClockPin, 0);
}

When I try to run code that combines both of these, I have some very very strange behavior that initially made me think that there was somehow a short somewhere in the circuit, but after testing multiple times with a multimeter, I can't find any instances of continuity in places where there shouldn't be.

Final Code:

 See comment Below, too many characters for a single post

I've set up the code so that:
in1 = GPA(); //Read inputs from General purpose register A
in1 = 1; //Overwrite the input from the register
P1 = in1; //Not exactly, but in the code above I check in1 on every i loop, and then use the values in P1 on the 16th iteration to debounce
Serial.println(P1) ;

I've overwritten what's actually being read and assigned it to because I seem to be having issues with the arduino crashing. In both of the examples pieces of code, the Serial.println shows results in the serial monitor and loops through endlessly, but here, the serial monitor will print 1 or 2 values, and then it will cease showing anything at all. It's like the arduino has inexplicably crashed... or maybe it's failing to connect to the multiplexer and just doesn't know what to do?

It's also worth mentioning that when I comment out the code to checkGPA and checkGPB, and just have the code below, it loops through endlessly:
P1 = in1; //Not exactly, but in the code above I check in1 on every i loop, and then use the values in P1 on the 16th iteration to debounce
Serial.println(P1) ;

I also thought it could be hardware related, but after checking and rechecking all of the connections, there does not appear to be any shorts and everything is wired properly (as far as I can tell, and as far as I can assume since the test code works).

The ONLY thing I can think of that may be a problem, but can't find any straight answers for anywhere, is that the RESET pin on the multiplexer should maybe be 'pulled high' with a resistor to Vdd, and not connected directly to Vdd? Any ideas? Thanks!

Finalized Code that combines elements of the test code (as mentioned in initial Post above):
(apparently its too long, So I'll have to split it into the main void loop in this comment, and the other functions in the next)

#include <Wire.h>

// Define what pins to use

//Pin connected to ST_CP of 74HC595
int latchPin = 2; //Yellow
//Pin connected to SH_CP of 74HC595
int clockPin = 3; //White 
//Pin connected to DS of 74HC595
int dataPin1 = 4; //Green

// Pins for Player Selection
int interupt1 = 9;
int interupt2 = 10;
//Indicate the number of players
int checkPlayer1 = 0; //Check if there is a change in player state
int checkPlayer2 = 0; //Check if there is a change in player state
int numPlayers = 0;

//holder for information you're going to pass to shifting function
int byteScan = 0; //Scanning Byte.  No Additionl calculations needed
int byte1 = 0; 
int byte2 = 0; 
int byte3 = 0; 
int byte4 = 0; 

// Counter for checking the multiplexer. the multiplexer needs to 'debounce' so we don't 
// want to check it on every loop. 
int buttonCounter = 0;
int buttonCountCheck=16; //Set the default button counter value

// Initialize inputs from multiplexer
int P1=0;
int P2=0;

//The display state of how the matrices are related, range of 0-3
int displayState = 0; 

// 1) Initialize a 2D byte array:
//bytes are Y, bits are X
byte matrix[5][8] = {{0, 0, 0, 0, 0, 0, 0, 0}, //Temporary matrix
{0, 0, 0, 0, 0, 0, 0, 0},  //Matrix1
{0, 0, 0, 0, 0, 0, 0, 0},  //Matrix2
{0, 0, 0, 0, 0, 0, 0, 0},  //Matrix3
{0, 0, 0, 0, 0, 0, 0, 0}};  //Matrix4
//
int x = 3; //The initial x-cursor location
int y = 3; //The initial y-cursor location

void setup() {
  // put your setup code here, to run once:
  //set pins to input or output because they are addressed in the main loop
  pinMode(latchPin, OUTPUT);
  
  pinMode(interupt1,INPUT);
  pinMode(interupt2,INPUT);
  digitalWrite(interupt1,LOW);
  digitalWrite(interupt2,LOW);
  // Initializer Serial Communication Speed for Reading Analog Inputs
  Serial.begin(9600);
}


void loop() {
  // put your main code here, to run repeatedly: 
  
  //Shift out to matrices
  int i=0;

  
  for (i=7; i>=0; i--)  {
    digitalWrite(latchPin, 0); // Drop the latch down so no data flows

    delay(10); //DELAY  NOTE: With delay(2), lasers are still bright and look solid
                 
    byteScan = 1<<i; //UDN, ROW SCANNER
    byte1 = matrix[1][i]; 
    byte2 = matrix[2][i]; 
    byte3 = matrix[3][i]; //This will need to be fliplr or flipud once all together
    byte4 = matrix[4][i]; //This will need to be fliplr or flipud once all together
    
    // Print Values to Serial Monitor for testing
    Serial.println(byte1);
    //Serial.println(byteScan);

    // Send the Bytes to the shift registers
    // Matrix 1
    shiftOut(dataPin1, clockPin, byte4);
    shiftOut(dataPin1, clockPin, byteScan); 
    // Matrix 2
    shiftOut(dataPin1, clockPin, byte3);
    shiftOut(dataPin1, clockPin, byteScan); 
    // Matrix 3
    shiftOut(dataPin1, clockPin, byte2);
    shiftOut(dataPin1, clockPin, byteScan); 
    // Matrix 4
    shiftOut(dataPin1, clockPin, byte1);
    shiftOut(dataPin1, clockPin, byteScan); 
    
    //return the latch pin to high to drop the bits from the register
    digitalWrite(latchPin, 1);
    
    //Check the player1 and player2 interupts on every refresher loop (outside of the buttonCounter loop)
    checkPlayer1 = digitalRead(interupt1);
    checkPlayer2 = digitalRead(interupt2);

          //TEST CODE
          checkPlayer1 = 0;
          checkPlayer2 = 0;
          numPlayers = 1;
    
    //Check interupts 1 and 2 and change the number of Players selected.
    //**NOTE: Might need to check for HIGH/LOW instead of 1/0
    if (numPlayers==0 & checkPlayer1==1){
      numPlayers=1;
    }
    else if (numPlayers==1 & checkPlayer1==1){
      numPlayers=0;
      clearMatrix(1);
      //clearMatrix(matrix1);
    }
    else if (numPlayers==1 & checkPlayer2==1){
      numPlayers=2;
      clearMatrix(2);
      //clearMatrix(matrix2);
    }
    else if (numPlayers==2 & checkPlayer2==1){
      numPlayers=1;
      //clearMatrix(matrix2);
    }
    

     buttonCounter++;
     if (buttonCounter>buttonCountCheck){
       buttonCounter=0;
     }
     
     // Check inputs from the multiplexer and save the results until the nth iteration
     // Note: After the nth iteration, the values of P1 and P2 are cleared
     int in1 = checkGPA(); 
     int in2 = checkGPB(); 
     //
     for (int jj=0; jj<=7; jj++){
       if (bitRead(in1,jj)==1){
         bitSet(P1, jj);
       }
       
       if (bitRead(in2,jj)==1){
         bitSet(P2, jj);
       }
       //Serial.println( bitRead(in1,jj) ); //Check that bits are properly read
       //Serial.println( bitRead(P1,jj) ); // Check that bits are properly written
     }
       
              //TEST MATRIX DISPLAY:
              //values 1, 2, 4, 8 for P2 have no effect in 1 player mode:
              //Need P2 non zero so matrix adjusting loop is entered
              P2=1;
              Serial.println(P2);
              //Serial.println(numPlayers);
        
              
    // Check the inputs and if the count is right, make changes to the matrix
    if (buttonCounter==buttonCountCheck){
      
      // Read input from multiplexer pins
      int input;


        for (int k=1; k<=numPlayers; k++){
          //Will only loop once if numPlayers==1
        // for (int k=1; k<3; k++)
        //Apply Transformations
          if (k==1){
            input=P1;
          }
          else if (k==2){
            input=P2;
          }
            
          if (P1!=0 | P2!=0) { //Only run through this loop if any buttons have been closed
          //if (input!=0)
            // Initialize button inputs:
            int left=0;
            int up=0;
            int right=0;
            int down=0;
            int checkPoint=0;
            int checkVline=0;
            int checkHline=0;
            int checkEraseBlock=0; 
            int checkCross=0;
            int checkCircle=0;
            int checkSquare=0;
            int checkDisplay=0; 
            
          } // Close Input Check
        } // Close P1 or P2 k-loop
        
        //Clear P1 and P2 to zero so that they are ready for the next round of inputs
        P1=0;
        P2=0;
    } // Close multiplexer Loop
  }// Close i-loop
}//Close main loop

Part2 of the code mentioned in the original post.

// Drop a byte into the shift register, one bit at a time
void shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
  // This shifts 8 bits out MSB first, 
  //on the rising edge of the clock,
  //clock idles low

  //internal function setup
  int i=0;
  int pinState;
  pinMode(myClockPin, OUTPUT);
  pinMode(myDataPin, OUTPUT);

  //clear everything out just in case to
  //prepare shift register for bit shifting
  digitalWrite(myDataPin, 0);
  digitalWrite(myClockPin, 0);

  //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--)  {
    // for i=7:-1:0
    digitalWrite(myClockPin, 0);

    //Them: if the value passed to myDataOut and a bitmask result 
    // true then... so if we are at i=6 and our value is
    // %11010100 it would the code compares it to %01000000 
    // and proceeds to set pinState to 1. (Delete this Later 
    // if my explanation below is right).

    if ( myDataOut & (1<<i) ) {
      pinState= 1;
    }
    else {	
      pinState= 0;
    }

    //Sets the pin to HIGH or LOW depending on pinState
    digitalWrite(myDataPin, pinState);
    //register shifts bits on upstroke of clock pin  
    digitalWrite(myClockPin, 1);
    //zero the data pin after shift to prevent bleed through
    digitalWrite(myDataPin, 0);
  }

  //stop shifting
  digitalWrite(myClockPin, 0);
}



//Multiplexer:
// Clock goes to pin A5 //yellow->blue
// Data goes to pin A4 // green ->green
// Buttons have Red to 5V, and Black to general purpose pins

//Check the General Purpose A register of the multiplexer
int checkGPA(){
  Wire.beginTransmission(0x20); //Open comms with multiplexer
  Wire.write(0x12); // set MCP23017 memory pointer to GPIOA address
  Wire.endTransmission(); // Close comms with multiplexer
  
   // // info from pins 21-28
  Wire.requestFrom(0x20, 1); // request one byte of data from MCP20317
  
  // Parse the bytes out into something meaningful:
  int P1 = Wire.read(); //First recieved byte stored here

  if (P1>0) // if a button was pressed
  {
    return P1;
    //Serial.println(P1, BIN); // display the contents of the GPIOB register in binary
    //delay(delayTime); // for debounce
  }
  else {
    return 0;
  }
}

//Check the General Purpose B register of the multiplexer
int checkGPB(){
  Wire.beginTransmission(0x20); //Open comms with multiplexer
  Wire.write(0x13); // set MCP23017 memory pointer to GPIOB address
  Wire.endTransmission(); // Close comms with multiplexer
  
   // // info from pins 1-8
  Wire.requestFrom(0x20, 1); // request one byte of data from MCP20317
  
  // Parse the bytes out into something meaningful:
  int P2 = Wire.read(); //First recieved byte stored here

  if (P2>0) // if a button was pressed
  {
    return P2;
    //Serial.println(P2, BIN); // display the contents of the GPIOB register in binary
    //delay(delayTime); // for debounce
  }
  else {
   return 0; 
  }
  
}

Problems with I2C can cause the Arduino to stop responding. This should have been minimized in 1.0x versions of the Arduino IDE. However, you will want to check your pullup resistors, SDA/SCL connections, etc.

I have a board that uses the MCP23017, and I created a library to go with it. You can easily use the same library with a self-built MCP23017 circuit: centipede_shield [macetech documentation]

You're also using a custom shift out function, but there's a canned shiftOut routine already...that might be causing problems.

Well I sure feel dumb. I was putting a musical instrument shield onto the arduino, and in the process shifted everything around. I discovered that my clock and ground wires were mixed up. This explains the I2C malfunction causing the Arduino to crash. The other problem was that in the void setup() loop I had forgotten:
Wire.begin();

As far as not using the built-in shiftOut function, it's because I am shifting out to 8 shift registers, and the built in shiftOut doesn't seem to be able to handle shifting to more than one shift register.