0

RGB LCD Shield Interrupt-On-Change
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

RGB LCD Shield Interrupt-On-Change

by deepz on Thu Sep 06, 2012 9:38 am

Hello.

In case it would be usefull to others, i have made a modification to the shield and the associated libraries to support the Interrupt-On-Change feature provided by the MCP23017 chip.

I have attached a PDF with the needed hardware (1 wire) and software (some lines of code to add) changes.

I hope this can be usefull,
Thanks,
Guido
Attachments
RgbLcdShieldInterrupt.zip
Add IOC feature to RGB LCD Shield
(252.13 KiB) Downloaded 297 times

deepz
 
Posts: 3
Joined: Sat Nov 12, 2011 4:56 am

Re: RGB LCD Shield Interrupt-On-Change

by joanba on Sat Jul 20, 2013 7:08 am

Thanks for your code and explanation !! It works for me.

Just one question: why the sketch crashes if I use the modes LOW or CHANGE in attachInterrupt function ?

Regards,
Joan
joanba
 
Posts: 18
Joined: Tue Oct 30, 2012 6:57 am

Re: RGB LCD Shield Interrupt-On-Change

by jefflawr on Tue Nov 26, 2013 2:45 am

Hi Guido,
This was a clear description of the necessary mod and equally clear code that was easy to put to use.
Thanks very much for taking the time to post this - it made my kiln logging project a lot easier.
Jeff in Santa Fe
jefflawr
 
Posts: 21
Joined: Thu Aug 01, 2013 2:24 pm

Re: RGB LCD Shield Interrupt-On-Change

by andybpenny on Sat Jan 11, 2014 1:58 am

Hi all,

I've made the modifications to allow interrupts on the Adafruit RGB LCD shield (via the MCP23017 chip), thanks Guido!! Interrupts are working great, however, I'm having a hard time catching which buttons were pressed. I have a rather involved sketch (GPS, SD, and multiple sensors) and too much time elapses between when the interrupt is signaled (flag in the ISR) and when lcd.readButtons() is executed later in the loop. I've tried moving lcd.readButtons() into the ISR so that it immediately follows the interrupt, but the program hangs as soon as a button is pressed. Has anyone successfully integrated the lcd.readButtons() with this interrupt setup on the LCD shield when using a more complicated sketch?

Thanks!
Andy
andybpenny
 
Posts: 3
Joined: Wed Dec 18, 2013 11:18 pm

Re: RGB LCD Shield Interrupt-On-Change

by mkbehbeh on Sun Jan 24, 2016 4:51 am

I am getting ready to try the same thing. I was planning to use the Interrupt capture register INTCAP(0x08) and save its contents in the interrupt routine. As long as you don't hit another button before you respond to the interrupt. it should have the correct button that was pressed.

mkbehbeh
 
Posts: 8
Joined: Thu Nov 19, 2015 6:23 pm

Re: RGB LCD Shield Interrupt-On-Change

by mkbehbeh on Wed Mar 02, 2016 4:24 pm

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=32813

Here 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)

mkbehbeh
 
Posts: 8
Joined: Thu Nov 19, 2015 6:23 pm

Please be positive and constructive with your questions and comments.