Author Topic: 4x4 keypad arduino non blocking  (Read 4008 times)

0 Members and 1 Guest are viewing this topic.

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
4x4 keypad arduino non blocking
« on: May 24, 2017, 12:54:06 pm »
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/KeypadTutorial

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

Offline dferyance

  • Regular Contributor
  • *
  • Posts: 130
Re: 4x4 keypad arduino non blocking
« Reply #1 on: May 24, 2017, 05:00:03 pm »
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.
 

Offline mariush

  • Super Contributor
  • ***
  • Posts: 3979
  • Country: ro
  • .
Re: 4x4 keypad arduino non blocking
« Reply #2 on: May 24, 2017, 05:22:08 pm »
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.pdf

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

Offline FrankBuss

  • Supporter
  • ****
  • Posts: 2321
  • Country: de
    • Frank Buss
Re: 4x4 keypad arduino non blocking
« Reply #3 on: May 24, 2017, 05:39:40 pm »
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.

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

Quote
If 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

Or 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.
So Long, and Thanks for All the Fish
Electronics, hiking, retro-computing, electronic music etc.: https://www.youtube.com/c/FrankBussProgrammer
 

Offline alexanderbrevig

  • Frequent Contributor
  • **
  • Posts: 590
  • Country: no
  • Musician, programmer and EE hobbyist
    • alexanderbrevig.com
Re: 4x4 keypad arduino non blocking
« Reply #4 on: May 24, 2017, 06:25:42 pm »
Hi :) I'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?

Offline stevelup

  • Regular Contributor
  • *
  • Posts: 156
  • Country: gb
Re: 4x4 keypad arduino non blocking
« Reply #5 on: May 25, 2017, 06:27:03 am »
I was about to say the same. Try and get rid of the delays - they are almost always a bodge.
 

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
Re: 4x4 keypad arduino non blocking
« Reply #6 on: June 12, 2017, 01:29:26 pm »
Solved this by using a Timer interrupt of 20ms & in it used state machines to scan keyboard.
Code is working fine for 4x4 keyboard
 

Offline alexanderbrevig

  • Frequent Contributor
  • **
  • Posts: 590
  • Country: no
  • Musician, programmer and EE hobbyist
    • alexanderbrevig.com
Re: 4x4 keypad arduino non blocking
« Reply #7 on: June 12, 2017, 01:33:38 pm »
Solved this by using a Timer interrupt of 20ms & in it used state machines to scan keyboard.
Code is working fine for 4x4 keyboard

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

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
Re: 4x4 keypad arduino non blocking
« Reply #8 on: June 12, 2017, 01:57:28 pm »
In ISR, I am using digitalread & digitalwrite only
Pins 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.
 

Online Rerouter

  • Super Contributor
  • ***
  • Posts: 4643
  • Country: au
  • Question Everything... Except This Statement
Re: 4x4 keypad arduino non blocking
« Reply #9 on: June 16, 2017, 10:42:30 pm »
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 inputs

The 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-29
I will have 4 variables called "Read1" to "Read4"

PORTB = 11101111b; // Sets only Output 1 Low
Read1 = PINB & 0xF; // Reads in the input state
PORTB = 11011111b; // Sets only Output 2 Low
Read2 = PINB & 0xF; // Reads in the input state
PORTB = 10111111b; // Sets only Output 3 Low
Read3 = PINB & 0xF; // Reads in the input state
PORTB = 01111111b; // Sets only Output 4 Low
Read4 = PINB & 0xF; // Reads in the input state

PORTB = 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
 

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
Re: 4x4 keypad arduino non blocking
« Reply #10 on: June 19, 2017, 05:09:51 am »
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: [Select]

#include "TimerOne.h"

const uint8_t no_key = 0U;

const uint8_t keys_val[4][4] = {
  {'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;
}


 

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
Re: 4x4 keypad arduino non blocking
« Reply #11 on: June 30, 2017, 11:14:13 am »
Algorithm for Alphanumeric keypad

1. 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 2
if same key is pressed again in 0.5 seconds, then shift to A
if same key is pressed again in 0.5 seconds, then shift to B
if same key is pressed again in 0.5 seconds, then shift to C
if same key is pressed again in 0.5 seconds, then shift to 0 again
until same key pressed passes 1 seconds


I am writing this code, was also looking for some reference codes
 

Offline Vindhyachal.takniki

  • Frequent Contributor
  • **
  • Posts: 445
Re: 4x4 keypad arduino non blocking
« Reply #12 on: July 03, 2017, 06:15:56 am »
any response...
any already tried c code on this?
 

Online Jeroen3

  • Super Contributor
  • ***
  • Posts: 3567
  • Country: nl
  • Embedded Engineer
    • jeroen3.nl
Re: 4x4 keypad arduino non blocking
« Reply #13 on: July 03, 2017, 06:26:57 am »
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 shortage  :--
https://github.com/Jeroen6/LED-Rotary-Dimmer-SW/blob/master/user/button.c

Humans 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.
« Last Edit: July 03, 2017, 06:30:09 am by Jeroen3 »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf