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.
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
-----------------------
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.
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
.
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
.
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.