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
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.
// 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");
}
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'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:
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.
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.
// 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".
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.