Maker Pro
Maker Pro

switch debouncing

Hey all,

I was checking out Gooligum's tutorials and came across this C code for switch debouncing:

for (db_cnt = 0; db_cnt < 10; )
{
_delay_ms(1);
if (GPIObits.GP3 == 0)
db_cnt++;
else
db_cnt = 0;
}

I understand how it all works but once the routine starts doesn't it get stuck in the for loop?(assuming the button never gets pressed long enough). This is all fine if that is all the program wants to do but if I want other things, like multiple buttons, or other switches to monitor, or even spitting out data to something how can I amend this code? Could I just stick something else in the 'for conditions' or is it more complex than that?
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Yes it does get stuck.

A better approach is to store the current state of the switch, the instantaneous state, and how long since the instantaneous state last changed. A function called frequently to update these values can update the "current state" to the instantaneous state if it has remained stable for some pre-determined amount of time.

Your code simply polls this frequently, then refers to the "current state" variable wherever it needs to know what state the switch is in.

The advantage of this approach is that your code does not have variable delays depending on how often you check the switch state.
 
Your code simply polls this frequently, then refers to the "current state" variable wherever it needs to know what state the switch is in.

Would an interrupt be of good use here or is it dangerous to have interrupts 'interrupting' things that deal with bouncing?
 

KrisBlueNZ

Sadly passed away in 2015
Whether it is or isn't a good idea to use an interrupt for pushbutton scanning really depends on the structure of the firmware.

There are many approaches to firmware debouncing, but they all require periodic sampling of the input(s).

Unless the remainder of the code is very compact and quick, it doesn't make sense to use delay_ms() to provide the sampling delay, because this forces the micro to spend most of its time twiddling its thumbs, when it could be doing other tasks.

Have a look at https://www.electronicspoint.com/required-delay-before-function-execution-t254903.html#post1512974.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
I would also recommend you to have a look at this thread.

It's a long thread, but that link should point you to a place to start reading (read back a bit if you want some background).

I'm seriously impressed at this guy's first attempt. If you're not a programmer and you're a bit daunted, don't be. If everyone's first effort was that good, I'd be out of a job :D

As Kris says above, whether any approach is good or not depends on many things. The approach I was suggesting in the thread I have pointed you to is one which lends itself to having many buttons monitored.

Using interrupts to scan buttons is *an* approach, but beware that this can cause a button to change state in the middle of your code.

If your code polls the buttons then sets status based on that, then polls again... you can be sure that the code setting the status has a static picture. It may be far easier to code knowing things are not changing under your feet (also the code may be easier to understand)
 

KrisBlueNZ

Sadly passed away in 2015
That's a good description Steve.

There are a lot of ways to debounce switches. Before starting to write code, you should read Jack Ganssle's article on debouncing. Just Google those words and you'll find it. It's also been linked to in several posts in these forums.

My article covers the general ways that a task can get periodic execution, to perform the scanning and processing. Yours covers the details of one way debouncing can be performed; there are many approaches. One factor to consider is whether you can tolerate a delay between the initial press and detection of the button; if you can, you can just wait until the button has been read in the same state for a certain number of consecutive samples, but if you can't, you need to immediately detect the change from OFF to ON, then apply debouncing after that time so that multiple presses and releases are not detected.

Regarding communication between the debouncing code and the mainline code, there are ways to do this so that button states do not change during execution of the mainline. One simple way is to put the debounced button states into different bits of a single byte or word, and copy the value at the start of processing. Another way is to use a queue of change data, or just a queue of button-has-been-pressed information. This assumes that the actual debouncing is performed by the timer code (whether it's run under interrupt or not), which is the usual method.

Also, mainline code will not necessarily have a problem if the button state changes while it's running; you need to analyse the mainline code to see what effect that would have, if any.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
Before starting to write code, you should read Jack Ganssle's article on debouncing.

Very good advice. I have read that in parts previously but your suggestion took me to a PDF containing the whole lot.

It shows both hardware and software solutions. The code I suggested in the thread I pointed to earlier is basically a reimplementation of his "simple yet effective debounce algorithm", with changes to (a) allow it to be called asynchronously, and (b) simplified to assume 10ms for either make or break debouncing.

A later example of mine for detecting just a press (and ignoring the release) is subject to being triggered by noise, but I've never found it to be a major issue. Perhaps if you had buttons at the ends of very long wires you might.
 

KrisBlueNZ

Sadly passed away in 2015
Right. That's a pretty good and widely used article.

Ganssle's article includes a description of a pushbutton that generated an abberant, excessively long and poorly defined bounce signal on one specific test press. Personally, I think it's reasonable to consider that signal as a special case that does not have to be debounced correctly. In the general case, I think a 10 ms sampling period and a debounce time of 2~4 samples will handle over 99% of practical cases, and cases that it doesn't handle properly are so unusual that some "incorrect" handling is acceptable.

In other words, some pushbuttons may be just too crappy to deal with, and in that case, it's the button, not the firmware, that needs to be fixed or replaced.

As you say, the firmware requirements need to be considered in the light of how the hardware is designed and constructed; if there is a possible issue with interference, then perhaps most problems can be avoided by sampling the input(s) several times, with short delays between samples, on every timer tick. It really depends on a lot of factors, including the hardware design and the firmware architecture.
 

(*steve*)

¡sǝpodᴉʇuɐ ǝɥʇ ɹɐǝɥd
Moderator
My code doesn't care how long it bounces for, but presumes if it hasn't seen a bounce in the last 10ms, then it's a valid state. Unless you ned *really* fast response, you could increase that to 50ms or even longer if you had to.

At some point you lose the ability to pick up a brief stab at a button.

This is why I have other code that assumes the first closure you see is the act of pressing the button. The debouncing is done on release. In an environment where you're not seeing false closures due to noise it is very effective.

The disadvantage of my routine is that it captures and stores the time of the last observed transition. This has significant overhead in both storage and time (to get this value).

As long as you're not running short of either, I'm not fussed about using it.
 
Top