Pages: [1]   Go Down
Author Topic: Simple delay() debounce failure mode?  (Read 308 times)
0 Members and 1 Guest are viewing this topic.
Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I wrote the following code to debounce a switch:

Code:
  const byte debounceDelay = 5;                                   // delay between digitalReads for switch debouncing
  boolean preDebounce = digitalRead(remoteSwitchPin) == LOW;      // read remote switch state
  if (preDebounce != remoteSwitchWasPressed)                      // see if remote switch state MIGHT be different from last loop
  {
    delay(debounceDelay);                                         // wait to let bouncing settle
    boolean postDebounce = digitalRead(remoteSwitchPin) == LOW;   // check post-debounce value

    if (preDebounce == postDebounce)                              // see if pre- and post-debounce values agree
      remoteSwitchIsPressed = preDebounce;                        // if switch state has REALLY changed, record the new switch state
  }

I know that blocking code is generally considered to be inferior to non-blocking code, but let's assume for the sake of argument that the blocking is acceptable in this case. Are there other failure modes for this type of debounce that I might be missing? I've done some research on debounce algorithms, and I'm surprised at how complex some of them are. It makes me wonder whether they're addressing failure modes that I'm missing.
Logged

Saskatchewan, Canada
Offline Offline
Edison Member
*
Karma: 49
Posts: 1417
Coding Geezer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Where do you set remoteSwitchWasPressed?

Logged

There are 10 kinds of people in the world,
those who understand binary, and those who don't.

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Where do you set remoteSwitchWasPressed?

Doggone it. I knew somebody would ask for the rest of the code.

Code:
void loop()
{   
  const byte debounceDelay = 5;                                   // delay between digitalReads for switch debouncing
  boolean preDebounce = digitalRead(remoteSwitchPin) == LOW;      // read remote switch state
  if (preDebounce != remoteSwitchWasPressed)                       // see if remote switch state MIGHT be different from last loop
  {
    delay(debounceDelay);                                         // wait to let bouncing settle
    boolean postDebounce = digitalRead(remoteSwitchPin) == LOW;   // check post-debounce value

    if (preDebounce == postDebounce)                              // see if pre- and post-debounce values agree
      remoteSwitchIsPressed = preDebounce;                        // if switch state has REALLY changed, record the new switch state
  }

  // Handle if the remote switch has been pressed or released (updating behaviorState appropriately)
  if (remoteSwitchIsPressed != remoteSwitchWasPressed)
    doRemoteSwitchChanged();

  remoteSwitchWasPressed = remoteSwitchIsPressed; // pass through to next loop()
}

I think this is all the relevant code.
Logged

Louisville, KY
Offline Offline
Newbie
*
Karma: 0
Posts: 11
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to try the other route, here's a link to a great tutorial for hardware debouncing. It uses very inexpensive components and is pretty trivial to design and wire together:
http://www.jeremyblum.com/2011/03/07/arduino-tutorial-10-interrupts-and-hardware-debouncing/
Logged

Saskatchewan, Canada
Offline Offline
Edison Member
*
Karma: 49
Posts: 1417
Coding Geezer
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a wondefully bouncy switch intended for a doorbell. 5 ms is definitely not enough time to debounce it.

With 5 ms, I get between 4 and 7 changes reported. Changing it to 10 ms made it work flawwlessly. I don't see any problems with that code at all.
Logged

There are 10 kinds of people in the world,
those who understand binary, and those who don't.

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

If you want to try the other route, here's a link to a great tutorial for hardware debouncing. It uses very inexpensive components and is pretty trivial to design and wire together:

Is the Schmitt trigger strictly necessary?
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a wondefully bouncy switch intended for a doorbell. 5 ms is definitely not enough time to debounce it.
With 5 ms, I get between 4 and 7 changes reported. Changing it to 10 ms made it work flawwlessly. I don't see any problems with that code at all.

I tested this switch out with a separate sketch that only read the button, and found that as low as 2 ms would produce no bouncing. If I were to put this code into production, I would probably increase the value to around 20 ms, because there's no guarantee that other switches in the field would perform similarly to mine.
Logged

Offline Offline
Faraday Member
**
Karma: 62
Posts: 3008
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That code seems to be very obfuscatory.
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

That code seems to be very obfuscatory.

Can you elaborate?
Logged

Offline Offline
Faraday Member
**
Karma: 62
Posts: 3008
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Doggone it. I knew somebody would ask for the rest of the code.

It still isn't clear how "remoteSwitchWasPressed"  has a value,  the first time that loop() runs.
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
Doggone it. I knew somebody would ask for the rest of the code.

It still isn't clear how "remoteSwitchWasPressed"  has a value,  the first time that loop() runs.

Ah--I see. I apologize, but the entirety of the code is something like 1100 lines, so I don't want to just dump it all and expect folks to dig through it. remoteSwitchWasPressed is initialized in setup() as follows:

Code:
remoteSwitchWasPressed = digitalRead(remoteSwitchPin) == LOW;
Logged

France S-O
Offline Offline
Edison Member
*
Karma: 41
Posts: 2236
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Is the Schmitt trigger strictly necessary?
Strictly necessary -> No
Usefull -> yes, especially if you can set hysteresis  yourself.

You have help for calculation:
http://sim.okawa-denshi.jp/en/compkeisan.htm
or a more detail document:
http://www.analog.com/library/analogDialogue/archives/34-07/comparators/comparators.pdf

For this application a comparator is better then an  amplifier.
The best is to choose an open-collecteur comparator like LM393.
With an open-collecteur you have to put a pull-up of about 1k to 10 k.
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I implemented this function more generally, as such:

Code:
const byte defaultDebounceDelay = 10;                                  // default switch debounce delay time, if no other specified


boolean isButtonPressedDebounce(byte pinNum, boolean wasPressed)
{
  return isButtonPressedDebounce(pinNum, wasPressed, defaultDebounceDelay);
}

boolean isButtonPressedDebounce(byte pinNum, boolean wasPressed, byte debounceDelay)
{
  boolean preDebounce = digitalRead(pinNum) == LOW;               // read remote switch state

  if (preDebounce == wasPressed)                                  // if remote switch state is the same as before, then
    return wasPressed;                                            // indicate no change in button state
  else                                                            // otherwise, remote switch state MIGHT have changed (we'll see...)
  {
    delay(debounceDelay);                                         // wait to let bouncing settle
    boolean postDebounce = digitalRead(pinNum) == LOW;            // check post-debounce value

    if (preDebounce == postDebounce)                              // see if pre- and post-debounce values are in agreement
      return preDebounce;                                         // if switch state has REALLY changed, return the new switch state
    else
      return wasPressed;                                          // otherwise, indicate no change in button state
  }
}
Logged

Offline Offline
God Member
*****
Karma: 17
Posts: 522
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

One more...

Code:
/ This version of the function performs a debounced read of the pin without needing information about
// the pin's previous state. This means that the debounce delay will always occur. This version could
// be useful for switches where edge detection is not occurring for whatever reason, and so the "previous"
// state of the button is not being tracked.
//
// The function works by reading the pin's current state and then passing the inverse of that state to
// the "real" debounce function as the pin's "previous" state. This ensures that the debounce function
// always believes that the button's state may have changed, and therefore always performs the debounce.

boolean isButtonPressedDebounce(byte pinNum)
{
  boolean b = digitalRead(pinNum) == LOW;
  return isButtonPressedDebounce(pinNum, !b, defaultDebounceDelay);
}
Logged

Pages: [1]   Go Up
Jump to: