MCP23008 and Matrix Keypad

Hi, I'm trying to follow the App Note AN1081 to get the keypad values of an MCP23008 over the I2C bus using Wire library and, although I can read the 'row' value fine, I can't get the 'col' to complete the byte ( it's always either all high or all low depending on how I initialize the chip).

Am I just misunderstanding the App Note? :~

Any pointers would be greatly appreciated.

Thanks in advance,
Ian

//
// Code in Startup
//

 pinMode(int_enable, INPUT);		// digital pin for Interrupt from MCP23008

 Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(6);					// GPPU Register
  Wire.send(15);				// Set pullups off for row and on for col pins
  Wire.endTransmission();			// Close comms
  Serial.println("GPPU");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(9);					// GPIO Register
  Wire.send(240);				        // Set outputs
  Wire.endTransmission();			// Close comms
  Serial.println("GPIO");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0);					// IODIR Register
  Wire.send(240);				// Set rows to inputs and cols to outputs
  Wire.endTransmission();			// Close comms
  Serial.println("IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(4);					// INTCON Register
  Wire.send(240);				// Set row on and col off
  Wire.endTransmission();			// Close comms
  Serial.println("INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(3);					// DEFVAL Register
  Wire.send(240);				// Default value for INTCON to follow
  Wire.endTransmission();			// Close comms
  Serial.println("DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(2);					// GPINTEN Register
  Wire.send(240);				// Enable row pins for interrupt
  Wire.endTransmission();			// Close comms
  Serial.println("GPINTEN");

//
// Code in Loop
//

 //
  // Interrupt Monitor
  //
  
  int dataR, dataC;

  if (int_result == HIGH)
  {
	  lcd.setCursor(0,1);
	  lcd.print("INT 0");
  }
  else
  {
	  lcd.setCursor(0,1);
	  lcd.print("INT I");

  Wire.beginTransmission(0x24);
        Wire.send(8);
        Wire.endTransmission();
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataR = Wire.receive();
        Wire.endTransmission();
		Serial.println("Data R: ");
		Serial.print(dataR, BIN);

  flip_gpio();
    
  Wire.beginTransmission(0x24);
        Wire.send(9);
        Wire.endTransmission();
        Serial.println("Register Send");
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataC = Wire.receive();
          Serial.println("Data got?");
        //Wire.endTransmission();
		Serial.println("Data C: ");
		Serial.print(dataC, BIN);

  init_gpio();

  }

//
// Procedures to either Reinitialize or Flip the IO
//


void init_gpio()

  //
  // Start of I2C Keyboard GPIO
  //
  // Initialize the 2nd MCP23008 as per AN1081
  // GPPU at initialize
  // GPIO set low
  // IODIR row pins in col pins out
  // INTCON set for row clear for col
  // DEFVAL 
  // GPINTEN rows enabled for int-on-change
  // Keyscan
  // Which key?
  // 
{
  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(6);				        // GPPU Register
  Wire.send(15);				// Set pullups off for row and on for col pins
  Wire.endTransmission();			// Close comms
  Serial.println("GPPU");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(9);				        // GPIO Register
  Wire.send(240);				// Set all outputs off 0x00 or on 0xFF
  Wire.endTransmission();			// Close comms
  Serial.println("GPIO");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0);				        // IODIR Register
  Wire.send(240);				// Set rows to inputs and cols to outputs
  Wire.endTransmission();			// Close comms
  Serial.println("IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(4);				        // INTCON Register
  Wire.send(240);				// Set row on and col off
  Wire.endTransmission();			// Close comms
  Serial.println("INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(3);				        // DEFVAL Register
  Wire.send(240);				// Default value for INTCON to follow
  Wire.endTransmission();			// Close comms
  Serial.println("DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(2);				        // GPINTEN Register
  Wire.send(240);				// Enable row pins for interrupt
  Wire.endTransmission();			// Close comms
  Serial.println("GPINTEN");
  
}

void flip_gpio()
{
  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0);				        // IODIR Register
  Wire.send(15);				// Set rows to inputs and cols to outputs
  Wire.endTransmission();			// Close comms
  Serial.println("Flip IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(4);				        // INTCON Register
  Wire.send(15);				// Set row on and col off
  Wire.endTransmission();			// Close comms
  Serial.println("Flip INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(3);				        // DEFVAL Register
  Wire.send(15);				// Default value for INTCON to follow
  Wire.endTransmission();			// Close comms
  Serial.println("Flip DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(2);				        // GPINTEN Register
  Wire.send(15);				// Enable row pins for interrupt
  Wire.endTransmission();			// Close comms
  Serial.println("Flip GPINTEN");
}

A link to the application note would be good, when I googled the number all I got was an application note about More than 3 PCI Masters with STPC Client

Ok found it.
How have you wired it up. Those resistors going to Vdd should go to the arduino ground.

Ooops,

Sorry 'bout that.

HTH
Ian

Ok,

Confused now because it says under 'Initiate the KeyScan' : The row pins are high due to the 2.2k Pullups on row pins. If I stick them to ground, the code just loops and I don't get either value out.

Ian

If I stick them to ground, the code just loops and I don't get either value out.

OK so put them back up to +ve and post all your code so that others can compile it, as there is probably something wrong with that.

Ok, pullups back to +ve and code below..

Cheers,
Ian

// IO Expander

#include <Wire.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(0);
int dispmode = 0;

const int int_enable = 9; 

int int_result;						// Current reading from int_enable pin

void setup(void) {
  
  Serial.begin(57600);
  
  Wire.begin();						// Initialize the I2C bus
  lcd.begin(20, 4);
  lcd.clear();
  
  //
  //	Initialize inputs/outputs
  //

  pinMode(int_enable, INPUT);		// digital pin for Interrupt from MCP23008
  
  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x06);					// GPPU Register
  Wire.send(0x0F);						
  Wire.endTransmission();				
  Serial.println("GPPU");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x09);					// GPIO Register
  Wire.send(0xF0);				        
  Wire.endTransmission();				
  Serial.println("GPIO");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x00);					// IODIR Register
  Wire.send(0xF0);						
  Wire.endTransmission();				
  Serial.println("IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x04);					// INTCON Register
  Wire.send(0xF0);						
  Wire.endTransmission();				
  Serial.println("INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x03);					// DEFVAL Register
  Wire.send(0xF0);						
  Wire.endTransmission();				
  Serial.println("DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x02);					// GPINTEN Register
  Wire.send(0xF0);						
  Wire.endTransmission();				
  Serial.println("GPINTEN");
}

int on_minute = 1;						// indicates that this is the first time through the program.

void loop(void){

	{

  int_result = digitalRead(int_enable);

  //
  // Interrupt Monitor
  //
  
  int dataR, dataC;

  if (int_result == HIGH)
  {
          lcd.setCursor(0,1);
	  lcd.print("INT 0");
  }
  else
  {
          lcd.setCursor(0,1);
	  lcd.print("INT I");

  Wire.beginTransmission(0x24);
        Wire.send(0x08);
        Wire.endTransmission();
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataR = Wire.receive();
        Wire.endTransmission();
		Serial.println("Data R: ");
		Serial.print(dataR, BIN);

  flip_gpio();
    
  Wire.beginTransmission(0x24);
        Wire.send(0x09);
        Wire.endTransmission();
           Serial.println("Register Send");
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataC = Wire.receive();
          Serial.println("Got data?");
        Wire.endTransmission();
		Serial.println("Data C: ");
		Serial.print(dataC, BIN);

  init_gpio();

  }
  
  on_minute = 0;						//signals that the program has been run once
  }

} //end loop

void init_gpio()

  //
  // Start of I2C Keyboard GPIO
  //
  // Initialize the 2nd MCP23008 as per AN1081 - GPPU off for rows, on for columns, GPIO set low?
  // IODIR row pins inputs, column pins outputs, INTCON set for rows, cleared for columns 
  // DEFVAL equals INTCON, GPINTEN rows enabled for int-on-change
  // 
{
  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x06);				        // GPPU Register
  Wire.send(0x0F);					// Set pullups off for row and on for col pins
  Wire.endTransmission();				// Close comms
  Serial.println("GPPU");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x09);				        // GPIO Register
  Wire.send(0xF0);					// Set as per App Note Table 1
  Wire.endTransmission();				// Close comms
  Serial.println("GPIO");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x00);				        // IODIR Register
  Wire.send(0xF0);					// Set rows to inputs and cols to outputs
  Wire.endTransmission();				// Close comms
  Serial.println("IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x04);				        // INTCON Register
  Wire.send(0xF0);					// Set row on and col off
  Wire.endTransmission();				// Close comms
  Serial.println("INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0X03);				        // DEFVAL Register
  Wire.send(0XF0);					// Default value for INTCON to follow
  Wire.endTransmission();				// Close comms
  Serial.println("DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x02);				        // GPINTEN Register
  Wire.send(0xF0);					// Enable row pins for interrupt
  Wire.endTransmission();				// Close comms
  Serial.println("GPINTEN");
  
}

void flip_gpio()
{
  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x00);				        // IODIR Register
  Wire.send(0x0F);						
  Wire.endTransmission();				
  Serial.println("Flip IODIR");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x04);				        // INTCON Register
  Wire.send(0x0F);						
  Wire.endTransmission();				
  Serial.println("Flip INTCON");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x03);				        // DEFVAL Register
  Wire.send(0x0F);						
  Wire.endTransmission();				
  Serial.println("Flip DEFVAL");

  Wire.beginTransmission(0x24);			// Open comms to 2nd MCP23008
  Wire.send(0x02);				        // GPINTEN Register
  Wire.send(0x0F);						
  Wire.endTransmission();				
  Serial.println("Flip GPINTEN");
}

Am I just misunderstanding the App Note?

I would say you are.

I can't follow what you are trying to do but it doesn't look right. Once a key press has been detected you should then put all the outputs to a 1 and then put them to a zero one at at time. When you detect the zero on the input then you know that the output that is currently zero is in the column number of your key press.

I can't see that in your code.

I'm just following the Flow diagram on p9 of the note and the description which says that once a key has been pressed you get an interrupt and you read INTCAP to get the row. You then 'switch' the IODIR register and set IOCON,DEFVAL & GPINTEN. If you then 'read' the GPIO register it should indicate the'column' pressed. My code for that is:

 Wire.beginTransmission(0x24);
        Wire.send(0x09);
        Wire.endTransmission();
           Serial.println("Register Send");
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataC = Wire.receive();
          Serial.println("Got data?");
        Wire.endTransmission();
		Serial.println("Data C: ");
		Serial.print(dataC, BIN);

Does that 'read' the register?

Ian

When you swap things over I think you are missing swapping over the enabling of the internal pull up resistors.

 Wire.beginTransmission(0x24);
        Wire.send(0x09);
        Wire.endTransmission();
           Serial.println("Register Send");
  Wire.requestFrom(0x24, 1);
        if(!Wire.available()) { }
          dataC = Wire.receive();
          Serial.println("Got data?");
        Wire.endTransmission();

Very confusing indenting. More readable is:

  Wire.beginTransmission(0x24);
  Wire.send(0x09);
  Wire.endTransmission();
  Serial.println("Register Send");
  Wire.requestFrom(0x24, 1);
  if(!Wire.available()) {  }   // <--------- don't need this
  dataC = Wire.receive();
  Serial.println("Got data?");
  Wire.endTransmission();    // <--------- don't need this

Wire.requestFrom returns the number of bytes read. If it is zero, waiting won't give you any more bytes, but send the program into an infinite loop.

You don't need Wire.endTransmission after Wire.requestFrom.

Better would be:

  Wire.beginTransmission(0x24);
  Wire.send(0x09);
  Wire.endTransmission();
  Serial.println("Register Send");
  if (Wire.requestFrom(0x24, 1) == 1)
    {
    dataC = Wire.receive();
    Serial.print ("Got: ");
    Serial.println (dataC, HEX);
    }
  else
    Serial.println("No data received.");

Note that under 1.0+ of the IDE Wire.send became Wire.write and Wire.receive became Wire.read.

@Grumpy_Mike

When you swap things over I think you are missing swapping over the enabling of the internal pull up resistors

App note says 'Two of the registers need to be configured only during initialisation and will not require further manipulation' these being GPPU (pullups) and GPIO.

@Nick Gammon

Wire.requestFrom returns the number of bytes read. If it is zero, waiting won't give you any more bytes, but send the program into an infinite loop.

You don't need Wire.endTransmission after Wire.requestFrom.

That clears that up for me.

Still not getting what I expect by following the app notes flow, so does this code 'read' the state of the GPIO register?

  Wire.beginTransmission(0x24);
  Wire.send(0x09);
  Wire.endTransmission();
  Serial.println("Register Send");
  if (Wire.requestFrom(0x24, 1) == 1)
    {
    dataC = Wire.receive();
    Serial.print ("Got: ");
    Serial.println (dataC, HEX);
    }
  else
    Serial.println("No data received.");

Many thanks,
Ian

Can you post the full code, as amended, please? The full code above has lots of stuff in it that you should have fixed by now. Also how about making a few functions? I have a page about I2C port expanders here:

const byte EXPANDER_PORT = 0x24;

// set register "reg" on expander to "data"
// for example, IO direction
void expanderWrite (const byte reg, const byte data ) 
{
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.send (data);  
  Wire.endTransmission ();
} // end of expanderWrite

// read a byte from the expander
byte expanderRead (const byte reg) 
{
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.endTransmission ();
  Wire.requestFrom (EXPANDER_PORT, 1);
  return Wire.receive();
} // end of expanderRead

Now instead of cluttering all your code with the beginTransmission / endTransmission stuff you can just call expanderWrite to send something and expanderRead to get a result.

Hi,

Code is as follows

// IO Expander

#include <Wire.h>
#include <LiquidCrystal.h>

LiquidCrystal lcd(0);
int dispmode = 0;

const int int_enable = 9;
const int EXPANDER_PORT = 0x24;

int int_result;						// Current reading from int_enable pin

// set register "reg" on expander to "data"
// for example, IO direction
void expanderWrite (const byte reg, const byte data ) 
{
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.send (data);  
  Wire.endTransmission ();
} // end of expanderWrite

// read a byte from the expander
byte expanderRead (const byte reg) 
{  
  int data;
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.endTransmission ();
  Wire.requestFrom (EXPANDER_PORT, 1);
  data = Wire.receive();
  Serial.print("Byte: ");
  Serial.print(data, BIN);
} // end of expanderRead

void setup(void) {
  
  Serial.begin(57600);
  
  Wire.begin();						// Initialize the I2C bus
  lcd.begin(20, 4);
  lcd.clear();
  
  //
  //	Initialize inputs/outputs
  //

  pinMode(int_enable, INPUT);		// digital pin for Interrupt from MCP23008
  
  //
  // Start of I2C Keyboard GPIO
  //
  // Initialize the 2nd MCP23008 as per AN1081 - GPPU off for rows, on for columns, GPIO set low
  // IODIR row pins inputs, column pins outputs, INTCON set for rows, cleared for columns 
  // DEFVAL equals INTCON, GPINTEN rows enabled for int-on-change
  //
  expanderWrite(0x06, 0x0F);
  Serial.println("GPPU");
  expanderWrite(0x09, 0xF0);
  Serial.println("GPIO");
  expanderWrite(0x00, 0xF0);
  Serial.println("IODIR");
  expanderWrite(0x04, 0xF0);
  Serial.println("INTCON");
  expanderWrite(0x03, 0xF0);
  Serial.println("DEFVAL");
  expanderWrite(0x02, 0xF0);
  Serial.println("GPINTEN");
}

int on_minute = 1;						// indicates that this is the first time through the program.

void loop(void){

	{

  int_result = digitalRead(int_enable);

  //
  // Interrupt Monitor
  //

  if (int_result == HIGH)
  {
    lcd.setCursor(0,1);
    lcd.print("INT 0");
  }
  else
  {
    lcd.setCursor(0,1);
    lcd.print("INT I");
    Serial.print("Interrupt Received..... ");
    
  expanderRead(0x08);
  flip_gpio();
  expanderRead(0x09);
  reinit_gpio();
  }
  
  on_minute = 0;						//signals that the program has been run once
  }

} //end loop

void reinit_gpio()
{  
  expanderWrite(0x00, 0xF0);
  Serial.println("IODIR");
  expanderWrite(0x04, 0xF0);
  Serial.println("INTCON");
  expanderWrite(0x03, 0xF0);
  Serial.println("DEFVAL");
  expanderWrite(0x02, 0xF0);
  Serial.println("GPINTEN");
  }

void flip_gpio()
{
  expanderWrite(0x00, 0x0F);
  Serial.println("IODIR");
  expanderWrite(0x04, 0x0F);
  Serial.println("INTCON");
  expanderWrite(0x03, 0x0F);
  Serial.println("DEFVAL");
  expanderWrite(0x02, 0x0F);
  Serial.println("GPINTEN");
}

Really more interested in is my take on the init, flip reinit following the app note.

Cheers,
Ian

  expanderWrite(0x06, 0x0F);
  Serial.println("GPPU");
  expanderWrite(0x09, 0xF0);
  Serial.println("GPIO");
  expanderWrite(0x00, 0xF0);
  Serial.println("IODIR");
  expanderWrite(0x04, 0xF0);

Why do you have pull-ups on the outputs and not the inputs?


How about naming some registers? eg.

  expanderWrite(GGPU, 0x0F);
  expanderWrite(GPIO, 0xF0);

Not much point in having a read function that doesn't return anything:

// read a byte from the expander
byte expanderRead (const byte reg) 
{  
  int data;
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.endTransmission ();
  Wire.requestFrom (EXPANDER_PORT, 1);
  data = Wire.receive();
  Serial.print("Byte: ");
  Serial.print(data, BIN);
} // end of expanderRead

Add:

return data;

Really more interested in is my take on the init, flip reinit following the app note.

Let's clear up the obvious stuff before we get too deeply into what the app note says.

Hi,
Thanks for the continuing help....

Here's the modified code:

// IO Expander

#include <Wire.h>
#include <LiquidCrystal.h>

#define IODIR 0x00
#define IPOL 0x01
#define GPINTEN 0x02
#define DEFVAL 0x03
#define INTCON 0x04
#define IOCON 0x05
#define GPPU 0x06
#define INTF 0x07
#define INTCAP 0x08
#define GPIO 0x09
#define OLAT 0x0A

LiquidCrystal lcd(0);
int dispmode = 0;

const int int_enable = 9;
const int EXPANDER_PORT = 0x24;

int int_result;						// Current reading from int_enable pin

// set register "reg" on expander to "data"
// for example, IO direction
void expanderWrite (const byte reg, const byte data ) 
{
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.send (data);  
  Wire.endTransmission ();
} // end of expanderWrite

// read a byte from the expander
byte expanderRead (const byte reg) 
{  
  int data;
  Wire.beginTransmission (EXPANDER_PORT);
  Wire.send (reg);
  Wire.endTransmission ();
  Wire.requestFrom (EXPANDER_PORT, 1);
  data = Wire.receive();
  Serial.print("Byte: ");
  Serial.print(data, BIN);
  return data;
} // end of expanderRead

void setup(void) {
  
  Serial.begin(57600);
  
  Wire.begin();						// Initialize the I2C bus
  lcd.begin(20, 4);
  lcd.clear();
  
  //
  //	Initialize inputs/outputs
  //

  pinMode(int_enable, INPUT);		// digital pin for Interrupt from MCP23008
  
  //
  // Start of I2C Keyboard GPIO
  //
  // Initialize the 2nd MCP23008 as per AN1081 - GPPU off for rows, on for columns, GPIO set low
  // IODIR row pins inputs, column pins outputs, INTCON set for rows, cleared for columns 
  // DEFVAL equals INTCON, GPINTEN rows enabled for int-on-change
  //
  expanderWrite(GPPU, 0x0F);
  expanderWrite(GPIO, 0xF0);
  expanderWrite(IODIR, 0xF0);
  expanderWrite(INTCON, 0xF0);
  expanderWrite(DEFVAL, 0xF0);
  expanderWrite(GPINTEN, 0xF0);
  Serial.println("Init complete");
}

int on_minute = 1;						// indicates that this is the first time through the program.

void loop(void){

	{

  int_result = digitalRead(int_enable);

  //
  // Interrupt Monitor
  //

  if (int_result == HIGH)
  {
    lcd.setCursor(0,1);
    lcd.print("INT 0");
  }
  else
  {
    lcd.setCursor(0,1);
    lcd.print("INT I");
    Serial.print("Interrupt Received..... ");
    
  expanderRead(INTCAP);
  flip_gpio();
  expanderRead(GPIO);
  reinit_gpio();
  }
  
  on_minute = 0;						//signals that the program has been run once
  }

} //end loop

void reinit_gpio()
{  
  expanderWrite(IODIR, 0xF0);
  expanderWrite(INTCON, 0xF0);
  expanderWrite(DEFVAL, 0xF0);
  expanderWrite(GPINTEN, 0xF0);
  Serial.println("reinit complete");
  }

void flip_gpio()
{
  expanderWrite(IODIR, 0x0F);
  expanderWrite(INTCON, 0x0F);
  expanderWrite(DEFVAL, 0x0F);
  expanderWrite(GPINTEN, 0x0F);
  Serial.println("Flip complete ");
}

Pullups are as per app note which says "GPPU controls the internal 100k pullup resistors. They are disabled for the rows and enabled for the columns. External resistors(2k2) are used for the rows".

Thanks for your help
Ian

So you have the external pull-ups installed? On the face of it, your code looks OK.

Can you paste your debugging output please?

As soon as I can get access to a debugger, I'll respond. Which do you recommend by the way?

Cheers,
Ian

This output:

  Serial.print("Byte: ");
  Serial.print(data, BIN);

You are printing stuff. Just share that with us.

Ok, Output on Serial is as follows:

INIT Enable
Interrupt Received.....
Byte: 11100000 //Row 4
Flip Complete
Byte: 11111111
reinit

INIT Enable
Interrupt Received.....
Byte: 10110000 //Row 2
Flip Complete
Byte: 11111111
reinit

Yes, I do have the external pullups in place and as above, I do get the row indication but never the column, example above shows which row has key pressed.

Thanks,
Ian