CCS C Software and Maintenance Offers
FAQFAQ   FAQForum Help   FAQOfficial CCS Support   SearchSearch  RegisterRegister 

ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

CCS does not monitor this forum on a regular basis.

Please do not post bug reports on this forum. Send them to CCS Technical Support

Is this keypad matrix debouncing OK?

 
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion
View previous topic :: View next topic  
Author Message
Christophe



Joined: 10 May 2005
Posts: 323
Location: Belgium

View user's profile Send private message

Is this keypad matrix debouncing OK?
PostPosted: Tue May 30, 2006 3:28 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Tue May 30, 2006 12:44 pm     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 12:48 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 12:54 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 1:06 am     Reply with quote

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

View user's profile Send private message Send e-mail Visit poster's website

PostPosted: Wed May 31, 2006 2:22 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 2:33 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 4:07 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 4:49 am     Reply with quote

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

View user's profile Send private message

PostPosted: Wed May 31, 2006 6:07 am     Reply with quote

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.
Display posts from previous:   
Post new topic   Reply to topic    CCS Forum Index -> General CCS C Discussion All times are GMT - 6 Hours
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group