2 push buttons instead of one

JohnInTX

Joined Jun 26, 2012
4,787
And I am trusting John's knowledge of IOC that this is not the best way to go in this application. Post 14.
I appreciate the vote but maybe some clarification is in order.
I don't like IOC for this kind of thing because many other PICs don't have the level of control over IOC that this one does. Many of the others caution against polling the other bits of the IOC port etc. Since I don't like solving the same problem twice, I just don't use IOC as a source of multiple, unrelated interrupts like debouncing.

As far as the debouncing logic itself, I use the timer approach to schedule it and prefer independent switch debounce logic. But there are lots of good ways to do it and @ErnieM and @MMcLaren offer some good ones. I'd support any that:
1) work
2) fit
3) are maintainable
4) are portable
..roughly in that order.

Have fun.
 

shteii01

Joined Feb 19, 2010
4,644
I could have been more clear on that - sorry, typing on a tablet.
Single priority (and single interrupt vector) means that there is only one interrupt / service routine. You can process more than one interrupt in the system but they all share resources and if you in the process of servicing one interrupt, any others that may occur during that time have to wait, regardless of how much more important they may be. That is why experienced PIC programmers will tell you to always keep your interrupt routines as short as possible and never do delays inside the ISR.

I do my debouncing logic a little different than ErnieM but we both use a single timer interrupt to schedule sampling of the switch(es).
John, this is more for general education purposes.

OP. I mentioned Intel 8051. It has 4 hardware interrupts. Each of them has priority.

You are using 12F PIC. According to someone above, it has only 1 hardware interrupt. However, it might also have software interrupts. You need to read the datasheet. There should be a chapter on interrupts. The software and hardware interrupts should be listed and their priority should be listed too.

To return to the mention of the single hardware interrupt on PIC 12F. Since there is only 1 hardware interrupt, you don't need to worry about priority of hardware interrupts. However, you now have to find a way to implement second button. You can not use 2nd hardware interrupt simply because it does not exist. Your only choice is to use polling (that has also been mentioned). Polling is simple, the program checks the status of a pin, you, the programmer/user, determine how often the pin is checked. Someone have mentioned 25 ms. The 25 ms can be used in the debouncing or/and you can use it to determine if the second button was activated. Depending on the button use, it could be longer or shorter period of time, 25 ms is not set in stone.
 

MMcLaren

Joined Feb 14, 2010
861
Hi shteii01,

You can use IOC (Interrupt On Change) for all six I/O pins (one pin is actually input only) and/or a single hardware interrupt pin which can be programmed as active low or active high. There are some caveats associated with IOC on these older devices which have been corrected in newer devices. IOC on newer devices allow you to select either a positive or negative edge trigger, or both, for each individual pin. The new IOC also includes a register that latches the IOC interrupt flag for each pin to help eliminate lost IOC pin state changes.

Cheerful regards, Mike
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
Hi all,

First, I want to thank you all for your help.

Someone asked for the code that I am using. If you look up "12F683 ISR switch debounce" you will
find the code that I used when I wrote the one button version of the this.

Someone suggested to use polling, or questioned why I chose to do the task using interrupts. I choose
to use interrupts because it is my understanding that it is possible to loose events during the time the processor is in the polling loop where as, if an event happens when the processor is executing an interrupt the event is pushed onto the stack and will be executed when the ISR returns control back to the processor; maybe delayed but not lost. Am I wrong?

My thought for using 2 ISR's was / is that I do not want to loose events and if any event is lost it is pushed onto the stack and the executed later, delayed, but not lost. I spoke of the 12F683 but there are other PIC's to choose from so I am not bound to the 683. In the end, they are all the same but different.

Me? I am somewhat new to this, but I like it, and want to do what is best. I am not against sticking my face in a bunch of books and data sheets to gain the understanding that I need. I just need some direction as to which way to look. If there is a better way to approach this problem this please point in that direction and explain to me why. I am more then willing to do the work to make that happen.

Thanks,
Dave
 

ErnieM

Joined Apr 24, 2011
8,377
...I choose
to use interrupts because it is my understanding that it is possible to loose events during the time the processor is in the polling loop where as, if an event happens when the processor is executing an interrupt the event is pushed onto the stack and will be executed when the ISR returns control back to the processor; maybe delayed but not lost.
Just exactly how fast is this button getting pushed that you may miss some?
 

Thread Starter

dayv3

Joined May 22, 2014
38
It is just a regular push button pressed by a human so it is very slow.
I believe that my timing loop is 1ms and I check the button 25 or 30 times before I say the
button press is valid. I think the output pulse is about 4ms. I do not see a problem with both
buttons being pressed at the same time or even being close but, I want to pretend that it is a
concern so that, in the future, I can reuse the code if loosing some information is a concern.
 

spinnaker

Joined Oct 29, 2009
7,830
Erinie's point was that with a human pressing it and properly written code, you will more than likely not miss a button press interrupts or not.
 

