2 push buttons instead of one

MMcLaren

Joined Feb 14, 2010
842
Basically, you can use the same debounce code twice but you need separate and unique counter variables and flags for each switch. Perhaps something like this (untested) variation;
Code:
  /******************************************************************
   *                                                                *
   *  Project: 12F683 Debounce Test                                 *
   *   Source: 12F683_Debounce_Test.c                               *
   *   Author: Mike McLaren, K8LH                                   *
   *  (C)2012: Micro Application Consultants                        *
   *     Date: 05-Jun-2014, revised 05-Dec-2015                     *
   *                                                                *
   *  12F683 Debounce Test Code (AAC Forum)                         *
   *  12F683 Debounce Test For Two Switches (AAC Forum)             *
   *                                                                *
   *                                                                *
   *      IDE: MPLAB 8.92 (tabs = 4)                                *
   *     Lang: Sourceboost BoostC v7.05, Lite/Free version          *
   *                                                                *
   ******************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG, _MCLRE_OFF & _WDT_OFF & _INTOSCIO

   #pragma CLOCK_FREQ 1000000   // 1-MHz INTOSC

  /******************************************************************
   *  constants                                                     *
   ******************************************************************/

   #define LED1        gpio.2   // LED attached to pin 5
   #define LED2        gpio.1   // LED attached to pin 6
   #define SW1         gpio.3   // active lo switch on GP3 (pin 4)
   #define SW2         gpio.0   // active lo switch on GP0 (pin 7)

   #define deb_peroid 30        // 30ms - time for the switch to be considered debounced
   #define smpl_rate  1         // 1ms - per sample @ 1MHZ CPU clock /4 /TMR0 (sample rate)
   #define db_count (deb_peroid/smpl_rate)  // iterations needed for test

  /* ******    Global Variables      ****** */
   volatile bit sw1_state;      // sw1 state latch
   volatile bit sw1_changed;    // sw1 state change flag
   volatile bit sw2_state;      // sw2 state latch
   volatile bit sw2_changed;    // sw2 state change flag

  /******************************************************************
   *                                                                *
   ******************************************************************/

   void interrupt()             // ISR - interrupt service routine
   { static unsigned char db1_cnt = 0;  // sw1 debounce counter
     static unsigned char db2_cnt = 0;  // sw2 debounce counter

     if(intcon.T0IF)            // if timer0 overflows
     { intcon.T0IF = 0;         // clear interupt flag
       tmr0 += (256-250+3);     // reset for 250 * 4 = 1000 usecs

       if(SW1 == sw1_state)     // if sw1 == sw1 latch (same state)
         db1_cnt = 0;           // reset debounce counter
       else                     //
       { if(++db1_cnt == db_count)  // if 30-msecs
         { sw1_state ^= 1;      // update switch state latch
           if(sw1_state == 0)   // if "new press"
             sw1_changed = 1;   // flag "new press" for main
           db1_cnt = 0;         // reset debounce counter
         }  // end if
       }  // end if

       if(SW2 == sw2_state)     // if sw2 == sw2 latch (same state)
         db2_cnt = 0;           // reset debounce counter
       else                     //
       { if(++db2_cnt == db_count)  // if 30-msecs
         { sw2_state ^= 1;      // update switch state latch
           if(sw2_state == 0)   // if "new press"
             sw2_changed = 1;   // flag "new press" for main
           db2_cnt = 0;         // reset debounce counter
         } // end if
       } // end if
     }  // end if
   }  // end ISR

   void init()                  // Initialize 12F683 PIC
   { trisio = 0b00001001;       // GP3 and GP0 inputs

     cmcon0 = 0x07;             // Disable the comparator
     ansel = 0;                 // Disable the A/D - Make pins digital i/o
     adcon0 = 0;                // A/D converter off
     ccp1con = 0;               // compare/ capture/ PWM off
         
     osccon = 0x41;             // setup INTOSC = 1-MHz

     option_reg = 0x08;         // TMR0 prescaler 1 (4-us ticks)

     LED1 = 0;                  // LED1 'off'
     LED2 = 0;                  // LED2 'off'

     intcon = 1<<GIE|1<<T0IE;   // global & tmr0 interrupts enabled
   }

  /******************************************************************
   *                                                                *
   ******************************************************************/

   void main()
   { init();                    //
     sw1_state = 1;             // 1 = not pressed
     sw1_changed = 0;           // clear "new press" flag
     sw2_state = 1;             //
     sw2_changed = 0;           //

     while(1)
     { if(sw1_changed)          // if sw1 "new press
       { LED1 = ~LED1;          // toggle LED1 and
         sw1_changed = 0;       // clear sw1 flag
       }   // end if
       if(sw2_changed)          // if sw2 "new press"
       { LED2 = ~LED2;          // toggle LED2 and
         sw2_changed = 0;       // clear sw2 flag
       }   // end if
     }   // end while
   }   // end main
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
Got it, Clean up add the #defines and add extra variables
Fix trisio (I think I will need a different PIC that has more I/O)
There only one interrupt() routine and one timer (that is shared) that contains the

