joeyoung / arduino_keypads

arduino libraries for keypad interface on I2C bus
GNU General Public License v3.0
153 stars 102 forks source link

Interrupt support on Keypad_I2C #9

Closed adrianbn closed 4 years ago

adrianbn commented 4 years ago

Hi,

thanks for this library, it's been lifesaving so far :) I'm trying to get a matrix keypad connected via PCF8574 to work with interrupts and finding it requires direct pin access to the underlying PCF8574 when using this library. I was expecting (clearly wrongly) that the INT pin of the PCF8574 would be pulled LOW whenever a key was pressed on the matrix keypad, but it seems to always be HIGH by default. I think the library is setting all pins HIGH on init thus disabling the interrupt function?

To be able to read a key pressed via interrupts I'm doing the following:

#include <Keypad_I2C.h>

#define I2C_PFC8574_ADDR 0x20
const int KEY_PRESS_INT_PIN = 3;

const byte ROWS = 4; // four rows
const byte COLS = 4; // four columns
char hexa_keys[ROWS][COLS] = {{'1','2','3','A'},
                             {'4','5','6','B'},
                             {'7','8','9','C'},
                             {'*','0','#','D'}
                             };

Keypad_I2C key_pad(makeKeymap(hexa_keys), rowPins, colPins, ROWS, COLS, I2C_PFC8574_ADDR); 

bool key_pressed = false;

void handleKeyPress() {
  key_pressed = true;
}

void setup() {
 // Init the I2C bus
  Wire.begin();
  // Init the Keypad library
  key_pad.begin();

  pinMode(KEY_PRESS_INT_PIN, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(KEY_PRESS_INT_PIN), handleKeyPress, FALLING);
  for (int i=0; i<4; i++) {
    key_pad.pin_write(i, 0);
  }
  // End of Keypad init
}

void loop() {
  if (key_pressed) {
    // ROWS, 4 is bottom
    for (int i=4; i<8; i++) {
      if (!key_pad.pin_read(i)) {
        // Found row, go for column
        Serial.print("Row: ");
        Serial.println(i);
        for (int j=0; j<4;j++) {
          key_pad.pin_write(j, 1);
          if(key_pad.pin_read(i)) {
            // Found column
            Serial.print("Column: ");
            Serial.println(j);
          }
          key_pad.pin_write(j, 0); // Reset value
        }
      }
  }
  key_pressed = false;
}

Ignoring the lack of debouncing and code quality, this is obviously not so great since I'm effectively bypassing the library and I have to deal with key mapping myself once row / column has been found. Is there a way to utilize interrupts that I'm missing?

Cheers, Adrian

joeyoung commented 4 years ago

The Keypad library does not work using interrupts. It depends on being called repeatedly from loop( ) and debouncing key presses using the millis( ) timer. The PCF8574 interrupt output would be asserted every time any pin changes state--when the column pins are driven low during scanning--but then it will be cleared when the row pins are all read.

The key mapping is already done by Keypad, you don't need to find the row and column and then look up the key in keymap, getKey( ) returns the looked-up value.

I guess I would ask what you want an interrupt for? If it's to do something when ANY key is pressed, the library has that capability; in setup( ), add the statement keypad.addEventListener(keypadEvent); as illustrated in the EventKeypad examples.

adrianbn commented 4 years ago

As far as I understand it keypad.addEventListener(keypadEvent) still requires constantly calling getKey() in the loop function. I'm trying to remove some of the things I do in the loop and increase the responsiveness of the keypad by getting an interrupt any time a key is pressed, then figure out which key was it.

The code I showed above does operate the keypad using interrupts, but it is using pin_read and pin_write directly to get the key being pressed instead of invoking getKey(). Even though interrupts are not supported by the Keypad library, I was curious if it was something Keypad_I2C would support since PCF8574 provides the necessary elements to do so.

joeyoung commented 4 years ago

Yes, the EventListener does still require calling getKey, or getKeys.

I missed your intention to just use the port access functions. If you don't need Keypad's capabilities, take a look at my shrPort libraries: shrPort.

But, as your code shows, to use a matrix keypad, you still have to generate the interrupt by scanning the columns (or rows) and you'd want to incorporate some debouncing, both already included with Keypad. Still by thus stripping out features, you may make the code smaller and more responsive.

adrianbn commented 4 years ago

I'll be looking at shrPort, thanks! Closing this as answered!