I got my interrupt capture code working. I found you have to use FALLING instead of change (details below)
I have code that causes significant delays to responding to an interrupt (interupts are enabled, but I dont get around to reading the register for a few hundred ms so I might miss a button press). Because of that I could not use the ReadButton () function. I wrote an update to read the capture register and it worked fine. With the 5 button keypad, I am only interested in one button press at a time and dont have any need to distinguish between multiple button presses. In the sketch, I treat multiple button presses (value different than 1,2,4,8, or 16 as 0 and do nothing.
Here is the additions to the libraries starting with the Hardware interrupt modification described in
https://forums.adafruit.com/viewtopic.php?f=31&t=32813Here are the code modifications to the library
- Code: Select all | TOGGLE FULL SIZE
Add to AdafruitLCDShield.h
void enableButtonInterrupt(); //Added to default library
void disableButtonInterrupt(); //Added to default library
uint8_t readIntButton();
void home();
Add to Adafruit_MCP23017.h
void enableButtonInterrupt(); //Added to default library
void disableButtonInterrupt(); //Added to default library
uint8_t readINTCAPA(); //Added to default library to read button capture interrupt
Add to AdafruitLCDShield.cpp
//Added to default library
void Adafruit_RGBLCDShield::enableButtonInterrupt() {
_i2c.enableButtonInterrupt();
}
//Added to default library
void Adafruit_RGBLCDShield::disableButtonInterrupt() {
_i2c.disableButtonInterrupt();
}
//Added to default library
uint8_t Adafruit_RGBLCDShield::readIntButton() {
uint8_t reply = 0x1F;
reply &= ~(_i2c.readINTCAPA());
return reply;
}
Add to Adafruit_MCP23017.cpp
//Added to default library
//From the Adafruit_RGBLCDShield library we learn that the
//buttons are on bits 0..4, which are on GPIOA,
//so we use GPINTENA, INTCONA and IOCONA.
void Adafruit_MCP23017::enableButtonInterrupt() {
uint8_t data;
//Enable the interrupts on the button pins 0..4
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPINTENA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
data = wirerecv();
data |= 0x1F; //Bits 0..4 high, to enable IOC
//Write the new value back
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPINTENA);
wiresend(data);
Wire.endTransmission();
//We set INTCONA bits 0..4 to 0 = State change interrupt
//(instead of compare to state)
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_INTCONA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
data = wirerecv();
data &= ~0x1F; //Force bits 0..4 to low
//Write the new value back
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_INTCONA);
wiresend(data);
Wire.endTransmission();
//We set the INTA pin to Active-Low
//first read current register value
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_IOCONA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
data = wirerecv();
//Bit 1 = INTPOL = Low = Active-Low
//(When disabled or no interrupt, signal is high)
data &= ~0x02;
//Bit 2 = ODR = Low = Open Drain disabled
data &= ~0x04;
//Bit 6 = MIRROR = Low = INTA/INTB seperate
data &= ~0x40;
//Write the new value back
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_IOCONA);
wiresend(data);
Wire.endTransmission();
}
//Added to default library
void Adafruit_MCP23017::disableButtonInterrupt() {
uint8_t data;
//Disable the interrupts on the button pins 0..4
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPINTENA);
Wire.endTransmission();
Wire.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
data = wirerecv();
data &= ~0x1F; //Bits 0..4 low, to disable IOC
//Write the new value back
Wire.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_GPINTENA);
wiresend(data);
Wire.endTransmission();
}
//Added to default library
//reads and returns the entire captur interrupt contents as full byte
//readIntButton() in the main library will mask byte and give button pressed
uint8_t Adafruit_MCP23017::readINTCAPA(){
WIRE.beginTransmission(MCP23017_ADDRESS | i2caddr);
wiresend(MCP23017_INTCAPA); //request contents of INTCAPA register
WIRE.endTransmission();
WIRE.requestFrom(MCP23017_ADDRESS | i2caddr, 1);
return wirerecv();
}
In the sketch I ended up using globals for the interrupt flag and FALLING to trigger interrupt on pin 2(vs pin3 in pdf). I found if you use CHANGE, then the interrupt triggers both when you press the button (sets interrupt pin) and when you read the INTCAP or GPIO register (clears interrupt pin)
Globals
- Code: Select all | TOGGLE FULL SIZE
volatile bool isTriggered = false;
volatile uint16_t buttonEventTime;
volatile uint16_t prevButtonEventTime = 0;
in setup():
- Code: Select all | TOGGLE FULL SIZE
//Catch the external interrupt from the button input
attachInterrupt(0,ISR_Button,FALLING);
//This signal is active low, so HIGH-to-LOW when interrupt
In the main loop you have to be careful to only read the capture register. If you read the GPIO instad of the INTCAP you will clear INTCAP
code in loop()
- Code: Select all | TOGGLE FULL SIZE
if (isTriggered && ((buttonEventTime - prevButtonEventTime) > 100)) {
Serial.println ("interrupt ");
isTriggered = false;
uint8_t IntButton = lcd.readIntButton();
if (IntButton) {
lcd.clear();
lcd.setCursor(0,0);
if (IntButton & BUTTON_UP) {
lcd.print("UP ");
lcd.setBacklight(RED);
}
if (IntButton & BUTTON_DOWN) {
lcd.print("DOWN ");
lcd.setBacklight(YELLOW);
}
if (IntButton & BUTTON_LEFT) {
lcd.print("LEFT ");
lcd.setBacklight(YELLOW);
}
if (IntButton & BUTTON_RIGHT) {
lcd.print("RIGHT ");
lcd.setBacklight(VIOLET);
}
if (IntButton & BUTTON_SELECT) {
lcd.print("SELECT ");
lcd.setBacklight(VIOLET);
}
}
}
}
else if ((buttonEventTime - prevButtonEventTime) <100) {
isTriggered = false;
lcd.readButtons();
}
//ISR to service the button interrupt
void ISR_Button() {
isTriggered = true; //Also flag we triggered
prevButtonEventTime = buttonEventTime;
buttonEventTime = millis();
}
the code works nicely with my code and gives very responsive button presses (no missed button presses)