3x4 Matrix keypad Routine

Go To Last Post
15 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all,

I would like to know if my code for 3x4 matrix keypad is good?! or is this too crude?
I am setting first the rows high and cols low, then rows low and cols high.

Working wise it is Good, and I get what I want on the LCD or serial, decimal or asci no problem.

pls let me know how to write a better routine for keypad. I feel its too crude.

//--------------keypad-------------//
// Using PORTA0 to PORTA6 for Keypad interface
// PORTA0.3 for Rows
// PORTA4.6  for columns

char keypad(void)
{
 char data=0;
 int col_flag= 0,row_flag=0;
	
	
PORTA = 0xf0;
DDRA =  0x0f;
	timer1_delay(1); // 1msec delay

if(!(PINA &(1<<PA4)))
	{ 
  	col_flag = 1;
  	}
    
if(!(PINA & (1<<PA5)))
	{
   	col_flag = 2;
  	}

if(!(PINA & (1<<PA6)))
 	{
	col_flag = 3;
 	}

  
  PORTA = 0x0f;
  DDRA = 0xf0;	
 

if(!(PINA &(1<<PA0)))
	{ 
  	row_flag = 1;
   	}
if(!(PINA & (1<<PA1)))
	{
  	row_flag = 2;
  	}
if(!(PINA & (1<<PA2)))
	{
  	row_flag = 3;
 	}
if(!(PINA &(1<<PA3)))
	{
    row_flag = 4;
  	}


if ((row_flag == 1) && (col_flag == 1))
	{
    data = '1'; //  display 1
	KeyValue =1;	
    }
if ((row_flag == 1) && (col_flag == 2))
    {
    data = '2';
	KeyValue =2;	
	}
if ((row_flag == 1) && (col_flag == 3))
   {
    data = '3'; 
	KeyValue =3;	
    }

if ((row_flag == 2) && (col_flag == 1))
    {
	data = '4';// display 4
	KeyValue =4;	
    }
    
if ((row_flag == 2) && (col_flag == 2))
    {
	data = '5';// display 5
	KeyValue =5;	
	}
    
if ((row_flag == 2) && (col_flag == 3))
    {
	data = '6';// display 6
	KeyValue =6;	
	}
	
if ((row_flag == 3) && (col_flag == 1))
	{
	data = '7';// display 7
	KeyValue =7;	
	}
	 
if ((row_flag == 3) && (col_flag == 2))
    {
	data = '8';// display 8
	KeyValue =8;	
	}
	 
if ((row_flag == 3) && (col_flag == 3))
    {
	data = '9';// display 9
	KeyValue =9;	
	}

if ((row_flag == 4) && (col_flag == 1))
    {
	data = '*';// display *
	KeyValue =10;
	}

if ((row_flag == 4) && (col_flag == 2))
	{
	data = '0';// display 0
	KeyValue =0;	
	}

if ((row_flag == 4) && (col_flag == 3))
	{
	data = '#';  //display #
	KeyValue =11;
	}


if((row_flag ==0) && (col_flag ==0))			
	{
	Nil =0;	   // IF no key is pressed i
                   // want to use this variable
                   // so I can wait (global variable)
 	}
else
  Nil =1;   // IF any key is pressed i want
            //to use this variable to wait for
            //  key release (global variable)

 return data;
}

regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's quite tricky to follow your structure but it doesn't appear to be right to me. The usual method of scanning a matrix (assuming internal pull-ups are used on the inputs) is to walk a 0 across the output bits and in each case read the inputs and see if any have now connected to 0. The intercept of the output and the input tell you which junction in the matrix is active.

BTW whether you call the outputs "rows", inputs "cols" or outputs "cols", inputs "rows" is incidental.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I suspect that you could have problems with contact bounce especially when the keypad starts to wear a bit. I had that problem many years ago with a product, where I had to do a software release to provide contact bounce.

The other thing that I would do, is leave a few microseconds after you change the directions of the DDR and allow the DC conditions to settle.

With a keypad you can really afford to have quite a few milliseconds of contact debounce as it is hard for a user to press more then about 3-4 key stokes/second.

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gtpats wrote:
pls let me know how to write a better routine for keypad. I feel its too crude.

The scanning method was correct.
But it need a delay to make the inputs valid.
The converting to a number can be improved:

#include 

#define NOP();  asm volatile("nop"::);


#define KEY_PIN         PINB
#define KEY_PORT        PORTB
#define KEY_DDR         DDRB

// Pin 0..3 = column 1..4
// Pin 4..6 = row 1..3


uint8_t keyscan( void )
{
  uint8_t col = 0, row = 0;

  KEY_PORT |= 0x7F;                     // all pullups on
  KEY_DDR = (KEY_DDR & 0x80) | 0x70;    // pin 6..4 = output
  KEY_PORT &= 0x8F;                     // pin 6..4 = output low
  NOP();                                // wait until inputs sampled
  if( ~KEY_PIN & 1<<0 )                 // if pin 0 = low
    col = 4;
  if( ~KEY_PIN & 1<<1 )
    col = 3;
  if( ~KEY_PIN & 1<<2 )
    col = 2;
  if( ~KEY_PIN & 1<<3 )
    col = 1;
  row = col;
  if( col ){                            // if column found, check row
    KEY_PORT |= 0x7F;                   // all pullups on
    KEY_DDR = (KEY_DDR & 0x80) | 0x0F;  // pin 3..0 = output
    KEY_PORT &= 0xF0;                   // pin 3..0 = output low
    NOP();                              // wait until inputs sampled
    if( ~KEY_PIN & 1<<4 )
      row = 5;
    if( ~KEY_PIN & 1<<5 )
      row = 9;
    if( ~KEY_PIN & 1<<6 )
      row = 13;
  }                                     // 0 = no key
  return row - col;                     // 1..12 = key
}

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There are quite a few ways to skin a cat when it comes to keypads. As an example I have just found one that I wrote which is a 4X4 routine, which can be used to enter data or execute commands. It is a bit different because it is state machine driven. It is fully debounced, does error handling and can be re-configured quite easily.

