ISR with GPIO Expander Issues - HELP

Ok so I have a problem with either my hardware or software that is driving me absolutely batty.

I'm interfacing my arduino to a Microchip MCP23017 I/O expander chip via the I2C bus. There are two 8 bit ports on the 23017 and I've designated one port as rows and the other as columns so that I can create a switch matrix. Port A (rows) has the internal pullups enabled so its always pulled high and is set as an input. Port B (cols) is initialized as low output so that when a switch in the matrix is pushed, it triggers an interrupt. The Interrupt A pin on the 23017 is attached to Pin 2 on my ATMEGA2560. When the interrupt occurs it sets a flag in my ISR in my code that a key was pressed. Also, it prints to the serial monitor, "ISR" (meaning the ISR was triggered - for debugging purposes). Then a handling function is called that reads the interrupt capture on that port and prints it to the screen ("10111111", or something like that). Later i will use that information to decode the binary number and determine what switch was pressed, etc. Right now i am simulating a pressed button on the breadboard by just use a jumper wire to connect the row to col.

Here's the problem, whenever, I press a button, it prints "ISR" several times in the monitor. This tells me that it calls the ISR way too many times. Also, ISR is being called when I let go of the switch. I only want the interrupt to trigger when I press the switch. Below is what the serial monitor looks like if i press a button.

press a button on the expander's switch matrix
ISR
ISR
ISR
ISR
ISR
ISR
11111111 <-reading INTCAPA register, one of these should be zero depending on what pin is grounded

let go of button
ISR
11111111 <-reading INTCAPA register

Here is my code. Its not all the code but its the only part that pertains to the I2C expander and matrix.

ExpanderWrite is a function I wrote that takes the I2C device addr, the register, and the data as the parameters. So i am initializing the i2c device in the Setup().

void setup(){
  Serial.begin(9600); //beging serial comm at 9600 bits/s
  Wire.begin(); //wake up the i2c bus
  Serial.println("Boot-up sequence");

  expanderWriteBoth (I2C_TOGS, IOCON, 0b00000000); // mirror interrupts, disable sequential mode

  expanderWrite(I2C_TOGS, IODIRB, 0x00);

  expanderWrite(I2C_TOGS, GPIOB, 0x00);

  expanderWrite(I2C_TOGS, IOPOLA, 0x00);

  expanderWrite(I2C_TOGS, DEFVALA, 0xFF);

  expanderWrite(I2C_TOGS, INTCONA, 0x00);

  expanderWrite(I2C_TOGS, GPPUA, 0xFF);

  expanderWrite(I2C_TOGS, GPINTENA, 0xFF);

  // no interrupt yet
  keyPressed = false;

  // read from interrupt capture addr A to clear it (reading resets the addr interrupt)
  expanderRead(I2C_TOGS, INTCAPA);

  // pin 19 of MCP23017 is plugged into D2 of the Arduino which is interrupt 0
  attachInterrupt(0, keypress, FALLING);


  Serial.println("Initializing LCD...");
  initializeDisplay(); // initialize the display
  Serial.println("Initializing Cue Statuses");
  // initializeCues(cue);  //initialize cues

}

// ISR
void keypress(){
  keyPressed = true;
  Serial.println("ISR");
}


// Main Program Loop
void loop(){
  unsigned long i;
  // some imaddrant calculations here ...
  for (i = 0; i < 0x7FFFF; i++) {
  }

  // was there an interrupt?
  if (keyPressed){
    handleKeypress();
  }

  //updateDisplay(); if(displayData);


  // read Serial port
  /*if(Serial.available() > 0){
   char command = Serial.read(); //read the data
   delay(100); // wait for entire message to be received
   
   }*/

  /*
  
   
  /*
   void fireSelectedCue(int cue){
   Serial.write(cue);
   }
   
   */
}



// Write to both addrs of the expander
void expanderWriteBoth (const byte addr, const byte reg, const byte data ) {
  Wire.beginTransmission (addr);
  Wire.write(reg);
  Wire.write(data);  // addr A
  Wire.write(data);  // addr B
  Wire.endTransmission();
} 


