4x4 keypad arduino non blocking - Page 1
Forum section: electronics / beginners

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: May 24, 2017, 12:54:06 pm
content: 1. I am using ATMega 2560 arduino board & want to interface 4x4 keypad in it.It has lots of other peripherals like graphical lcd, relay, 3 uart peripherals. SO bit of large code in it.2. Need to interface 4x4 keypad. Was looking at this library.https://playground.arduino.cc/Main/KeypadTutorial3. On this page, its written that:" If key presses seem to take a long time to show up then you are probably using long delay()'s in your code. The same thing can happen if you use too many small delay()s like delay(10)."4. In my code, there are lots of peripherals connected, in many places I need to add delay for some functions.5. Is there any other keypad library, which is completely independent of this. So that I can read it in main without this delay issue & it should be non blocking

author: dferyance
author_rank: Regular Contributor
timestamp: May 24, 2017, 05:00:03 pm
content: I'm probably not going to be the most helpful but I hope I can give you a few tips / ideas since I have done some work with scanning matrix keypads.One possibility is wire it up that you can get an interrupt any time a key is pressed. You will have to scan the keys after the interrupt to know what was pressed but that way you don't have to be polling all the time. Be careful of ghosting / multiple keypresses though.When i've done it in PSoC, I used the UDB to scan the matrix in digital logic using a separate clock. This meant that the CPU only had to act to read what changed when something changed and didn't have to do anything with polling. You don't have that option in arduino but you may want to consider a separate keypad controller. This is like how your PC has a separate keyboard controller.Ultimately scanning the keys will take time. You can either try to be smart about it to not have to scan all the time, or offload that onto something else.

author: mariush
author_rank: Super Contributor
timestamp: May 24, 2017, 05:22:08 pm
content: Use a smaller cheaper arduino compatible or PIC or some microcontroller to create a sort of "keyboard" controller.The "controller" could then talk to your main board through i2c or uart or spi and respond to commands (like "return 2 bytes with the state of all 16 buttons" or "tell me if button x is pressed" ) and could also send a message that would cause an interrupt on your main board to tell it that the state of some button has changed (and then the board can quickly read the state of all buttons)Your controller could also do some sort of software or hardware debouncing of keys.You can determine if a button is pressed or not with very few pins (but at the cost of using a lot of resistors) by using the ADC inside microcontroller to read the voltage on an input pin. Cleverly using different value resistors for each button, you have a voltage divider which can produce a different voltage for each button pressed, so the microcontroller only has to read the voltage and convert it to the button number.See TIP #5 and TIP #7 in this book :http://ww1.microchip.com/downloads/en/DeviceDoc/01146B.pdfIf you want to be able to detect multiple buttons pressed at same time, the easiest way would be to use a chip with 16 input pins one for each button - your chip simply reads the 1s and 0s (button pressed or not) and packs them into 2 bytes and compares the value with a previous read... if the value is different, send it to main board through i2c or spi or uart or whatever , or just "ping" the main board (so that an interrupt would pop on the main board) to tell it the buttons have changed and then give the bytes when the board requests them.Your controller could also control the number of "refreshes", limiting for example to maximum 25-50 changes per second if there's no need for higher update frequency.You can reduce the number of pins by reading the buttons in groups.. for example group the buttons in segments of 8, so you'd need 8 input pins , and use 2 output pins to send voltage to the buttons, one segment at a time. You send 1 on one output pin, you read the 1s and 0s for the 8 buttons (voltage goes through the pressed button, through a resistor, into your input pin)... you set the output pin to 0. You then send 0 on the first output pin and send 1 on the other output pin which sends voltage to the other group of 8 leds and you read the 8 buttons now.And you keep alternating between first and second group of leds to refresh the button states.So with 10 pins you read 16 buttons.

