Search code examples
arduinokeypadmultiplexing

4x4 membrane keypad giving unexpected signals


I am working on a project where I try to code my own function for a membrane keypad. I want the 16 key states to be stored in a uint16_t variable, so I have only one variable. The code compiles.

The problem is that it will not properly display the key states. When I press the 1 key, it will tell me a lot more keys are being pressed. It will also display that keys are being pressed without touching the thing at all.

Pins 5-8 of the keypad are connected to PORTD, pin 4-7 on the Nano. Pins 1-4 of the keypad are connected to PORTB, pin 0-3 on the Nano.

Keypad [PDF].

This is the code.

uint16_t keys = 0;

void setup() {
  // put your setup code here, to run once:
  DDRD |= 0xF0; //Set bit 4-7 as output on PORTD
  DDRB &= 0xF0; //Set bit 0-3 as input on PORTB
  Serial.begin(9600); 
}

void loop() {
  getKeys(&keys);
  Serial.println("-----------------------");
  delay(100);
}

void getKeys(uint16_t* keys){
  *keys = 0; //Clear keys variable
  for(int rows = 0; rows < 4; rows++){ //Loop through every row

    PORTD &= 0x0F; //Turn pin 4-7 on PORTD off 
    PORTD |= (16 << rows); //Turn on pin.
    Serial.print(String(PORTD) + String(" : ")); //Print current selected bit(row)
    uint16_t temp = (PINB & 0x000F); //Take bit 0-3 from PORTB
    Serial.println(temp, BIN); //Print the PINB values as binary
    *keys |= (temp << (rows*4)); //Shift the PORTB values into keys.
  }
}

This is the output in the Serial monitor.

16 : 0
32 : 0
64 : 0
128 : 0
-----------------------
16 : 0
32 : 0
64 : 0
128 : 1
-----------------------
16 : 0
32 : 0
64 : 0
128 : 11
-----------------------
16 : 0
32 : 1000
64 : 10
128 : 1111
-----------------------
16 : 1000
32 : 1110
64 : 1110 
128 : 1111
-----------------------
16 : 1000
32 : 1110
64 : 1110
128 : 1111
-----------------------
16 : 0
32 : 0
64 : 0
128 : 0
-----------------------
16 : 0
32 : 0
64 : 0
128 : 0
-----------------------

Solution

  • When you have all keys unpressed, then you inputs are not connected to anything. They just are "floating in the air" and can receive any electrical disturbance from outside.

    Keypad should work in other way.

    1. On input pins (PINB) turn on pull-up resistors DDRB &= 0xF0; PORTB |= 0x0F. Then ALL you inputs now should read as logical 1, eg. 1111.

    2. On output pins (PORTD) provide HIGH level (logical 1) to all rows, except the one, that you are going to measure (this should be LOW level - logical 0), e.g. 1110, 1101, 1011, 0111.

    3. Then you can read pressed key as LOGICAL 0 on input pins (PINB). After that, you can invert the read number, if you need (bitwise NOT operator ~), e.g. temp = PINB; temp ~= temp; temp &= 0x0F or just temp = (~PINB) & 0x0F.

    The same way you can use bitwise NOT opearator before output, e.g. PORTD = (~(16 << rows)) & 0x0F.

    This way your inputs on PINB will always be connected to some source - to VCC (+5V) through pull-up resistors inside the AVR chip, or to GND, when you set logical 0 on output pins of PORTD. And you will not receive any electrical noise on them.