if (FT_SWITCH1 == swt1_state)
if (FT_SWITCH2 == swt2_state)
*
if (FT_SWITCHn == swtn_state) statements

and Fix main and any other thing that needs to be adjusted.
 

MMcLaren

Joined Feb 14, 2010
842
Good deal...

Just in case... here's an untested version using parallel switch state logic with independent 24-msec debounce timers. It probably looks like gibberish but I hope that someone may find it useful.

Good luck on your project. Cheerful regards, Mike

Code:
  /******************************************************************
   *                                                                *
   *  Project: 12F683 Debounce Test                                 *
   *   Source: 12F683_Debounce_2.c                                  *
   *   Author: Mike McLaren, K8LH                                   *
   *  (C)2015: Micro Application Consultants                        *
   *     Date: 05-Dec-2015                                          *
   *                                                                *
   *  12F683 Multi-Switch Debounce Test (AAC Forum)                 *
   *                                                                *
   *                                                                *
   *      IDE: MPLAB 8.92 (tabs = 4)                                *
   *     Lang: Sourceboost BoostC v7.05, Lite/Free version          *
   *                                                                *
   ******************************************************************/

   #include <system.h>

   #pragma DATA _CONFIG, _MCLRE_OFF & _WDT_OFF & _INTOSCIO

   #pragma CLOCK_FREQ 1000000   // 1-MHz INTOSC


  /******************************************************************
   *  variables                                                     *
   ******************************************************************/

   unsigned char delta[] = { 0,0,0,0 };
   unsigned char swnew = 0;     // switch logic work variable
   unsigned char swold = 0;     // switch state latch
   unsigned char flags = 0;     // switch "new press" flags

  /******************************************************************
   *  constants                                                     *
   ******************************************************************/

   #define sw1 GP3              // assign sw1 pin
   #define sw2 GP0              // assign sw2 pin
   #define sw_mask 1<<sw1|1<<sw2

   #define sw1_flag  flags.sw1  // sw1 (GP3) 'new press' flag
   #define sw2_flag  flags.sw2  // sw2 (GP0) 'new press' flag

   #define LED1      gpio.2     // LED attached to pin 5
   #define LED2      gpio.1     // LED attached to pin 6

  /******************************************************************
   *                                                                *
   *  K8LH parallel switch state logic (new press filter)           *
   *                                                                *
   *  debounces 'press' and 'release' states when sampled four      *
   *  times at the same state in a 24-msec span.                    *
   *                                                                */
   void interrupt()             // 8-msec TMR2 interrupts
   { pir1.TMR2IF = 0;           // clear TMR2 interrupt flag
     delta[3] = delta[2];       // shift delta accumulator
     delta[2] = delta[1];       //  "
     delta[1] = delta[0];       //  "
     swnew = ~gpio;             // sample active lo switches
     swnew &= sw_mask;          // on GP3 & GP0 pins only
     swnew ^= swold;            // changes, press or release
     delta[0] = swnew;          // add to delta accumulator
     swnew &= delta[1];         // check debounce 'time-out'
     swnew &= delta[2];         //  "
     swnew &= delta[3];         //  "
     swold ^= swnew;            // update switch state latch
     swnew &= swold;            // filter out 'release' bits
     flags |= swnew;            // save 'new press' flags
   }                            //

  /******************************************************************
   *  main setup                                                    *
   ******************************************************************/

   void main()
   { trisio = sw_mask;          // GP3 & GP0 inputs
     cmcon0 = 0x07;             // comparator off, digital I/O
     ansel = 0;                 // adc off, digital I/O
     osccon = 0x41;             // setup INTOSC = 1-MHz
  /*                                                                *
   *  setup TMR2 for 8-msec interrupts                              *
   *                                                                */
     t2con = 0b00001001;        // '0-------' unimplemented bit
                                // '-0001---' TOUTPS<3:0>, postscale 2
                                // '-----0--' TMR2ON, Timer 2 off
                                // '------01' T2CKPS<1:0>, prescale 4
     pr2 = 250-1;               // 250 x 32-usec 'ticks' = 8 msecs
     pir1 = 0;                  // clear peripheral interrupt flags
     pie1.TMR2IE = 1;           // set Timer 2 interrupt enable bit
     intcon = 0b11000000;       // '1-------' GIE, enable global and
                                // '-1------' PEIE, peripheral ints
                                // '--0-----' T0IE, TMR0 ints off
                                // '---0----' INTE, off
                                // '----0---' GPIE, IOC disabled
                                // '-----000' T0IF/INTF/GPIF flags
     t2con.TMR2ON = 1;          // start TMR2 (and interrupts)
  /*                                                                *
   *                                                                *
   *                                                                */
     LED1 = 0;                  // turn LED1 off
     LED2 = 0;                  // turn LED2 off

  /******************************************************************
   *  main loop                                                     *
   ******************************************************************/

     while(1)
     { if(sw1_flag)             // if sw1 "new press"
       { sw1_flag = 0;          // clear sw1 flag
         LED1 ^= 1;             // toggle LED1
       }                        //
       if(sw2_flag)             // if sw2 "new press"
       { sw2_flag = 0;          // clear sw2 flag
         LED2 ^= 1;             // toggle LED2
       }                        //
     }   // end while
   }   // end main
 
