RGB LCD Backpack - Interrupt

EL Wire/Tape/Panels, LEDs, pixels and strips, LCDs and TFTs, etc products from Adafruit

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
User avatar
mkbehbeh
 
Posts: 8
Joined: Thu Nov 19, 2015 6:23 pm

Re: RGB LCD Backpack - Interrupt

Post by mkbehbeh »

GeertVc wrote:
adafruit_support_rick wrote:The mcp23017 helps you out with this a little bit by providing the INTCAP register, which will tell you which pin caused the interrupt.
This is not correct. The INTCAP register only "snapshots" the state of the IO pins the moment the interrupt happens. There's another register, INTF or Interrupt Flag Register, that indicates exactly which pin(s) have caused the interrupt. That register must be used to know the origin of the interrupt.
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 viewtopic.php?f=31&t=32813

Here are the code modifications to the library

Code: Select all

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

volatile bool isTriggered = false;
volatile uint16_t buttonEventTime;
volatile uint16_t prevButtonEventTime = 0;
in setup():

Code: Select all

//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

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)

Locked
Please be positive and constructive with your questions and comments.

Return to “Glowy things (LCD, LED, TFT, EL) purchased at Adafruit”