Hardware debounce issues

Hello,

I'm trying to hardware debounce a button, I've read both Ganssle's guide and Jeremy Blum's guide and tried both ways without reaching the desired results.

First, I tried Ganssle's way, using a CMOS chip (CD74HCT14EX), here I get a consistent single reading on button close, but also a pretty consistent single reading on button open (a few times I won't get a reading on open, but mostly I do).
Swap the IC to a TTL chip (DM74LS14N) and mayhem ensues - here I get continuous readings as long as the switch is pressed closed. Due to the spamming of reading on switch close I don't know what happens on switch open, but after the release instance is over and the switch is at steady open I don't keep getting spam.

Moving on to Jeremy's method, on the TTL chip I get a fairly consistent single reading on button close, but also pretty consistent 1-6 readings on button release. Swapping the chip back to CMOS I get the best results yet - consistent single reading on button close, but still occasional single reading on button open (say 1 in 3 will give a false reading).

I've tested the setup with 2 different types of buttons, so it's not the specific button or even the specific type that's behaving weirdly.

The code itself is pretty basic and only designed to test the debouncing operation before implementing it to the desired project:

#define STPBTN 2
boolean STATE = LOW;

void setup() {
  pinMode(STPBTN, INPUT);
  attachInterrupt(digitalPinToInterrupt(STPBTN), STPISR, RISING); // set interrupt for stop button
  Serial.begin(9600);
}

void loop() {
  digitalWrite(LED_BUILTIN, STATE);
}

void STPISR()
{
  STATE = !STATE;
  Serial.println("1");
}

I would appreciate any insight you might have.
Thanks

I hope you don't indent to use Serial.print() in an ISR in the real code.

PaulS:
I hope you don't indent to use Serial.print() in an ISR in the real code.

No, it's there just for debugging, when I saw the LED was flickering I put it in there to see what's going on.

Looking at the first link (the second has a video or so, not going to waste my time on that) I don't see any issues.

The 2xNAND solution looks sound to me, as does the more commonly used RC solution (that NOT gate is optional). So bar wiring errors I don't know where you could go wrong.

Did you add the required decoupling capacitor to the ICs?

wvmarle:
Looking at the first link (the second has a video or so, not going to waste my time on that) I don't see any issues.

The 2xNAND solution looks sound to me, as does the more commonly used RC solution (that NOT gate is optional). So bar wiring errors I don't know where you could go wrong.

Did you add the required decoupling capacitor to the ICs?

The second is a simple RC with different values (10KOhm & 10uF, also with a Schmidt trigger), I found the link to it reading back here looking for a solution.
The 2xNAND solution requires a double throw switch, making it unsuitable to my project at hand, and the RC solution looked good on paper, but for some reason isn't working for me (note that the NOT gate is used because of the Schmidt trigger on it's input, not simply as an inverter).

I didn't see a required decoupling capacitor in either the IC's datasheets, but can add one and see if that helps.
I've tried debouncing a switch with only RC in the past and the results weren't impressive, that's why I'm trying to go the trigger way now.

I see lots of work going into this debouncing effort so I have to ask: have you looked at the behavior of just the switch? What does the raw mechanical bounce behavior look like?

It just seems to me that problem is most likely an absolutely crappy switch that bounces for 20-50 milliseconds or more. Without knowing exactly what you’re trying to fix, a solution will continue to elude you.

Usually 10-100 nF is enough for debouncing. 10 uF * 10k = 0.1 seconds!

But the most reliable way is indeed doing it in software, ignoring state changes for 25-50 ms after the first has been detected.

The NOT gate is not needed as your pins have a Schmitt trigger already, and this is also not the kind of signal that would benefit greatly from this.

The best way to perform de-bouncing of a switch or pushbutton, is to use a purpose-designed chip - such as an ATmega328. :roll_eyes:

Did some tests today following your comments.
It seems that my attempts of fixing the issue only made it worse compared to hooking the switch directly to the pin.
Following are my results so far:

*******switch wired directly to pin(internal pullup):**************

****************switch press: **************
1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1
12:32:39.965 -> 1

****************switch release:**************
1
12:32:43.730 -> 1


**********Ganssle's circuit:****************


****************switch press: **************
1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.309 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
12:24:01.356 -> 1
1

********** switch release: *************


12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.121 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
12:24:03.167 -> 1
1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.214 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.261 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.308 -> 1
12:24:03.355 -> 1
12:24:03.355 -> 1
12:24:03.355 -> 1
12:24:03.355 -> 1
12:24:03.355 -> 1
12:24:03.355 -> 1

