Keypad matrix with PCF8575

Hi. I'm working on using a PCF8575 port expander to read a keypad 8x8 matrix.
This board has two bytes of values, first byte for pins 0 to 7, and the second byte for pins from 8 to 15.
In the setup function, I set the first byte as 0xFF and the second one as 0x00.
If I jump any pin from pins 8 to 15 (that are all low), to any pin from 0 to 7 it returns a 0 in the proper 8-bit BIN value in the first byte. For instance, if I short pins P12 and P02, I get: 11011111 00000000.
Now, if I change the value of the second byte to 0xF7 it means that not jumping any pin, I get a read of 11111111 11101111. In this case, in order for the first byte to react, I can only do it by jumping any pin from pin P13. Any other pins from 8 to 15 will do nothing.
So far so good, that's the basic principle of a matrix, you turn one column low (in this case columns would be byte 2, aka pins from 8 to 15) at a time, and scan each row separatedly.

So I have a couple of questions to proceed from here:

  • How can I do a for function that jumps from 11111110, to 11111101, to 11111011, etc?
  • the method to read and write these boards, since they consist of two bytes, is to run the Wire.write() function twice, as you can see, in the setup function (code below). For the matrix function I'm working on, I need to be able to write a value the first byte, then read the second byte, then write again the first byte, and so on so forth. How would you suggest I do this?

Test code for now reads what pins from 0 to 7 are jumped:

#include "Wire.h"
#define address 0x20

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.beginTransmission(address);
  Wire.write(0xFF);
  Wire.write(0x00);
  Wire.endTransmission();
}

void loop() {
  byte read_value;
  if (Wire.requestFrom(address, 2) == 2) {
    read_value = (Wire.read());
    for (int i = 0; i < 8; i++) {
      Serial.print(bitRead(read_value, i));
    }
    Serial.print("\t");
    read_value = (Wire.read());
    for (int i = 0; i < 8; i++) {
      Serial.print(bitRead(read_value, i));
    }
    Serial.println();
    delay(500);
  }
}

That part is easy, and all I address here.

Use an unsigned character to hold a bit. Use the inverse of that (~ operator), and then shift the single 1 a binary place to the left to access the next row or column or whatever you doing.

Tested. The last value printed does not show the leading zero, but of course it is there. :expressionless:

void setup() {
  Serial.begin(115200);

  unsigned char theBit;

  theBit = 1;  
  for (int n = 0; n < 8; n++) {
    unsigned char temp = ~theBit;  // temp is theBit all bits inverted

    Serial.println(temp, 2);     // print in binary

    theBit <<= 1;    // mover the bit over for next time
  }
}

void loop() {
}

HTH

a7

Check -GitHub - RobTillaart/I2CKeyPad8x8: Arduino library for 8x8 or smaller KeyPad connected to an I2C PCF8575.

Hi Rob. I already did, and we had a conversation about it in github. I need to be able to "capture" multiple buttons pressed at the same time.

Is your goal to detect (and ignore) a multikey press, or do you want to know which keys are pressed.
The first is simpler, the second is much more difficult as there can be 2..64 keys pressed in theory.
If it is only about detecting keys pressed you have already three scenarios

  • both on one row
  • both on one column
  • both row and column differ (diagonal).

Scenario 1 and 2 are relative easy to detect.
Scenario 3 is as far as I can see ambiguous if you scan all rows/columns at once.

It is for a gaming device, so yes, I need to know what keys are being pressed.
I'm using a matrix with diodes to prevent ambiguity.

Thank you all. I think I've got it, though I still have to test it with the button matrix (for now I have only been able to test single button presses, cause I'm waiting for some diodes that I puchased to arrive). I think they way it works should capture any button presses simultaneosly, since it's storing the value of each button/switch in an array.

Here's the code, which is still a test code for the Mega (hence the commented lines), in case anyone wants to have a look at it and sees anything wrong:

//#include "Joystick.h"
#include "Wire.h"
#define address 0x20

