# Counting number of push button press by external interrupt

It's a requirement that I should count the number of button press by external or any other interrupt.

So when I use the external interrupt to increment the counter by 1, it sometimes is crease by 2 or 3 due to debounce. Can anyone tell me how to count accurately in this case?

void Handle_PB() // my ISR called by pushbutton press falling edge
{
pb++;
if(pb>3)
{  num1=1;
}
if(pb>4)
{  num2=1;
pb=0;
}
}


pb is the pushbutton counter. and num1 and num2 are flags.

So after 4 times pushbutton press I want to do some other task. Simerly after 5 times pushbutton press I want to do another task. so Important is to count an exact number of a button press. I know the debounce principle. But where can I put this debounce delay code as it is interrupt driven?

• You can simply add say a 50ms delay after the first bounce if your program is fine with that much delay. That's not a very good solution but it might work. – dirac16 Jan 30 '18 at 18:16
• Can you add an external de-bounce circuit, and set the GPIO for rising edge detection? – Tyler Jan 30 '18 at 18:24
• I think you're better off polling. I've always done it by checking that a value is the same two polls in a row (for when I'm doing human interface push switches) and that's always worked perfectly. You can use a timer to do said polling, log the value then check in the main loop, or just poll within the main loop. – DiBosco Jan 30 '18 at 19:25
• I can't use any external circuit. And I have to do it by external interrupt as it is a time-critical project. Can't afford an ISR just to poll for a push button. – litun bls Jan 31 '18 at 8:26
• Exactly how time-critical is your project? Does the MCU have any hardware timers that you can use? – Bruce Abbott Jan 31 '18 at 17:46

1) It is better, if you can deal with debouncing in hardware itself. If possible, put an appropriate capacitor on the interrupt pin to ground.

2) In software you can deal like: (Assuming that bouncing can last up to 50s)

void my_interrupt_handler()
{
interrupt_time = currentmilliseconds();

if (interrupt_time - last_interrupt_time > 50)
{
press++;
last_interrupt_time = interrupt_time;
}

}


last_interrupt_time has 0 initial value. The code increments the press value for the very first press. But it will not increment for 50 ms, no matter how many button interrupts come in between due to bouncing. This will efficiently work only if the microcontroller supports interrupts within interrupts, otherwise it has chance of missing timer overflow interrupt.

3) Another method is simply adding a 50 ms delay after incrementing press. It works. But not a good method though.

4) Another idea, by using a flag variable:

void my_interrupt_handler()
{
if (flag == 0)
{
press++;
flag = 1
}
}


flag is a volatile variable shared between main() and ISR. Code is written in main() such that it reinitialises flag back to 0 only after counting 50 ms. So this is like disabling button interrupts for 50 ms. Anyway, Humans cant press faster than that.

• "It is always better, if we can deal with debouncing in hardware itself." Wrong! In cases where you have a microcontroller already, it makes sense to have it also handle the debouncing. It takes a small fraction of the processing power of most modern micros. Adding hardware would just increase size, weight, and cost with no benefit. – Olin Lathrop Jan 31 '18 at 17:56

You need to do what is called debouncing. There are many ways, and there is surely much written about it out there.

The method I ususally use is to not consider a new state valid until the input has been in that state for 50 consecutive 1 ms clock interrupts. 50 ms is longer than most switches bounce, but is still instantaneous in human time. Put another way, a human won't notice a 50 ms delay between pressing a button and some action occurring.

In rare cases where the system has to react faster than the debouncing time of the mechanical switch, you can trigger off the first change in state, then block out new changes in state until the existing change has settled. This gives you almost no latency, but does make the system susceptible to short glitches. You have to decide what is actually important.

Again, keep in mind that delays of up to about 50 ms are unnoticed by human users.

• Can you tell me how to implement debounce here because I use interrupt? Any spikes immediately call my ISR. – litun bls Jan 30 '18 at 18:35
• @litunbls, you can wait inside of the ISR (although it is generally bad practice to block an ISR), new interrupts should not trigger the ISR again. – Catsunami Jan 30 '18 at 22:39
• I am little confused whether delaying 30-50ms inside ISR is a suitable method as it is a very time critical application. – litun bls Jan 31 '18 at 8:27
• @lit: Waiting in the ISR is a bad idea. As I said, I usually have a periodic 1 ms interrupt. This is often useful for a variety of reasons. One of the things this interrupt does is to count whether each switch input has been in the same state for 50 consecutive interrupts. – Olin Lathrop Jan 31 '18 at 11:50
• @litunbls, if it is time sensitive, then you have no choice but to poll the button state. You could, however, wait until the first rising edge, then setup a timer to go off 50ms later to check the state again. If the state is still the same - great! The button is pressed. You can keep a flag in your button press ISR to indicate whether you are already waiting or if you need to start the timer, or you can disable the button ISR when waiting for timer so you don't have to respond to the interrupts you don't care about. – Catsunami Jan 31 '18 at 16:47

It's hard to propose solution without knowing what kind of MCU is that. Do you have any timer or sys tick?

Maybe something like this:

volatile unsigned long PBStart;

void Handle_PB() // my ISR called by pushbutton press falling edge
{
if (getSysTickTimer() - PBStart > 50)   // Where 50 is some threshold like 50ms
{
PBStart = 0;
}

if (PBStart == 0)   // Is ok to count
{
PBStart = getSysTickTimer();

pb++;
if(pb>3)
{  num1=1;
}
if(pb>4)
{  num2=1;
pb=0;
}
}
}

• It does similar to a when you poll with a timer interrupt. I do not want to run 2 ISR just to count the pushbutton press. Is there any way to do it by just one external interrupt triggered by pushbutton which also takes care of debouncing? – litun bls Jan 30 '18 at 22:29
• Is it a good idea to read the pin consecutively thrice inside interrupt and if the logical OR of all 3 results is 0 then increment the counter? Can anyone suggest any other out of box idea? – litun bls Jan 30 '18 at 23:05
• You do not need two ISR. getSysTickTimer() can just return the timer count. If you have spare timer just set it with high prescaler (so it is slow) and just read its value (no ISR needed). Again, I don't know what MCU we are talking about. Is is ARM or AVR? 32 or 8 bit? Regarding reading consecutively it may not be any solution. Image you press the button, you go to the ISR, then you read 10 times the button. How much time will it take? Few hundreds nanoseconds? It may be too fast to perform debouncing. Can you just use something like delay_ms() inside ISR? And then read the button few times? – zupazt3 Jan 31 '18 at 14:09
• You need some additional resources to perform debouncing. Timer, or delay function, or anything. If you don't want to, maybe you can use hardware debouncing with capacitor? – zupazt3 Jan 31 '18 at 14:12