Electronic – Pressing one key in a button matrix reads all keys in that column

buttongpiomatrixmcp23s17

I'm using an MCP23017 IO Expander to make a button matrix (4×4).
Button Matrix
IO Expander

//Defines for Matrix Rows/Columns
#define ROW0    0x10
#define ROW1    0x20
#define ROW2    0x40
#define ROW3    0x80
#define COL0    0x01
#define COL1    0x02
#define COL2    0x04
#define COL3    0x08

void bRead(){
    mcp_send_command(IObut1, MCP_GPIOB,ROW0); //Row0 High
    mcp_read_command(IObut1,MCP_GPIOB); //Read the Port, state stored in 'receive' variable
    k1 = (receive&COL0); //If the Col0 bit is set, then K1 was pressed
    k2 = (receive&COL1);
    k3 = (receive&COL2);
    k4 = (receive&COL3);
    mcp_send_command(IObut1, MCP_GPIOB,ROW1);
    mcp_read_command(IObut1,MCP_GPIOB);
    k5 = (receive&COL0);
    k6 = (receive&COL1);
    k7 = (receive&COL2);
    k8 = (receive&COL3);
    mcp_send_command(IObut1, MCP_GPIOB,ROW2);
    mcp_read_command(IObut1,MCP_GPIOB);
    k9 = (receive&COL0);
    k10 = (receive&COL1);
    k11 = (receive&COL2);
    k12 = (receive&COL3);
    mcp_send_command(IObut1, MCP_GPIOB,ROW3);
    mcp_read_command(IObut1,MCP_GPIOB);
    k13 = (receive&COL0);
    k14 = (receive&COL1);
    k15 = (receive&COL2);
    k16 = (receive&COL3);
}

Just to run through the logic, in case I'm missing something obvious:

  1. Rows set to output, Columns set to input
  2. Set one row high, and read the columns.
  3. If a column is high, then the key in that column, on that row, is pressed.

The problem I'm having at the moment is that when I press a key, then any key in that column will read as pressed when I go through the rows. Eg: If I hold down Key1 and set Row0 high, then I read the key without issue. But if I keep Key1 pressed, then Key5 reads as pressed when I set Row0 high, then Key9 on Row2, and Key13 on Row3 (likewise for the other columns).

The previous row is set low when I move onto a new row, so there aren't two rows high at any given time. I've tried setting all rows to zero, adding a 10ms delay, then setting the next row high to ensure it's not just catching the result before the previous row goes low. I've also tried setting the internal pullups on/off for rows/columns/both.

Any help would be greatly appreciated – hopefully I'm missing something obvious.

Best Answer

The columns need a pull down, I see no evidence of that above.

The way you have the diodes facing the buttons will pull the column upwards, so you need to counter that with a pull-down (so you can see a low signal when the button is inactive).

Alternatively you could swap the signal directions and polarities and send a low signal on the columns and receive it on the rows. (with pull-up on the rows)