View previous topic :: View next topic |
Author |
Message |
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
Is this keypad matrix debouncing OK? |
Posted: Tue May 30, 2006 3:28 am |
|
|
Hello,
here you have a view of the keypad matrix:
[img]http://img50.imageshack.us/my.php?image=keypadmatrix8mn.jpg[/img]
http://img50.imageshack.us/my.php?image=keypadmatrix8mn.jpg
KO.. to K7 are the inputs or datalines
SCAN0... SCAN8 are the outputs or scanlines.
This is the routine to read in the matrix:
Code: | //***************************** KEYBOARD SCAN ********************************//
// 1 = niets ingedrukt, 0 = ingedrukt
if (ENABLE_Keypad_Scan)
{
Alles_los = TRUE;
Pressed = FALSE;
// RIJ 1
// RIJ 2
Output_low(R2);
delay_us(100);
temp = input_d();
Output_float(R2);
if ((buffer[1] & temp) != buffer[1])
Pressed = TRUE;
buffer[1] = buffer [1] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 3
Output_low(R3);
delay_us(100);
temp = input_d();
Output_float(R3);
if ((buffer[2] & temp) != buffer[2])
Pressed = TRUE;
buffer[2] = buffer [2] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 4
Output_low(R4);
delay_us(100);
temp = input_d();
Output_float(R4);
if ((buffer[3] & temp) != buffer[3])
Pressed = TRUE;
buffer[3] = buffer [3] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 5
Output_low(R5);
delay_us(100);
temp = input_d();
Output_float(R5);
if ((buffer[4] & temp) != buffer[4])
Pressed = TRUE;
buffer[4] = buffer [4] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 6
Output_low(R6);
delay_us(100);
temp = input_d();
Output_float(R6);
if ((buffer[5] & temp) != buffer[5])
Pressed = TRUE;
buffer[5] = buffer [5] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 7
Output_low(R7);
delay_us(100);
temp = input_d();
Output_float(R7);
if ((buffer[6] & temp) != buffer[6])
Pressed = TRUE;
buffer[6] = buffer [6] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 8
Output_low(R8);
delay_us(100);
temp = input_d();
Output_float(R8);
if ((buffer[7] & temp) != buffer[7])
Pressed = TRUE;
buffer[7] = buffer [7] & temp;
if (temp != 255)
Alles_los = FALSE;
// RIJ 9
Output_low(R9);
delay_us(100);
temp = input_d();
Output_float(R9);
if ((buffer[8] & temp) != buffer[8])
Pressed = TRUE;
buffer[8] = buffer [8] & temp;
if (temp != 255)
Alles_los = FALSE;
Output_low(R1);
delay_us(100);
temp = input_d(); // Lees de waarde van de poort
Output_float(R1);
if ((buffer[0] & temp) != buffer[0]) // er is gedrukt
Pressed = TRUE;
buffer[0] = buffer [0] & temp; // Onthoud dat er ingedrukt is
if (temp != 255)
Alles_los = FALSE;
if (Pressed)
{
if (!E3_SLEEP)
{
if (KEYCLICK)
CLICK();
Printf("%c%c%S%c",0, COMMANDO_BYTE,DRUK_COMMANDO,0);
}
}// Pressed
// Er is een toetsencombinatie ingedrukt:
if ((Alles_los) && (
(buffer[0] != 0xFF) ||
(buffer[1] != 0xFF) ||
(buffer[2] != 0xFF) ||
(buffer[3] != 0xFF) ||
(buffer[4] != 0xFF) ||
(buffer[5] != 0xFF) ||
(buffer[6] != 0xFF) ||
(buffer[7] != 0xFF) ||
(buffer[8] != 0xFF)
)
)
{
ENABLE_Keypad_Scan = FALSE;
KEYPAD_Debounce_Counter = 3;
key = GetKeyToSend(); // Decodeer toetsencombinatie
for ( i = 0 ; i < 9 ; i++)
{
buffer[i] = 0xFF;
}
} // if losgelaten
} // if enable keypad_scan = TRUE |
This is what happens; when all key (combinations) are released,
* the scanning routing get's disabled: ENABLE_Keypad_Scan = FALSE;
* A Timer of 30ms gets started: KEYPAD_Debounce_Counter = 3;
* The pressed combination is decoded to an int16 key
Code: | //************************** Timer2 interrupt ********************************//
//Timer0 overloopt elke 10 ms. (100Hz) Gebruikt om 1. minutenteller 2. off timer
//3. Debounce timer
#int_Timer2
Timer2_isr()
{
static int8 Count = 100;
// OFF Timer start te tellen indien TRUE
// some other code
// KEYPAD Debouncing
if (KEYPAD_Debounce_Counter == 0)
{
KEYPAD_Debounced = TRUE;
KEYPAD_Debounce_Counter = 255;
}
else if (KEYPAD_Debounce_Counter < 255)
{
--KEYPAD_Debounce_Counter;
}
}// TIMER2 |
After 30mS; KEYPAD_Debounced = TRUE.
Handler in main():
Code: | //****************************** KEYPAD DEBOUNCE *****************************//
if (KEYPAD_Debounced)
{
KEYPAD_Debounced = FALSE;
if (!E3_SLEEP)
{
Printf("%c%c%S%c%c%c",0 ,COMMANDO_BYTE,TOETS_COMMANDO,2 ,make8(key,1),make8(key,0));
}
ENABLE_Keypad_Scan = TRUE;
} |
Send code (int16 key) and re-enable the matrix scanning routine.
Is this a good practice for matrix keypad debouncing? What can be improved? What can be made faster?
regards + thx for reviewing |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Tue May 30, 2006 12:44 pm |
|
|
I would generally *not* recommend using delay routines in code that is used quite often. A keyboard scanning routine would usually be called several times a second to make sure all keypresses a detected.
You could use a timer and state machine to scan through each line in turn, take care of debouncing, etc.
A quick and dirty state machine might run at 50Hz include the following states ...
Code: | enum {
IDLE,
OUTPUT_LINE,
DELAY,
INPUT_DATA,
KEY_RELEASE
}; |
IDLE = state machine is idle !!
OUTPUT_LINE = output next scan line
DELAY = delay to sort out key debounce
INPUT_DATA = read data lines, check for valid key
KEY_RELEASE = wait here until key is released
At each state, the timer counts can be adjusted to vary the time till the next state occurs. |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed May 31, 2006 12:48 am |
|
|
mpfj,
I am using a timer.. and not delay_ms(x) which is a bad practice.
rgds |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 31, 2006 12:54 am |
|
|
Christophe wrote: | mpfj,
I am using a timer.. and not delay_ms(x) which is a bad practice. | Instead you are calling 9 * delay_us(100), not good practice either... |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed May 31, 2006 1:06 am |
|
|
You are right,
but that is necessary for hardware reasons. Our keypads are PCB footprints of small tracks that run close to each other. A carbon pillar makes the connection.
The result of this is that the pad acts as a capacitor which has charging time and decharging time. Without this delay we sometimes get wrong readings caused by capacitor hasn't charged to 3V yet.
If anyone has a software solution, feel free to give some tips. |
|
|
mpfj
Joined: 09 Sep 2003 Posts: 95 Location: UK
|
|
Posted: Wed May 31, 2006 2:22 am |
|
|
Christophe wrote: | but that is necessary for hardware reasons. |
You could account for this in the state machine (e.g. adding a "CHARGING" state).
Any use of delays just ties up the CPU and stops it from handling any other tasks / interrupts.
A 100us delay is 200 instructions @ 8MHz, or 1000 @ 40MHz. This may be acceptable, but it's worth considering. |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed May 31, 2006 2:33 am |
|
|
True,
I could use Timer0 that overflows every 100uS and read a SCAN - line there. In main I can check for 'All keys released'.
The crystal freq is 4Mhz (PIC16LF877A). |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 31, 2006 4:07 am |
|
|
At 4MHz you are executing 100 instructions in 100us.
The interrupt overhead for saving and restoring registers in a PIC16 is about 45 instructions.
In this particular case the extra effort and complicating the code perhaps doesn't outweigh the cost of implementing interrupts. All depends on the other tasks your software has to perform, for example serial communication at 9600baud receives 1 character in about the same time as your scanning routine. |
|
|
Christophe
Joined: 10 May 2005 Posts: 323 Location: Belgium
|
|
Posted: Wed May 31, 2006 4:49 am |
|
|
Yes,
but the interrupt handler only takes 3 instructions:
Code: | //***************************** RDA interrupt ********************************//
//Als een karakter wordt ontvangen door de UART; inlezen en opslaan.
#int_RDA
RDA_isr()
{
data [Read_Pointer] = getc();
Read_Pointer = ( Read_Pointer + 1 ) % BUFFER_SIZE;
Byte_Received = TRUE;
}//RDA ISR |
So can you say that you stay 100uS inside this function? |
|
|
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Wed May 31, 2006 6:07 am |
|
|
Quote: | but the interrupt handler only takes 3 instructions: | 3 lines of C code is not the same as 3 processor instructions. One of the reasons why people love to program in higher level languages like C is that you can write compact code and have the compiler expand this to the most optimal assembly language.
Besides the code in your interrupt routine there is also some code added by the compiler for saving and restoring any registers that you might change in the interrupt routine. This overhead depends on the processor family you are using and the number of active interrupts; for a PIC16 the overhead is about 45 instructions and for a PIC18 (with more registers) about 80. |
|
|
|