author: FrankBuss
author_rank: Unknown
timestamp: May 24, 2017, 05:39:40 pm
content: Quote from: mariush on May 24, 2017, 05:22:08 pmThat's not necessary, if you use interrupts. But I would suggest to implement a timer interrupt and then a state machine to scan the keyboard, which makes debouncing easy, too.QuoteOr simply add diodes to each button, if you can access each button, as explained here (later in the article) :http://blog.komar.be/how-to-make-a-keyboard-the-matrix/Then you need only 2x pins for x^2 keys.

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: May 24, 2017, 06:25:42 pm
content: HiI'm the author of the keypad library.If you need delays then the way to go is with hardware and an interrupt.You probably do not need delays though. Why do you need them?

author: stevelup
author_rank: Regular Contributor
timestamp: May 25, 2017, 06:27:03 am
content: I was about to say the same. Try and get rid of the delays - they are almost always a bodge.

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: June 12, 2017, 01:29:26 pm
content: Solved this by using a Timer interrupt of 20ms & in it used state machines to scan keyboard.Code is working fine for 4x4 keyboard

author: alexanderbrevig
author_rank: Frequent Contributor
timestamp: June 12, 2017, 01:33:38 pm
content: Quote from: Vindhyachal.takniki on June 12, 2017, 01:29:26 pmSounds like a perfectly good way to go about it.Beware if you do too much in the actual ISR things might start behaving weird. That is, if you use the pinMode and digitalRead functions. If you use the registers you should be fine.

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: June 12, 2017, 01:57:28 pm
content: In ISR, I am using digitalread & digitalwrite onlyPins are defined as constants so I think digitalread & digitalWrite takes less time when pins are constant defined.I am using arduino mega.I have used micros function on beginning & end of isr. Worst time in ISR I get is 196.