Last edited:

ErnieM

Joined Apr 24, 2011
8,020
In the last few projects I have done I used 5 buttons arranged in an up-down-left-right-enter pattern. So while it is unlikely two buttons ever get pressed together it s possible, though not a major concern of mine.

Buttons are checked in a timer based ISR that fires off every 25 ms. Actually it fires every ms but only checks every 25th time.

All 5 buttons get timed together. The state is read every 25 ms and compared to the previous reading. Two consequative like readings indicate a valid button state. The current button state is published in a global variable for main code to consume as it needs.

It makes for simple code, even in my case where I share the lines to read the buttons with an alphanumeric LCD display, which adds a few complications to handle.
 

Art

Joined Sep 10, 2007
806
Is there a reason the interrupt is needed for a button press?
I can’t see why unless the rest of the code is so tight and fast you can’t afford to check them and increment timers.
Code:
int debouncea;
int debounceb;
int debounceval = 0xFFFF;



loop() {

if (debouncea == 0) {
if (buttona == high) {
// button a pressed
debouncea = debounceval;
}}

if (debounceb == 0) {
if (buttonb == high) {
// button b pressed
debounceb = debounceval;
}}


if (debouncea > 0) { // decrement debounce timers
debouncea—-;
}
if (debounceb > 0) {
debounceb—-;
}
for faster you could sample the whole port the buttons are on and look for an change in value.

Code:
if (oldport != newport) {
// check for the bit that changed
//bittest port.bit0 for button a.... etc.
}
oldport = newport
It somehow seems like a waste of an interrupt.
 

MMcLaren

Joined Feb 14, 2010
842
If you're using interrupts already (for heartbeat, display, etc.) it doesn't cost much to handle switch debounce there in the background. While you can debounce in the main loop for simple programs (below), I wonder if it might require more work making lengthy functions "switch aware" in more complex programs?

Code:
     while(1)
     { static char swnew = 0;   //
       static char swold = 0;   // debounced switch state latch
   /*                                                               *
    *  K8LH parallel switch state logic (new press filter)          *
    *                                                               *
    *  swnew  ___----____----____  invert active lo switches        *
    *  swold  ____----____----___  switch state latch               *
    *  swnew  ___-___-___-___-___  changes, press or release        *
    *  swnew  ___-_______-_______  filter out 'release' bits        *
    *                                                               */
       delay_ms(25);            // 25-ms debounce/sample interval
       swnew = ~gpio;           // sample active lo switches
       swnew ^= swold;          // changes, press or release
       swold ^= swnew;          // update switch state latch
       swnew &= swold;          // filter out 'release' bits

       if(swnew & 0b00000001)   // if sw0 (GP0) 'new press'
       { LED1 ^= 1;             // toggle LED1
       }                        //

       if(swnew & 0b00001000)   // if sw3 (GP3) 'new press'
       { LED2 ^=1;              // toggle LED2
       }                        //
     }                          // end while(1)
   }                            // end main
 
Last edited:

MMcLaren

Joined Feb 14, 2010
842
Hi Ernie. Happy Holidays.
I do not see any code for debouncing in your post.
The debouncing mechanism is easily overlooked (grin). Debouncing is handled automatically simply by choosing an appropriate sample/debounce interval.

We discussed this last October in the Debouncing a switch in software using a PIC16F72 thread in posts 8 and 10 through 12.

Debouncing is similar to your method in that switches are sampled at a debounce interval but I don't force an extra 25-msec debounce sample. That is, the routine recognizes a switch state change in one sample or two samples (if bouncing) while your method requires two samples or three samples (if bouncing).

Cheerful regards, Mike
 
Last edited:

Art

Joined Sep 10, 2007
806
In any Pic wouldn’t that hold up what the main program was doing for the 25ms delay intervals?
It depends if the main program is cycling fast enough or makes delay itself I suppose.
 

ErnieM

Joined Apr 24, 2011
8,020
In any Pic wouldn’t that hold up what the main program was doing for the 25ms delay intervals?
It depends if the main program is cycling fast enough or makes delay itself I suppose.
True dat, but that is example code more suitable for inclusion in a heartbeat timer ISR. While MM doesn't explicitly state that I am sure that is his intent.

I still have to play thru his state logic. Mine is dirt simple (current switch reading must match last to score stable), I suspect his is correct but I don't see it to say that.
 

MMcLaren

Joined Feb 14, 2010
842
Hi Art.

In the case of that example program, the only events are the switch presses which toggle a pair of LEDs on and off so including a 25-msec delay isn't a problem. If you modify the loop timing to accommodate faster events, you could use counters to enable the switch code logic at the slower 25-msec intervals. Or... you could use interrupts to handle periodic events (grin)...
 
Top