Some type of overlap between I2C and Serial1 on Mega 2560?

I've got a Mega 2560 connecting to a YwRobot I2C backpack on a 20x4 LCD, via pins 20 and 21. The system also uses an Adafruit Ultimate GPS on Serial2, and I'm using the serial monitor via USB. Additionally, I have falling interrupts setup for buttons on pins 2, 3, 18 and 19, using the associated interrupts (0, 1, 5, 4). I'm using the internal pullups for the interrupt pins. I am using the LiquidCrystal_I2C library for driving the LCD display.

Without the LCD being updated in my loop() method, the interrupts work fine, and the GPS works fine. With the LCD being updated in the loop, however, my pin 19 interrupt is being triggered by something (w/o touching the button), about every 300ms. I can write to the LCD just fine in my setup() method (ie, not in loop()) - I do some initialization of the LCD, and that works fine, but when I update the LCD in my loop() (even just a call to lcd.setCursor() will cause the problem), the interrupt on pin 19 is getting fired. It doesn't happen on every data push to the LCD, but it is happening very repeatably at ~300ms. If I set my LCD display update period to every 50ms, it happens every 6th update. If I set it to a 150ms update period, it happens every other update. (Where an update is an lcd.setCursor() followed by an lcd.print(), but either one of those alone will cause the problem.) The LCD works just fine through all of this - its just the triggering of interrupt 4 (pin 19) that is a problem.

Does the I2C do anything w pin 19 or interrupt #4?

I've tried updating the LCD as infrequently as every 10 seconds, just to see if maybe I was running into a timing problem w/ trying to update the LCD too frequently, but it still causes the problem. (Any update rate over 300ms results in the interrupt being triggered on every LCD update cycle.)

I'm kind of at my wit's end on this. If I disconnect the input to pin 19, everything works fine (but, of course, I lose the functionality of the button connected to that pin. :slight_smile:

I've tried using an external pull-up on pin 19, as well, with no change in behavior.

Thoughts or suggestions?

Thanks!

Does the I2C do anything w pin 19 or interrupt #4?

One would have to know specifically which I2C library you are using to even begin to answer this question.

Don

Sorry, I was first just checking to see if this was some kind of common knowledge that I just hadn't picked up on.

Here is my attempt at a simple test case. This is running on a Funduino Mega 2560, using the LiquidCrystal_I2C library and the Wire library. This sketch involves a button wired to pin 19 on one side, and to ground on the other side, as well as an I2C backpack LCD (20x4, backpack type of YwRobot). The I2C is connecting to pins 20 (SDA) and 21 (SCL) on the Arduino, as well as +5 and GND.

The interrupt-triggering behavior occurs when the button is wired to pin 19 (using interrupt #4). When the button is wired to pin 18 (using interrupt #5), this behavior does not occur.

The test sketch is catching the interrupt event, and setting a boolean and a byte val in the ISR, then reporting on those values on the next iteration of loop.

Running the sketch as-is results in a trigger of the interrupt on the lcd operation in setup, then again on the lcd operation occurring on each iteration of the loop. (This can be observed via the Serial Monitor.) It is interesting that the reported pin state in this case is 1 (using internal pull-up, so it seems its clearly not a result of some action on the pin.) If you press the actual button, the ISR fires, but correctly reports the pin state as zero (ie, button actually pushed).

If you comment out the 2 lines assigning the button pin and interrupt to pin 19 and interrupt 4, and uncomment the 2 lines assigning the button pin and interrupt to pin 18 and interrupt 5 (also wiring the button to pin 18 now), the ISR is not fired as a result of any lcd operations, but does of course correctly report when the button is pressed.

I'd love to know if I'm doing something dumb, or if this is some expected behavior, or any other suggestions for what might be going on, or what to try. (In my actual program where this is causing a problem, I've resorted to not assigning pin 19 to an interrupt, but am instead now polling the pin on each iteration of loop() - with some code-based pseudo-debouncing, to avoid re-triggering until it is actually pushed again.)

Thanks!

#include <LiquidCrystal_I2C.h>
#include <Wire.h>

//pin 19, int4 - interrupt is triggered on lcd operation
static byte buttonPin = 19;
static byte buttonInt = 4;

//pin 18, int5 - interrupt is not triggered on lcd operation
//static byte buttonPin = 18;
//static byte buttonInt = 5;

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);  // Set the LCD I2C address
//state-holder variables set in ISR
volatile bool buttonHit = false;
volatile byte buttonVal = 1;

void isr() {
  buttonHit = true;
  buttonVal = digitalRead(buttonPin);
}

//indicate that ISR was hit, and show button state at time it was hit
void checkButtonHit() {
  if (buttonHit) {
    Serial.print(millis());
    Serial.print(": isr hit. Pin state: ");
    Serial.println(buttonVal);
    //clear the hit flag
    buttonHit = false;
  }
}

void setup() {
  //setup button - internal pullup, using pin and interrupt set above
  pinMode(buttonPin, INPUT_PULLUP);
  attachInterrupt(buttonInt, isr, FALLING);
  
  //Serial so that we'll know when the ISR is hit
  Serial.begin(115200);
  Serial.println("I2C Interrupt4 Conflict Test");

  //start LCD, and do the first operation - this triggers interrupt #4, but not interrupt #5
  lcd.begin(20,4);
  lcd.backlight();
  lcd.setCursor(0,0);
  lcd.print("Print from Setup");
  
  //report if the ISR was hit from lcd operation in setup
  checkButtonHit();
  
  Serial.println("entering loop...");
}

void loop() {
  //call lcd operation, which will trigger ISR for interrupt #4, but not interrupt #5
  lcd.setCursor(0,1);
  lcd.print(millis());
  
  //report if the ISR was hit from lcd operation in loop
  checkButtonHit();
  
  //don't overload the Serial buffer
  delay(100);
  
}