author: Rerouter
author_rank: Super Contributor
timestamp: June 16, 2017, 10:42:30 pm
content: If you want faster, you can do your reads and writes in an 8 bit chunk, providing they are all connected to the same port "PBXX" on the micro.I'm going to assume you leave all 4 outputs low to begin with to allow the pullups of the inputs to trigger it,So lets say first 4 pins of the port are outputs, and last 4 are your inputsThe below code would be inside your pin change interrupt.To start our sequence we would write 1 of the outputs high, we still want those pullups, so we will write 1 for any inputs (writing a 0 to the port of an input turns off its pullup)Lets say i am using port B, pins 22-29I will have 4 variables called "Read1" to "Read4"PORTB = 11101111b; // Sets only Output 1 LowRead1 = PINB & 0xF; // Reads in the input statePORTB = 11011111b; // Sets only Output 2 LowRead2 = PINB & 0xF; // Reads in the input statePORTB = 10111111b; // Sets only Output 3 LowRead3 = PINB & 0xF; // Reads in the input statePORTB = 01111111b; // Sets only Output 4 LowRead4 = PINB & 0xF; // Reads in the input statePORTB = 00001111b; // Sets it back to how it began.You can then use the results of read1-read 4 to map your buttons being pressed, any time you find a 0, that's a pressed button

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: June 19, 2017, 05:09:51 am
content: below is the code, max time I found was 160us, when key in postion 4,4 is pressed.Time is measured from start of isr to end of isr as u can see in code.Code:#include "TimerOne.h"const uint8_t no_key = 0U;const uint8_t keys_val = {{'1','2','3','A'},{'4','5','6','B'},{'7','8','9','C'},{'*','0','#','D'}};volatile uint8_t key_main_access;volatile uint8_t key_state;volatile uint8_t old_col;volatile uint8_t new_col;volatile uint8_t new_row;volatile uint8_t main_read;volatile uint8_t main_interrupted;volatile uint8_t key1;volatile uint8_t key2;const int row_1 = 33;const int row_2 = 35;const int row_3 = 37;const int row_4 = 39;const int col_1 = 41;const int col_2 = 43;const int col_3 = 45;const int col_4 = 47;volatile uint32_t old_time, new_time, diff = 0U;void setup(){Serial.begin(9600);/* init all vars for keypad in default state */init_all_vars__key_c();/* 20ms timer interrupt */Timer1.initialize(20000);Timer1.attachInterrupt(isr_key_callback);/* all colums inout high */pinMode(col_1, INPUT_PULLUP);pinMode(col_2, INPUT_PULLUP);pinMode(col_3, INPUT_PULLUP);pinMode(col_4, INPUT_PULLUP);/* all rows output HIGH */pinMode(row_1, OUTPUT);pinMode(row_2, OUTPUT);pinMode(row_3, OUTPUT);pinMode(row_4, OUTPUT);digitalWrite(row_1, HIGH);digitalWrite(row_2, HIGH);digitalWrite(row_3, HIGH);digitalWrite(row_4, HIGH);}void isr_key_callback(){//old_time = micros();uint8_t check;if(0U == key_main_access) /* if main is not accessing keypad variable, will happen if main wants to reset keypad varaibles */{if(0U == key_state) /* ground all rows & check for any col low */{digitalWrite(row_1, LOW); digitalWrite(row_2, LOW); digitalWrite(row_3, LOW); digitalWrite(row_4, LOW);if(!digitalRead(col_1)) /* if found store the col & move to next state */{old_col = 1U;key_state = 1U;}else if(!digitalRead(col_2)){old_col = 2U;key_state = 1U;}else if(!digitalRead(col_3)){old_col = 3U;key_state = 1U;}else if(!digitalRead(col_4)){old_col = 4U;key_state = 1U;}else{}}else if(1U == key_state) /* ground one row at a time & check for col low */{check = 0U; /* assume row 1 is found to be low */digitalWrite(row_1, LOW); digitalWrite(row_2, HIGH); digitalWrite(row_3, HIGH); digitalWrite(row_4, HIGH);if(!digitalRead(col_1)) /* if found store the col & row */{new_col = 1U;new_row = 1U;}else if(!digitalRead(col_2)){new_col = 2U;new_row = 1U;}else if(!digitalRead(col_3)){new_col = 3U;new_row = 1U;}else if(!digitalRead(col_4)){new_col = 4U;new_row = 1U;}else{check = 1U; /* initial assumption that this row found to be false, so now check next row */}if(1U == check) /* repeat same as row 1 */{check = 0U;digitalWrite(row_1, HIGH); digitalWrite(row_2, LOW); digitalWrite(row_3, HIGH); digitalWrite(row_4, HIGH);if(!digitalRead(col_1)){new_col = 1U;new_row = 2U;}else if(!digitalRead(col_2)){new_col = 2U;new_row = 2U;}else if(!digitalRead(col_3)){new_col = 3U;new_row = 2U;}else if(!digitalRead(col_4)){new_col = 4U;new_row = 2U;}else{check = 1U;}}if(1U == check) /* repeat same as row 1 */{check = 0U;digitalWrite(row_1, HIGH); digitalWrite(row_2, HIGH); digitalWrite(row_3, LOW); digitalWrite(row_4, HIGH);if(!digitalRead(col_1)){new_col = 1U;new_row = 3U;}else if(!digitalRead(col_2)){new_col = 2U;new_row = 3U;}else if(!digitalRead(col_3)){new_col = 3U;new_row = 3U;}else if(!digitalRead(col_4)){new_col = 4U;new_row = 3U;}else{check = 1U;}}if(1U == check) /* repeat same as row 1 */{check = 0U;digitalWrite(row_1, HIGH); digitalWrite(row_2, HIGH); digitalWrite(row_3, HIGH); digitalWrite(row_4, LOW);if(!digitalRead(col_1)){new_col = 1U;new_row = 4U;}else if(!digitalRead(col_2)){new_col = 2U;new_row = 4U;}else if(!digitalRead(col_3)){new_col = 3U;new_row = 4U;}else if(!digitalRead(col_4)){new_col = 4U;new_row = 4U;}else{check = 1U;}}if(0U == check) /* a row low has been found */{if(new_col == old_col) /* if new column & old colum has been equal */{if((new_col >= 1U) && (new_col <= 4) && (new_row >= 1) && (new_row <= 4)) /* if new col & new row are within array boundaries */{if(0U == main_read) /* if main is not reading the key state */{key1 = keys_val[new_row-1U][new_col-1U]; /* store the key value */}else{key2 = keys_val[new_row-1U][new_col-1U]; /* mainline got interrupted */main_interrupted = 1U;}key_state = 2U; /* key found successfully, now move to next state */}else{key_state = 0U; /* if new col & new row are not within array boundaries, something wrong, move to default state */}}else /* if new col & old col are not same, something wrong, move to default state */{key_state = 0U;}}else /* if no row low has been found, something wrong,move to default state */{key_state = 0U;}}else if(2U == key_state) /* wait for all keys to open again, by grounding all rows, & wait for col to be high */{digitalWrite(row_1, LOW); digitalWrite(row_2, LOW); digitalWrite(row_3, LOW); digitalWrite(row_4, LOW);if(digitalRead(col_1) && digitalRead(col_2) && digitalRead(col_3) && digitalRead(col_4)){key_state = 0U; /* all col found high, move to default state */}}else{}}/*new_time = micros();if(diff < (new_time-old_time)){diff = new_time-old_time;Serial.println(diff);}*/}void loop(){uint8_t temp;while(1){temp = key_read();if(no_key != temp){Serial.print("KEY: ");Serial.println((char)temp);}}}uint8_t key_read(void){uint8_t temp;main_read = 1U; /* set var for main line read & read the key value */temp = key1;key1 = no_key;main_read = 0U;if(1U == main_interrupted) /* if main line gets interupted, again read the key var */{temp = key2;key2 = no_key;main_interrupted = 0U;}return temp;}void clear_key_vars(void){key_main_access = 1U; /* clear all previous key varaibles & start aagin */key_state = 0U;old_col = 1U;new_col = 1U;new_row = 1U;main_read = 0U;main_interrupted = 0U;key1 = no_key;key2 = no_key;key_main_access = 0U;}void init_all_vars__key_c(void){key_main_access = 0U; /* init ll vars to default state */key_state = 0U;old_col = 1U;new_col = 1U;new_row = 1U;main_read = 0U;main_interrupted = 0U;key1 = no_key;key2 = no_key;}

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: June 30, 2017, 11:14:13 am
content: Algorithm for Alphanumeric keypad1. My above posted code is working fine. This works fine if every key has one number attached to it.2. Now I have to convert it to alphanumeric keypad as in earlier mobile phones. Each key will have 4 fuctions like:(2,A,B,C) for one key.3. What would be change in algo for this.4. What I am thinking is as soon as key is detected in key_state = 1 in above code, I start a counter in this timer,if timer counts to greater than 1seconds, then key 2if same key is pressed again in 0.5 seconds, then shift to Aif same key is pressed again in 0.5 seconds, then shift to Bif same key is pressed again in 0.5 seconds, then shift to Cif same key is pressed again in 0.5 seconds, then shift to 0 againuntil same key pressed passes 1 secondsI am writing this code, was also looking for some reference codes

author: Vindhyachal.takniki
author_rank: Frequent Contributor
timestamp: July 03, 2017, 06:15:56 am
content: any response...any already tried c code on this?

author: Jeroen3
author_rank: Super Contributor
timestamp: July 03, 2017, 06:26:57 am
content: You use a dead time of 0.5 s before flagging the key as pressed. Within this dead time you allow for another keypress. If it happens, you can read double, triple, quadruple or n-press.I made something like it once, this was the only part of the project to be working... time shortagehttps://github.com/Jeroen6/LED-Rotary-Dimmer-SW/blob/master/user/button.cHumans are slow, I noticed that 300 ms didn't have a "slow" effect on single presses, while still easy to do multiple presses.But 500 ms might give you a delayed effect on single presses.You can also solve this one layer up, so you can actually display the character selected. I recall old phones having about 1 second delay before the character was submitted.