JohnInTX

Joined Jun 26, 2012
4,787
It is just a regular push button pressed by a human so it is very slow.
I believe that my timing loop is 1ms and I check the button 25 or 30 times before I say the
button press is valid. I think the output pulse is about 4ms. I do not see a problem with both
buttons being pressed at the same time or even being close but, I want to pretend that it is a
concern so that, in the future, I can reuse the code if loosing some information is a concern.
Your concerns are valid because of the highlighted words in the quote - timing loop. The old crystal ball has the 'code needed' icon flashing but here are some things to keep in mind.

Never do timing in a code loop. As you are seeing, that leads to problems of missing things because you are burning cycles when you should be monitoring other things.
Doing more than a few things on a PIC (or arguably any computer) requires that you never wait on anything. If you are debouncing a switch for 25ms for example, don't call a 1ms delay between samples. Other things can happen in the 1ms. And even if you get around to seeing a 4ms pulse by checking its input after the 1ms delay then what? Now you have to mix up the 4ms stuff with the 25*1ms stuff. Can you figure out a way to do it - sure! Would I do it? Heck no.

Set up a timer interrupt that executes once every 1ms and sample/accumulate switch counts on every interrupt service. If the switch is closed, increment a counter then leave. If the switch is open, clear the counter then leave. If the switch is closed for 25 interrupts (25ms) set a flag that its closed then leave. Note that the operative word here is 'leave'. Log the state of the switch and leave to perform other processing. The timing (25*1ms) is generated by 25 interrupts occurring at 1ms intervals - not by having the CPU play mumbly-peg for 1ms then coming back to see what it may have missed.

In a single interrupt PIC, you can have multiple interrupt sources, they just you just process them one at a time using the same rule - process the current condition then leave. If you need to remember what you did on the last interrupt on a debounce it could be a counter. On other things a flag or maybe a full-fledged state-machine. Whatever the requirement, provide flags, state variables, counters as required, process them quickly and then get the heck out of there.

One thing that's a challenge for inexperienced embedded programmers is that you interface with the real world and the real world is ugly and not on any kind of schedule - least of all yours. You have to deal with inputs (and outputs too, but that's another can of worms) that come at you at any time, legit or bogus, and you have to take each one, understand it and smooth it into your program. Things that work, don't. Processes that are guaranteed to complete, don't. Windows and Python guys don't have to deal with that. We do.

Its vital to think about the timings involved. Yes, we debounce switches for 25ms or so but that has nothing to do with what the switches actually do. The debounce is because mechanical wafers, spring loaded contacts 'bounce' i.e. mechanically hit the contact and rebound. The bounce is switch-specific and varies with age etc. but that's different than how fast a user can press and release the switch. Much slower. If you tried hard, got focused you might hit the switch 10 times a second - 100ms. In practice, users want to see feedback from hitting the switch - 200ms-300ms would be considered 'snappy'. So now we have 3 timing ranges. The two extremes are pertinent - debounce the switch so that one mechanical press is one signal to the software. Then, assuming that your software isn't off in the weeds delaying waiting on some other random input signal for 500ms, process the switch inputs. The key idea here is to segregate the timings and put their processing in the right place.

I could write a book on this.... Ernie and Mike could too.
 
Last edited:

Thread Starter

dayv3

Joined May 22, 2014
38
Set up a timer interrupt that executes once every 1ms and sample/accumulate switch counts on every interrupt service.
But that is what I did.
Maybe I used the wrong term of timing loop but I set up a timer and check the (single) switch's state every time the timer overflows and then updates the flags accordingly. Now that I am wanting to go into the world of multiple switches I am unsure of myself. Does every switch added need it's own timer or can the 1ms timer I set up be used for both, or more, switches? Can I just add more flags for the different switches to my current ISR or does each switch need its own ISR?

Your post makes me think; I want to look at the data sheet and some of my notes for a bit so let me write more later.
 

MMcLaren

Joined Feb 14, 2010
861
... Set up a timer interrupt that executes once every 1ms and sample/accumulate switch counts on every interrupt service. If the switch is closed, increment a counter then leave. If the switch is open, clear the counter then leave. If the switch is closed for 25 interrupts (25ms) set a flag that its closed then leave. Note that the operative word here is 'leave'. Log the state of the switch and leave to perform other processing. The timing (25*1ms) is generated by 25 interrupts occurring at 1ms intervals - not by having the CPU play mumbly-peg for 1ms then coming back to see what it may have missed. ...
That's exactly what he's doing, John.
 

spinnaker

Joined Oct 29, 2009
7,830
Your OP suggested that when you said interrupt, you meant interrupts for the pins on that the switches are connected and not a timer interrupt. Had you posted your code (and not hiding away on some other thread where we need to search) there would not have been that confusion.

