Help with Debouncing my Matrix Switch

Discussion in 'Embedded Systems and Microcontrollers' started by beeson76, Nov 22, 2010.

  1. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Here is my code:

    Code ( (Unknown Language)):
    1.  
    2.  
    3. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    4. //This Pendant has 10 buttons using a 3 Column 4 Row
    5. //Matrix Switch.  
    6. //
    7. //Whenever a switch is pushed, that switches name is displayed on an LCD Display.
    8. //Here are the names of the Buttons:
    9. //Switch 1      --  Column 1 Row 4
    10. //Switch 2  --  Column 2 Row 4
    11. //Switch 3  --  Column 3 Row 4
    12. //Switch 4      --  Column 1 Row 3
    13. //Switch 5      --  Column 2 Row 3
    14. //Switch 6  --  Column 3 Row 3
    15. //Swtich 7      --  Column 1 Row 2
    16. //Switch 8      --  Column 2 Row 2
    17. //Switch 9      --  Column 3 Row 2
    18. //Swtich 10     --  Column 1 Row 1
    19. //Switch 11     --  Column 2 Row 1
    20. //Switch 12     --  Column 3 Row 1
    21. //
    22. //The PIC16F886 Microcontroller from Microchip is being used.
    23. //2x16 LCD Display being used
    24. //
    25. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    26.  
    27. #include
    28. #include "lcd.h"
    29.  
    30. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    31. ////DEFINE STATEMENTS
    32. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    33. #define COL_1           RB0
    34. #define COL_2           RB1
    35. #define COL_3           RB7
    36. #define ROW_1           RB3
    37. #define ROW_2           RB4
    38. #define ROW_3           RB5
    39. #define ROW_4           RB6
    40. #define  DelayS(T)      {unsigned char i; for (i = 0; i < T * 10; i++) __delay_ms(100);}    //Delay Macro
    41. #define  _XTAL_FREQ             4000000                             //Needs to be set for __delay_ms
    42.  
    43. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    44.  
    45. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    46. ////CONFIGURATION FUSES (BITS)
    47. ////Master Clear Reset enabled & Internal RC No Clock & Watchdog Timer Disable & Power Up Timer On & Brown Out Reset Disabled &
    48. ////Low Voltage Porgramming Disabled & Code Unprotect
    49. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    50. __CONFIG (MCLREN & INTIO & WDTDIS & PWRTEN & BORDIS & LVPDIS & UNPROTECT);
    51. /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    52.  
    53.  
    54. main()
    55. {
    56.    
    57. PORTA = 0x00;                                       //PORTA is cleared and set low
    58. PORTB = 0xFF;                                       //(0b11111111 binary) (0xFF hex)--ROWS set to HIGH, COLUMNS set to HIGH  
    59.                                                     //PORTB7:B0 is set to high,high,high,high,high,high,high,high
    60.                                                    
    61. PORTC = 0x00;                                       //PORTC is cleared and set low
    62. TRISA = 0x00;                                       //Set PORTA to outputs for LCD--RA1, RA2, RA4 are Control lines for LCD
    63.  
    64. TRISB = 0x7C;                                       //(0b01111100 binary) (0x7C hex)--ROWS set to INPUT, COLUMNS set to OUTPUT
    65.                                                     //PORTB7:B0 is set to output,input,input,input,input,input,output,output,
    66.                                                     //ROWS set to INPUT
    67.  
    68. TRISC = 0x00;                                       //Set PORTC to outputs for LCD--RC0:RC3 are Data Lines for LCD
    69.  
    70.  
    71. ANSEL = 0;                                          //Initialize A/D Ports off
    72. ANSELH = 0;                                         //Initialize ........
    73. CM1CON0 = 0;                                        //Initialize Comparator 1 off
    74. CM2CON0 = 0;                                        //Initialize Comparator 2 off
    75.  
    76. OPTION = 0b01010101;                                //OPTION REG
    77.                                                     //xbxxxxx101  1:64
    78.                                                     //xbxxxx0xxx  Prescaler set to Timer0
    79.                                                     //xbxxx1xxxx  (T0SE) set to Increment on high-to-low transition on T0CKI pin
    80.                                                     //xbxx0xxxxx  (T0CS) Internal instruction cycle clock
    81.                                                     //xbx1xxxxxx  (INTEDG) Interrupt on rising edge of INT pin
    82.                                                     //xb0xxxxxxx  (RBPU) PORTB pull-ups are enabled by individual PORT latch values
    83. RBPU = 0;                                           //Don't think I need this, but to be safe...PORTB Weak Internal Pullups enabled
    84. WPUB0 = 0;                                          //COL_1 Weak pullup is individually DISABLED--OUTPUT
    85. WPUB1 = 0;                                          //COL_2 Weak Pullup is inidivdually DISABLED--OUTPUT
    86. WPUB2 = 0;                                          //Not being used...Weak Pullup is individually DISABLED
    87. WPUB3 = 1;                                          //ROW_1 Weak Pullup is individually ENABLED--INPUT
    88. WPUB4 = 1;                                          //ROW_2 Weak Pullup is individually ENABLED--INPUT
    89. WPUB5 = 1;                                          //ROW_3 Weak Pullup is individually ENABLED--INPUT
    90. WPUB6 = 1;                                          //ROW_4 Weak Pullup is individually ENABLED--INPUT
    91. WPUB7 = 0;                                          //COL_3 Weak Pullup is individually DISABLED--INPUT
    92.  
    93.  
    94. lcd_init();                                         //LCD Display is Initialized...See LCD.C file and LCD.h
    95.  
    96. unsigned char debouncecount;
    97.  
    98.  
    99.  
    100. while (1)
    101.     {
    102.         lcd_clear;                                  //Clear LCD
    103.         lcd_goto(0);                                //Go to Line 1 Position 1
    104.         lcd_puts("Matrix Switch");              //Display "Matrix Switch"
    105.         lcd_goto(40);                               //Go to Line 2 Position 1
    106.         lcd_puts("Press Button");                   //Display "Press Button"
    107.        
    108.         for (debouncecount = 0; debouncecount <= 10; debouncecount++);
    109.             {
    110.                 __delay_ms(1);
    111.                 COL_1 = 0;                                  //COLUMN 1 is set LOW
    112.                 if (ROW_1 == 0)                             //and If ROW 1 is LOW...
    113.                     {                                       //...
    114.                         lcd_clear();                        //...
    115.                         lcd_goto(0);                        //...
    116.                         lcd_puts("Switch 3");                   //Display "Flat"
    117.                         __delay_ms(150);
    118.                         __delay_ms(150);
    119.                         __delay_ms(150);
    120.                         __delay_ms(150);
    121.                         __delay_ms(150);
    122.                         __delay_ms(150);
    123.                         __delay_ms(100);
    124.                        
    125.                     }
    126.            
    127.                
    128.                 if (ROW_2 == 0)                             //if ROW 2 is LOW...
    129.                     {                                       //...
    130.                         lcd_clear();                        //...      
    131.                         lcd_goto(0);                        //...
    132.                         lcd_puts("Switch 6");                   //Display "Switch 6"
    133.                         __delay_ms(150);           
    134.                         __delay_ms(150);
    135.                         __delay_ms(150);
    136.                         __delay_ms(150);
    137.                         __delay_ms(150);
    138.                         __delay_ms(150);
    139.                         __delay_ms(100);
    140.                     }  
    141.                 if (ROW_3 == 0)                             //If ROW 3 is LOW
    142.                     {                                       //...
    143.                         lcd_clear();                        //...
    144.                         lcd_goto(0);                        //...
    145.                         lcd_puts("Switch 9");               //Display "Switch 9"
    146.                         __delay_ms(150);
    147.                         __delay_ms(150);
    148.                         __delay_ms(150);
    149.                         __delay_ms(150);
    150.                         __delay_ms(150);
    151.                         __delay_ms(150);
    152.                         __delay_ms(100);
    153.                     }
    154.                 if (ROW_4 == 0)                             //If ROW 4 is LOW
    155.                     {                                       //...
    156.                         lcd_clear();                        //...
    157.                         lcd_goto(0);                        //...
    158.                         lcd_puts("Switch 12");              //Display "Switch 12"
    159.                         __delay_ms(150);
    160.                         __delay_ms(150);
    161.                         __delay_ms(150);
    162.                         __delay_ms(150);
    163.                         __delay_ms(150);
    164.                         __delay_ms(150);
    165.                         __delay_ms(100);
    166.                     }
    167.            
    168.                 COL_1 = 1;                                  //COLUMN 1 is set HIGH again
    169.                 COL_2 = 0;                                  //COLUMN 2 is set LOW
    170.                 if (ROW_1 == 0)                             //and If ROW 1 is LOW...
    171.                     {                                       //...
    172.                         lcd_clear();                        //...
    173.                         lcd_goto(0);                        //...
    174.                         lcd_puts("Switch 2");                   //Display "Switch 2"
    175.                         __delay_ms(150);
    176.                         __delay_ms(150);
    177.                         __delay_ms(150);
    178.                         __delay_ms(150);
    179.                         __delay_ms(150);
    180.                         __delay_ms(150);
    181.                         __delay_ms(100);
    182.                     }
    183.                
    184.                 if (ROW_2 == 0)                             //if ROW 2 is LOW...
    185.                     {                                       //...
    186.                         lcd_clear();                        //...      
    187.                         lcd_goto(0);                        //...
    188.                         lcd_puts("Switch 5");               //Display "Switch 5"
    189.                         __delay_ms(150);           
    190.                         __delay_ms(150);
    191.                         __delay_ms(150);
    192.                         __delay_ms(150);
    193.                         __delay_ms(150);
    194.                         __delay_ms(150);
    195.                         __delay_ms(100);
    196.                     }
    197.                 if (ROW_3 == 0)                             //If ROW 3 is LOW
    198.                     {                                       //...
    199.                         lcd_clear();                        //...
    200.                         lcd_goto(0);                        //...
    201.                         lcd_puts("Switch 8");                   //Display "Switch 8"
    202.                         __delay_ms(150);
    203.                         __delay_ms(150);
    204.                         __delay_ms(150);
    205.                         __delay_ms(150);
    206.                         __delay_ms(150);
    207.                         __delay_ms(150);
    208.                         __delay_ms(100);
    209.                     }
    210.                 if (ROW_4 == 0)                             //If ROW 4 is LOW
    211.                     {                                       //...
    212.                         lcd_clear();                        //...
    213.                         lcd_goto(0);                        //...
    214.                         lcd_puts("Switch 11");          //Display "Switch 11"
    215.                         __delay_ms(150);
    216.                         __delay_ms(150);
    217.                         __delay_ms(150);
    218.                         __delay_ms(150);
    219.                         __delay_ms(150);
    220.                         __delay_ms(150);
    221.                         __delay_ms(100);
    222.                     }
    223.                 COL_2 = 1;                                  //COLUMN 2 is set HIGH again
    224.                 COL_3 = 0;                                  //COLUMN 3 is set LOW
    225.                 if (ROW_1 == 0)                             //and If ROW 1 is LOW...
    226.                     {                                       //...
    227.                         lcd_clear();                        //...
    228.                         lcd_goto(0);                        //...
    229.                         lcd_puts("Switch 1");               //Display "Switch 1"
    230.                         __delay_ms(150);
    231.                         __delay_ms(150);
    232.                         __delay_ms(150);
    233.                         __delay_ms(150);
    234.                         __delay_ms(150);
    235.                         __delay_ms(150);
    236.                         __delay_ms(100);
    237.                     }
    238.                
    239.                 if (ROW_2 == 0)                             //if ROW 2 is LOW...
    240.                     {                                       //...
    241.                         lcd_clear();                        //...      
    242.                         lcd_goto(0);                        //...
    243.                         lcd_puts("Switch 4");               //Display "Switch 4"
    244.                         __delay_ms(150);           
    245.                         __delay_ms(150);
    246.                         __delay_ms(150);
    247.                         __delay_ms(150);
    248.                         __delay_ms(150);
    249.                         __delay_ms(150);
    250.                         __delay_ms(100);
    251.                     }
    252.                 if (ROW_3 == 0)                             //If ROW 3 is LOW
    253.                     {                                       //...
    254.                         lcd_clear();                        //...
    255.                         lcd_goto(0);                        //...
    256.                         lcd_puts("Switch 8");               //Display "Switch 8"
    257.                         __delay_ms(150);
    258.                         __delay_ms(150);
    259.                         __delay_ms(150);
    260.                         __delay_ms(150);
    261.                         __delay_ms(150);
    262.                         __delay_ms(150);
    263.                         __delay_ms(100);
    264.                     }
    265.                 if (ROW_4 == 0)                             //If ROW 4 is LOW
    266.                     {                                       //...
    267.                         lcd_clear();                        //...
    268.                         lcd_goto(0);                        //...
    269.                         lcd_puts("Switch 12");          //Display "Switch 12"
    270.                         __delay_ms(150);
    271.                         __delay_ms(150);
    272.                         __delay_ms(150);
    273.                         __delay_ms(150);
    274.                         __delay_ms(150);
    275.                         __delay_ms(150);
    276.                         __delay_ms(100);
    277.                     }
    278.                 COL_3 = 1;                                  //COLUMN 3 is set HIGH again
    279.                 debouncecount = 0;
    280.             }
    281.     }
    282. }
    283.  
    284.  
    Does this look like I am debouncing the switch the correct way. Thanks for any help that you can give. I appreciate it a lot. Thanks again.
     
  2. Kermit2

    AAC Fanatic!

    Feb 5, 2010
    3,795
    950
  3. Bosparra

    Member

    Feb 17, 2010
    79
    3
    You need to first read to check for a keypress. Once a keypress is detected, wait for about 50ms and then read the pin again to check if the key is still being pressed, if so, you have a valid keypress.

    You will make life allot easier for yourself, if you break your code up into functions. Just don't go crazy with nested function calls, else you will run out of stack space quickly. For instance, create a function where the do the key debouncing and make it return the value of that was pressed. That way you get code reuse and you can actually make your application size smaller.

    Enjoy.
     
  4. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Thanks for the replies. So here is what I think I should do. Please tell me if this is correct. I will go step by step per reply, so it will be easier for me to keep straight.

    In my define statements I will define each switch, such as:

    Code ( (Unknown Language)):
    1.  
    2.  
    3. #define SWITCH10    COL_1 && ROW_1
    4. #define SWITCH7     COL_1 && ROW_2
    5. #define SWITCH4     COl_1 && ROW_3
    6. #define SWITCH1     COL_1 && ROW_4
    7. #define SWITCH11    COL_2 && ROW_1
    8. #define SWITCH8     COL_2 && ROW_2
    9. #define SWITCH5     COL_2 && ROW_3
    10. #define SWITCH2     COL_2 && ROW_4
    11. #define SWITCH12    COL_3 && ROW_1
    12. #define SWITCH9     COL_3 && ROW_2
    13. #define SWITCH6     COL_3 && ROW_3
    14. #define SWITCH3     COL_4 && ROW_4
    15.  
    16.  
    Then I will assign SWITCH... to a variable named keypress.

    Is that correct as far as the start.

    Thanks.
     
  5. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    Use a timer interrupt instead of code delay loops for debouncing, that way the processor isn't spending most of the time waiting to see if a switch is bouncing or not.

    Debouncing
     
  6. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Now its getting too complicated for me. I am trying to keep it simple at first to learn the basics and then go from there. I have decided that I would like to use 3 functions. A key_detect, key_read, and key_delay.

    The key_press function is the function were I would like it to detect a switch being pressed.

    The key_read function is where I would like to read what switch is pressed.

    The key_delay is the debounce function of maybe 20 to 50 ms.

    From here can someone direct me in the right direction on how to go about programming these.

    Thanks for the help.
     
  7. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    The debounce and read should be the same function. Usually sample the line once every 10mS 5 times (total 50mS), if 3-5 of 5 samples are high, respond to it. Create a variable and add 1 to it each time it is read high, then if variable > 3 at end of routine, it is high.

    The ~10mS delay can be part of the "read" code and rest of program, checking all rows and columns, and adding to a variable every time one line is polled. Once all are polled, and a key is decided to be pressed, reset counters, return the key value. Otherwise, if 5 reads pass with no lines hitting 3-5 counts, simply reset the counters for the rows/columns that were used in debouncing, and wait for the next call of the key-read function. The key-read function would be continually called at roughly 10mS points throughout the code while it is working on other things such as updating the LCD, etc.
     
  8. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Thanks Thatoneguy for the replies. Appreciate it very much. When you say sample the line once every 10ms, how do I do that. I must be having a brain block or something because I couldn't even begin to think how you would do that. Thanks again for the replies.

    If I would would create a key_detect function, I would put it into a while statement such as

    while (key_detect == 1)

    That would get me into a while statement wouldnt it when the key_detect() function detects a key press. If not could you lead me in the right direction.

    Thanks.
     
  9. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    Use an interrupt on long timer event. 10mS is a long time, you could call the routine more often, or read it faster. It depends on what you have set up on the timer prescalers and the clock speed. If not needed for anything else, set the prescalers down to trigger every 128 clocks or so, and on the interrupt service, add to a number until it equals <whatever period you want to sample at>.

    When the interrupt hits the needed time for sampling, set a flag. In the main loop of a program, have an if (flag) <key read/debounce routine here>, reading the lines, updating variable counts for each line, and clearing the flag at the end. The routine would then be run again when the timer has hit the interrupt enough times.
     
  10. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Thanks again for the reply. I have been doing some reading to see if I can understand what you are telling me to do. Here is what I have come up with.
    Would my program have in it somewhere the following lines. I know it is kind of obscure, but I was reading a book a while ago about Timer Overflows and I remembered this section. Here is what I have. Can you tell me if I am heading the right direction.

    while (!name of variable);
    pause(10);
    while (!name of variable);

    void msecbase (void)
    {
    OPTION = 0b00000001;
    TMR = 0xD;
    while (!T0IF);
    T0IF = 0;
    }
     
  11. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    There should be a function that is automatically called on each interrupt, here is the sample from Boost C samples:

    Code ( (Unknown Language)):
    1.  
    2. /*
    3.   Basic sample for BoostC compiler.
    4.   Use the 'Led Block' plugin to see
    5.   changing value on port B.
    6. */
    7.    
    8. #include <system.h>
    9.  
    10. // Set configuration word (sample only, ajust for your particular case)
    11. #ifdef _PIC16
    12.     #pragma DATA 0x2007, _HS_OSC & _WDT_OFF
    13. #endif //_PIC16
    14.  
    15. [COLOR=Red]void interrupt( void )
    16. {
    17. #ifdef _PIC16
    18.     portb++;
    19. #else
    20.     latb++;
    21. #endif
    22.  
    23. [/COLOR] [COLOR=Red]     clear_bit( intcon, T0IF );  //clear TMR0 overflow flag
    24. }[/COLOR]
    25.  
    26. void main()
    27. {
    28.     trisb = 0;        //configure port B
    29.  
    30. #ifdef _PIC16
    31.     portb = 0;        //clear port B
    32.  
    33.     option_reg = 7;    //set prescaler
    34. #else
    35.     latb = 0;        //clear port B
    36.  
    37.     // configure Timer0
    38.     set_bit( t0con, TMR0ON );    //enable timer
    39.     clear_bit( t0con, T08BIT );    //set 16-bit mode
    40.     clear_bit( t0con, T0CS );    // select internal clock
    41.     clear_bit( t0con, PSA );     // select prescaler
    42.     set_bit( t0con, T0PS0 );    // set 1:64 prescale ratio
    43.     clear_bit( t0con, T0PS1 );  
    44.     set_bit( t0con, T0PS2 );
    45. #endif
    46.     // enable interrupts
    47.     set_bit( intcon, T0IE ); //enable TMR0 overflow bit    
    48.     set_bit( intcon, GIE );
    49.  
    50.  
    51.     while( 1 ); //endless loop
    52. }
    53.  
     
  12. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    I am having trouble figuring out how to get into my continuous loop with the while statement, but I saw that you put your while statement at the end of your program. I wonder if that would work for me too?
     
  13. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    Yes, it will loop through whatever code is while(1) {code here}, and still hit the interrupts to update things. In the while loop is where you check to see if something needs to be done and do it.

    I like to keep as much processing out of the interrupt as possible, just set a bit flag when something is required. Then in the main while loop, test the different bit flags that may have been set during an interrupt, and act on them, clearing the bit.

    It's sort of like adding a "software interrupt" on top of the hard timer or pin input interrupt. You want to be sure your interrupt code completes before the next interrupt may occur, or it will be missed.

    The example above doesn't do this, as the interrupt only acts as a counter to flash the LEDs. Instead of incrementing PORTB, I would set a variable called increment to true, then in the while statement, have a line of
    Code ( (Unknown Language)):
    1.  
    2.  
    3. while(1)
    4. {
    5. if(increment)
    6. {
    7.    PORTB++;
    8.    increment=FALSE;
    9. }//end if
    10. }//end while
    11.  
    Add all of the if conditionals in the while{} loop, the interrupts will change the conditionals as needed, and you have no "downtime" doing a delay!
     
  14. beeson76

    Thread Starter Member

    Apr 19, 2010
    185
    1
    Ah. Its making sense now. You are considering the while loop the interrupt. Im not familiar with terms as of yet. But after reading it several times, I am getting the picture. This is awesome!! Thanks. Ok so from that point of view, I will re-read the previous post and see if it becomes clearer. I appreciate very much the help your giving me. This is a learning experience for me, and I appreciate the patience.
     
  15. thatoneguy

    AAC Fanatic!

    Feb 19, 2009
    6,357
    718
    That is essentially it. If you don't look at the interrupt routine, it would appear that very little code would actually run in the while loop. The reason it works is having global variables that are set in the interrupts, and "worked on" in the time between interrupts. This gives a lot more "realtime" feel to embedded applications, since a delay() function basically locks up the uC from a user point of view when interrupts aren't enabled.
     
Loading...