// called from main loop when we know we had an interrupt
void handleKeypress ()
{
  unsigned int keyValue = 0;
  byte readData;
  int col;
  boolean flag = false;
  byte bitData = 0x00;
  delay(100);  // de-bounce before we re-enable interrupts

  for(int i=0; i<8; i++){
    expanderWrite(I2C_TOGS, GPIOB, bitSet(bitData,i));

    readData = expanderRead(0x24, GPIOA);
    if(readData != 0 && !flag){
      flag=true;
      col=i;
    }

    expanderWrite(I2C_TOGS, GPIOB, 0x00);

  }



  if(readData != 0x00){
    readData |= col;
    Serial.println(readData, BIN);
    readData = 0x00; 
  }

  //expanderWrite(I2C_TOGS, GPIOB, 0x00);

  expanderRead(0x24, INTCAPA); //reset the interrupt capture on addr A

    keyPressed = false;  // ready for next time through the interrupt service routine

  /*
  // display which buttons were down at the time of the interrupt
   for (byte button = 0; button < 16; button++)
   {
   // this key down?
   if (keyValue & (1 << button))
   {
   Serial.print ("Button ");
   Serial.print (button + 1, DEC);
   Serial.println (" now down");
   }  // end of if this bit changed
   
   } // end of for each button
   
   // if a switch is now pressed, turn LED on  (key down event)
   if (keyValue)
   {
   time = millis ();  // remember when
   digitalWrite (ONBOARD_LED, HIGH);  // on-board LED
   }  // end if*/

}  


// write a byte to the expander
void expanderWrite(const byte addr, const byte reg, const byte data){
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.write(data);
  Wire.endTransmission();
}

// read a byte from the expander
unsigned int expanderRead (int addr, const byte reg) 
{
  Wire.beginTransmission(addr);
  Wire.write(reg);
  Wire.endTransmission();
  Wire.requestFrom(addr, 1);
  return Wire.read();
}

You don't really have a Serial.print() statement in an ISR, do you? I'm sure that that must be a figment of my imagination.

Here's the problem, whenever, I press a button, it prints "ISR" several times in the monitor. This tells me that it calls the ISR way too many times.

It tells me that your "switch" is bouncing.

HAHA point taken. I will try taking the serial.print() out. Just curious - why is a print statement forbidden in there?

Also, isn't the switch being "de-bounced" in my handleKeypress() routine because of the delay(100)?

Just curious - why is a print statement forbidden in there?

Sending data via the serial port requires that interrupts be enabled. They are not, while your ISR is running.

Serial.print() simply writes to a buffer, and then returns. So, it's perfectly safe to use in an ISR. Providing, of course, that there is room in the outgoing buffer for the data that print() is going to out in the buffer.

But, you have no idea how much data print() is going to put there, or how much room is available.

If there isn't room, print() will simply wait for there to be room. That will happen when the appropriate interrupt happens. But, wait, those interrupts don't happen while the ISR is running, so there never will be room, so print() will never return.

Not a good thing to have happen, is it?

Also, isn't the switch being "de-bounced" in my handleKeypress() routine because of the delay(100)?

In the ISR? delay() doesn't work in ISRs because delay() relies on clock ticks happening, which are interrupt driven, and interrupts don't happen in an ISR.

If the delay() isn't in the ISR, then it has no impact on the interrupt happening.

Well if the interrupts are not enabled during my ISR, how am I getting information on the serial monitor?
Sorry, I'm just slightly confused.

And no the delay is happening after the ISR in my handleKeypress() routine. I know it isnt a debouncing issue because I get the same number of "ISR" prints on the monitor when I simulate a keypress. If it was bouncing that number would vary, correct?

Well if the interrupts are not enabled during my ISR, how am I getting information on the serial monitor?
Sorry, I'm just slightly confused.

The data is actually sent after the ISR ends. Prove it to yourself. Turn on an LED in the ISR. Call Serial.print(). Add while(1); after the Serial.print() statement in the ISR. Time how long it takes for the serial data to arrive after the LED comes on.

Come back in a couple of years and let us know the results.

I know it isnt a debouncing issue because I get the same number of "ISR" prints on the monitor when I simulate a keypress. If it was bouncing that number would vary, correct?

That would depend on how you simulate the key press.

PaulS:
The data is actually sent after the ISR ends. Prove it to yourself. Turn on an LED in the ISR. Call Serial.print(). Add while(1); after the Serial.print() statement in the ISR. Time how long it takes for the serial data to arrive after the LED comes on.

Come back in a couple of years and let us know the results.
That would depend on how you simulate the key press.

I think I have everything working now. The only issue I have now is that the ISR is called on any change to the pin (getting pulled high or low). The INTCONA register is supposed to allow me to control if the interrupt triggers on change from the default value or trigger on any change. So I tried setting it to trigger on change from the default value but when I do that the interrupt triggers once, then never resets. Thoughts?

Anyone have any ideas on why the ISR isn't resetting?