It you indeed are using a timer and doing a count while the switch is pressed then it is a simple matter to expand on that. In you interrupt routine, just copy the code you have for one 2 more times. Modify that code so the new pins are read an change the name of four each counter. Say for example your counter was called counter rename it to counter1 and the other two would be counter2 and counter3. You may need to rename other variables too.

Or take a look at the code MMclaren presented a few posts back.
 

Thread Starter

dayv3

Joined May 22, 2014
38
I am not against reposting the code. If people want I will post the code that I posted before here.
I was just trying to prevent unnecessary redundancy.

Is that what up want?
 

spinnaker

Joined Oct 29, 2009
7,830
I am not against reposting the code. If people want I will post the code that I posted before here.
I was just trying to prevent unnecessary redundancy.

Is that what up want?
Yes please. It will be easier and clear up some confusion. Just post the relevant code (not the whole project). Please remember to use code tags. Makes reading the code easier.
 

Thread Starter

dayv3

Joined May 22, 2014
38
C:
/******************************************************************
   *                                                                *
   *  Project: 12F683 Debounce Test                                 *
   *   Source: 12F683_Debounce_Test.c                               *
   *   Author: Mike McLaren, K8LH                                   *
   *  (C)2012: Micro Application Consultants                        *
   *     Date: 05-Jun-2014                                          *
   *                                                                *
   *  12F683 Debounce Test Code (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                                                     *
   ******************************************************************/

   int i;


   #define LED         gpio.2   // LED attached to pin 5
   #define FT_SWITCH   gpio.0   // active lo switch on RA0  pin 4

  /* ******  constants   ****** */
   #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 count_needed (deb_peroid/smpl_rate)  // interations needed for test

  /* ******    Global Variables      ****** */
   volatile  bit  swt_state;    // believed  switch state     (0 = pressed)
   volatile  bit  swt_changed;  // switch state change flag   (1 = changed)



   void init()                  // Initialize 12F683 PIC
   { trisio = 0b00001001;       // RAo (pin 7) input, others outputs

     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)
                                // weak pull-ups enabled
     wpu.0 = 1;                 // RA0 pull-up
     gpio.2 = 0;                // make sure that pin 5 output led is off
  
     intcon = 1<<GIE|1<<T0IE;   // global & tmr0 interrupts enabled
   }


   void interrupt()             // ISR - interrupt service routine
   { static unsigned char debounce_cnt = 0;  //  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(FT_SWITCH == swt_state)  // Are the physical switch and the switch
         debounce_cnt = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt;
         if(debounce_cnt == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt = 0;         // reset debounce count
         }  // end if
       }  // end if
     }  // end if
   }  // end ISR


   void main() 
   { init();                    //
     swt_state = 1;             // 1 = not pressed
     swt_changed = 0;           // clear "new press" flag

     while(1)
     { if(swt_changed)          // if "new press" flag
       { LED = ~LED;            // toggle LED and
         swt_changed = 0;       // clear flag
       }   // enf if
     }   // end while
   }   // end main
 
Last edited by a moderator:

MMcLaren

Joined Feb 14, 2010
861
It looks like you didn't wrap it in <code> tags, Dave...

Here's an excerpt from the project I created from your other thread last year;
Code:
   void interrupt()             // ISR - interrupt service routine
   { static unsigned char debounce_cnt = 0;  //  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(FT_SWITCH == swt_state)  // Are the physical switch and the switch
         debounce_cnt = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt;
         if(debounce_cnt == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt = 0;         // reset debounce count
         }  // end if
       }  // end if
     }  // end if
   }  // end ISR
Perhaps you should expand your current method as Spinnaker suggests. Create separate counters and flags, one set for each switch and act on the new flags in your main program.
 

spinnaker

Joined Oct 29, 2009
7,830
This is just to give you the idea. It is not the exact code. You may need to make more changes. One of them might be you will need a swt_state0 instead of swt_state same for changed. I did not look into you code in that detail. Just expand this idea out for the 3rd and fourth switch.

Code:
#define FT_SWITCH0   gpio.0   // active lo switch on RA0  pin 4
#define FT_SWITCH1   gpio.0   // active lo switch on RA1  pin 4


static unsigned char debounce_cnt0 = 0
static unsigned char debounce_cnt 1= 0


 if(FT_SWITCH0== swt_state)  // Are the physical switch and the switch
         debounce_cnt0 = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt0;
         if(debounce_cnt0 == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt0 = 0;  



 if(FT_SWITCH1== swt_state)  // Are the physical switch and the switch
         debounce_cnt1 = 0;         // state flag the same?  If so reset counter
       else
       { ++debounce_cnt1;
         if(debounce_cnt1 == count_needed)  // is the switch is pressed for >= 30ms?
         { swt_state ^= 1;      // update switch state latch
           if(swt_state == 0)   // if "new press"
             swt_changed = 1;   // flag "new press" for main
           debounce_cnt1 = 0;
 
Top