Attachment(s): 

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I always implement an 'anykey pressed' routine that turns on all three columns at the same time, reads the row inputs... if any of them are low, something is pressed, return true.
You can call if(anykey()) c=getkey() once per pass. I hate to make trouble, but I think if keypad reads are 10ms apart, and keybounce is 2-4ms, there isn't a bounce problem. If its not pressed this pass, and you press it, and it reads open on a bounce, there is no change, you get the right reading next pass (10ms later).

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Naturally beside the scan routine you need to debounce the reading:

uint8_t key_debounce( void )            // should be called every 5 .. 50ms
{
  static uint8_t oldkey, debounce;
  uint8_t key = keyscan();

  if( oldkey != key ){
    oldkey = key;
    debounce = 4;
  }
  if( debounce && --debounce == 0 )
    return key;
  return 0;
}

A count of 4 consecutive equal readings should be sufficient
The calling interval was not critical, e.g. 5 .. 50ms works nice.

Peter

Last Edited: Fri. May 7, 2010 - 07:55 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

LDEVRIES wrote:
It is a bit different because it is state machine driven.

I like it, if I have separate routines for different tasks.
Thus I not merged main loop and key reading together.
The main call only the reading routine.
I think, ist easier to understand, to maintenance and to reuse.

Divide and conquer :wink:

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Incidentally after covering keypad scans & state machines I used to give that code to students for their project. The last thing I wanted to see was 30-40 students all doing their own "keypad thing" and then have to help them out, because in the end they would all finish up with the same code anyway. I was more interested in the major part of their project.

Quote:
I like it, if I have separate routines for different tasks.
Thus I not merged main loop and key reading together.
The main call only the reading routine.

I am not sure if I fully understand your comments but I will summarize it this way.
a) the keypad routine and other non-keypad routines are called from main.
b) the keypad in main is run under control of a timer to make it regular.
c) some "key" initiated tasks can be run from the keypad code when required. START, STOP, LEFT,RIGHT etc. while the other keys enter data.
d) other tasks from main can be broken up and run as state machines just as the keypad is.
e) interrupt routines should be as short as possible
f) try to avoid delay routines altogether.

One of the key functions (F) could be a "function key" which selects another switch statement where you could now have 15 other functions. F key remains a function shift key/un-shift key. By having multiple function levels you could have 100's of keypad initiated activities with only 4 X 4 keypad.

If you only need a 3 X 4 keypad, just don't wire up the fourth column. You can't use the unused column line however unless you re-hack the code.

An interesting option also is that you can have four momentary action front panel switches wired into the 3 X 4 matrix. Ie. UP, DOWN,START,STOP, FUNCTION etc.

Have fun!

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all so much,,,
Yes, i think a small delay is must after the ports are reversed.

Now I have an idea how to better this routine. I feel keypad is always a speed breaker for the microcontroller. takes so much of time while waiting for key press and key release.

I guess its best to use a keypad always as an interrupt. Though I have never used it that way until now.

Could anyone pls let me know if...

Quote:
Are there any disadvantages in using a matrix keypad in interrupt mode in medium to complex applications?

hoping to hear from you.

regards

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Medium & complex are relative terms. In a complex system, I would use nothing else but a state machine driven keypad such as I have described.
Incidentally, the keypad code is not interrupt driven, but rather interrupt syncopated.

Charles Darwin, Lord Kelvin & Murphy are always lurking about!
Lee -.-
Riddle me this...How did the serpent move around before the fall?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

LDEVRIES wrote:
a) the keypad routine and other non-keypad routines are called from main.

Yes, thats always my approach.

But on your example the keypad code was interwoven with the main code.
This make it hard to understand your code.

Thus I separate the keypad code completely and the main get only the return value to handle it.

This make it easy to change the key input code, e.g. to a remote control.
Also then the keypad code can easy be modified to get further events, if wanted, e.g. key release, a second key, auto repeat, long press and so on.

So with a separate function I have more flexibility.
And changing this code would never harm the main code.

Thus in general I avoid writing interwoven code.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gtpats wrote:
I feel keypad is always a speed breaker for the microcontroller.

No, it isn't. :!:

A well written keypad routine takes only far below 1% of CPU time.

gtpats wrote:
takes so much of time while waiting for key press and key release.

That's the wrong approach.
You should never wait inside the main, never, under no circumstances. :!:

The main calls periodically the keypad routine.
If the return value was zero, the main does nothing and continue with other tasks.
Only if the return value was non zero, the main get it and handle it.

Peter

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Use a timer driven interrupt to generate the poll interval for the keypad. The ISR won't take long to service, and it should run about once every 10-50ms depending on how many row/col combos you have. I would have the routine switch the scan output at the END of the routine and check the current line at the begining of the routine so you have lots of time for the line to settle. You don't switch lines when a key is found down, and you debounce by requiring a key to be down for at least two or three scan periods. You also need to debounce key up's too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you all so very much,
Yes, I too, never wait in the main loop, i wait in the function of the keypad.
now i am moving into writing a more refined code rather than just a code!
:)
thnx