*******simple RC (Jeremy's):******

****************switch press: **************
1

****************switch release:**************

1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.324 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.370 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.417 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.464 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
12:38:46.499 -> 1
1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.543 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1
12:38:46.590 -> 1

Not sure what to make of all this, I guess I'll have to go the software way despite my desire to avoid it.
I also ordered a couple of dedicated debouncer IC's (MC14490), but they will probably take a month to get to my neck of the woods so I'm trying to move forward without them.

ChaiChai:
Not sure what to make of all this, I guess I’ll have to go the software way despite my desire to avoid it.

But there is the question - since software de-bounce is the best and most reliable way to do it, why do you not want to do that properly?

And just to explain: a genuine software de-bounce routine polls the switch status on every pass of the loop, which in a properly written application is many times per millisecond, and registers a valid change of state when that state differs from the previous validated state and is replicated on every pass of the loop while the millis() value advances by a defined interval, generally between 5 and 10 ms. That is to say, if the “new” state is not (exactly) maintained on any of the polls, then the algorithm resets.

The hardware to implement that complete algorithm is not trivial, and it is therefore far easier to implement in software - such as on an ATmega328. In addition, a large number of buttons can be de-bounced simultaneously in such software because the “state” to which the algorithm refers is not limited to a single binary bit.

There are of course much more crude algorithms which necessarily are nowhere near as reliable, and if you use an inappropriate algorithm and find it to be unreliable, you might well come to the wrong conclusion.

Well, first of all I'm a beginner when it comes to programming, and would like to avoid writing complicated algorithms at this point. Second, my program has many repeating tasks, which in order to avoid stuffing them all in the Loop I separated to procedures, the procedure is broken when a flag changes and that change was supposed to happen via an interrupt with a very lean ISR that only change the state of a Boolean. I guess I could write a procedure that polls the relevant pins, perform the time interval check and change the flag accordingly, and then link to that procedure on every loop of the original procedure, but that somehow seems overly complicated compared to the interrupt way and will not work if the button is pushed and released while the procedure it using delay.
For the time time being I changed the user interface from a series of push buttons to a selector switch, which I simply read the state of on every pass, but I would like to manage to use buttons on interrupts in the future, so I'm still trying to understand how to debounce them.
BTW, I did find a debouncing library, and maybe I'm not using it correctly, but it wasn't working very well for me when in procedures outside the loop.

ChaiChai:
I guess I could write a procedure that polls the relevant pins,
[...]
but that somehow seems overly complicated compared to the interrupt way...

In fact, it's not. You still have to look for that flag - that's equivalent to looking at the pin. You do save yourself dealing with an ISR and the consequences of that.

As a rule, you don't need interrupts, and they're best avoided. Unless you know why you really need them. A very short signal is one reason (e.g. counting pulses from a high speed encoder, or frequency counting), accurate timing of when a signal changes another (e.g. when timing the discharge of a capacitor), waking from sleep a third reason.

...and will not work if the button is pushed and released while the procedure it using delay.

Now there's your problem. Use millis() for timing - have a look at the Blink Without Delay example on how that's done. That allows for a responsive program, which can do a lot while still polling a bunch of buttons, etc.

As a rule, you're not allowed to use delay() until you know why you're not allowed to use delay(). Because sometimes, when doing a quick and dirty test, it's really convenient.

HI,
Have you got a scope to see exactly what you are trying suppress?

There is a debounce library for the arduino, so you will not have to write any special code.

It also includes examples.

Tom..

ChaiChai:
Well, first of all I'm a beginner when it comes to programming, and would like to avoid writing complicated algorithms at this point.

And you think that interrupt programming is "not complicated" eh? As wvmarle points out, you really are a beginner! :grinning:

ChaiChai:
Second, my program has many repeating tasks, which in order to avoid stuffing them all in the Loop I separated to procedures, the procedure is broken when a flag changes and that change was supposed to happen via an interrupt with a very lean ISR that only change the state of a Boolean. I guess I could write a procedure that polls the relevant pins, perform the time interval check and change the flag accordingly, and then link to that procedure on every loop of the original procedure, but that somehow seems overly complicated compared to the interrupt way and will not work if the button is pushed and released while the procedure it using delay.

Clearly your program is at this point, disorganised. If you care to discuss your program, rather than an "XY Problem", we might help you get somewhere. With microcontrollers, software trumps hardware.

ChaiChai:
For the time time being I changed the user interface from a series of push buttons to a selector switch, which I simply read the state of on every pass, but I would like to manage to use buttons on interrupts in the future, so I'm still trying to understand how to debounce them.

To manage buttons on interrupts, you forget about interrupts. :grin:

TomGeorge:
There is a debounce library for the Arduino, so you will not have to write any special code.

I haven't looked at that library, as I find "libraries" difficult to examine, but I have great suspicions as to whether it actually complies with the full algorithm I described above. Do you know?