int buttons[8][8] = {
  { 0, 1, 2, 3, 4, 5, 6, 7 },
  { 8, 9, 10, 11, 12, 13, 14, 15 },
  { 16, 17, 18, 19, 20, 21, 22, 23 },
  { 24, 25, 26, 27, 28, 29, 30, 32 },
  { 33, 34, 35, 36, 37, 38, 39, 40 },
  { 41, 42, 43, 44, 45, 46, 47, 49 },
  { 50, 51, 52, 88, 53, 54, 55, 57 },
  { 58, 59, 60, 61, 62, 63, 64, 86 },
};
bool buttonvalues[8][8] = {
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
  { 1, 1, 1, 1, 1, 1, 1, 1 },
};
int xAxis_ = 0;
int yAxis_ = 0;

//Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, JOYSTICK_TYPE_JOYSTICK, 90, 0, true, true, false, false, false, false, true, true, false, false, false);

void setup() {
  Serial.begin(115200);
  Wire.begin();
  Wire.beginTransmission(address);
  Wire.write(0xFF);
  Wire.write(0xFF);
  Wire.endTransmission();
  //Joystick.begin();
}

void loop() {
  byte read_value;
  byte write_value = 1;
  for (int n = 0; n < 8; n++) {
    byte temp = ~write_value;
    Wire.beginTransmission(address);
    Wire.write(temp);
    Wire.write(0xFF);
    Wire.endTransmission();
    if (Wire.requestFrom(address, 2) == 2) {
      read_value = (Wire.read());
      read_value = (Wire.read());
    }
    for (int i = 0; i < 8; i++) {
      if (bitRead(read_value, i) != buttonvalues[i][n]) {
        //Joystick.setButton(buttons[i][n], !bitRead(read_value, i));
        Serial.print(buttons[i][n]);
        Serial.print("pressed");
        Serial.print("\t");
        buttonvalues[i][n] = bitRead(read_value, i);
        Serial.println();
      }
    }
    write_value <<= 1;
  }
  delay(500);
}

@Paul_B, we've talked about this in private. I thought you may want to have a look at it too.

I'm not sure it is wrong, but that looks odd.

Also, there is no else clause in that if statement - how would you know if your request() was failing? And what would then be in read_valje?

   if (Wire.requestFrom(address, 2) == 2) {
      read_value = (Wire.read());
      read_value = (Wire.read());
    }
    else Serial.println(" this should not print!");

Sry if that's a red herring. I am looking through the tiny window just now.

a7

That's because I only need to read the 2nd byte. The first byte is the one I'm writing.
In order to read the 2nd byte I need to call the function Wite.write twice, unless there's a better way to do it.

That would mean that the arduino can't contact the PCF. In that case there's something wrong and an else statement wouldn't be of much help, I think.

OK IC. Logical.

Might it not draw your attention to a problem that needs addressing?

a7

Good morning everyone.
Sorry to reopen this topic, but although the above code works fine for the most part, I do have found a minor issuse with it. There's a very easy workaround to it, but if I can find the root problem and fix it the proper way, instead of applying patches, I would rather do that.

So to re-cap and sumarize, I have an 8x8 matrix with diodes, connected to a PCF8575, so values of the presses are passed to the Arduino via I2C.
To this matrix, there are several inputs connected:

  • normal push buttons: one lead to one row, one lead to one column
  • 3, 4 and 5 position switches: these have one common pin for all the positions, and each position has an individual pin. Essentially is like having multiple pushbuttons linked together. The common pin is connected to row, and each individual position pin to a column.

There is a diode for each row pin, with the cathode pointing towards the button/switch pin, and to make things easier, I designed and produced a PCB that looks like this (I hope you can see it properly, ignore the separate connectors, those are for something else):


ISSUE: The 4-way switches, although they are not intended to be pressed in multiple directions (it's function is to press only up, down, left or right), they can phyisically be pused together (i.e. up + right). In some instances (not with all combinations), when that happens, other non-related to that switch button presses are detected.

Obviously, the easiest workaround would be to plug the 4 way switches that are giving problems to an additional, separate PCF8575 and treat those independently. But I'd like to understand if there is something wrong with my code, or with the polarity of the diodes, or something else. Maybe I should reverese the polarity of the dioides, maybe I should try switching around columns and rows... I don't know.

Ghosting in matrix keyboards is eliminated with a diode in series with every set of contacts.

As you have seen, your scheme is allowing ghosts.

You could move the buttons that are inadvertently being pressed at the same time to switch positions that do not ghost one another.

I think one on every row would work. Or one on every column. Or as many as eight arranged in that configuration where eight queens cannot attack each other. Which is to say I just don't know.

Or use four real I/O input pins, or as you suggest another separate keypad.

a7