debounce problem

  1. Disable interrupt in the ISR on first contact and record time to a global variable. Re-enable interrupts in the loop after a bounce guard time.

When the code enters an ISR interrupts are automatically disabled.
And automatically re-enabled when the ISR ends.
Also, how are you going to measure this bounce guard time, if interrupts are disabled hence millis() can’t advance ?
(Self-answer: probably keeping only that particular interrupt disabled while globally enabling interrupts.)

Too tricky IMHO, if at all feasible.

Point 2 looks doable, instead.

Hi
Firstly MarkT is correct in saying that a schmitt trigger such as 74HC14 should be used for this type of switch debounce circuit. You are inputting a relatively slowly changing voltage level to the invertor which may result in false switching signals as the input slowly crosses the switching levels of the device.

Secondly, I am new here and know little of the Arduino microproc boards, so my next suggestion is a question. Is there any reason why the standard two Nand gate flip flop with a change over switch can not be used for switch debounce? This is the circuit I have almost always used for such applications.

See for example http://hyperphysics.phy-astr.gsu.edu/Hbase/electronic/setreset.html

Apologies for not attaching an image but I am still learning how to operate this site.

Cheers

switch debounce.gif

Although TCSC47's suggestions are valid and workable, the whole point of microcontrollers is that by using software they can do in a single device many functions that previously we had to build separate hardware to do. Use software debounce.

Use software debounce.

I have found the old fashioned r/c debouncer to be quite effective and reliable.

dhenry:

Use software debounce.

I have found the old fashioned r/c debouncer to be quite effective and reliable.

Yes, I’m sure it is; the atmega mcus have input hysteresis, so they can cope with the slowly-varying signal that this provides. But I prefer writing code to soldering components. [Actually, I don’t even have to write any code, because I re-use the PushButton class that I developed a while ago.]

Hi dc42 Your answer for using software debounce is one that I certainly would accept for large production professional designs requiring small space and small component count.

Also, as you have informed us, the inputs for the atmega device have hysterisis, then the sensor switches can be connected directly to the device, eliminating the 74HC14 completely, reducing the component count to a minimum.

However, and admitting that I do not have full details of what ironbot is doing, his project seems to me to be a learning exercise. Thus I would always advise a gate of some sort between the microproc and an outside input, to protect the microproc and allow alot of messing about with soldering, flying leads, and mistakenly connected inputs etc.

Also, as such, I would try and separate as many of the component parts of the design as possible. By using the two NAND gate debounce which is virtually infallible, we can get into the more intersting aspects of programming the functioning of the project, knowing that switch bounce is not a problem. Later on when all the fun bits have been done, then we could return to the finer points of professional design.

Incidently, as has been pointed out here already, an oscilloscope would be a very useful bit of kit to see exactly what is going on with problems like switch bounce. I've just bought a second hand scope with a terrific spec. for £100 on line. Cheers

the inputs for the atmega device have hysterisis, then the sensor switches can be connected directly to the device, eliminating the 74HC14 completely, reducing the component count to a minimum.

I don't know how the hysterisis (either on the atmega or hc14) would have eliminated the need for debouncing. Your circuit would have worked with a non-ST gate and the atmega, with hysterisis, would malfunction without a debouncing approach.

tuxduino:
When the code enters an ISR interrupts are automatically disabled.
And automatically re-enabled when the ISR ends.
Also, how are you going to measure this bounce guard time, if interrupts are disabled hence millis() can’t advance ?

The trick is to only disable the relevant interrupt vector (external interrupt or pin-change interrupt). Using external debounce-circuits with microcontrollers went out of fashion 20 years ago.- This thread can serve as good example why that is.

BenF:

tuxduino: When the code enters an ISR interrupts are automatically disabled. And automatically re-enabled when the ISR ends. Also, how are you going to measure this bounce guard time, if interrupts are disabled hence millis() can't advance ?

The trick is to only disable the relevant interrupt vector (external interrupt or pin-change interrupt). Using external debounce-circuits with microcontrollers went out of fashion 20 years ago.- This thread can serve as good example why that is.

I thought about that. But then you'd have nested interrupts. Scary :P Much better IMHO to have the ISR fire at regular intervals and debounce using volatile global variables, or by using static locals and using a volatile global to communicate with the main code.

Scary

Agreed. With that kind of risks, I would take the 2R/C debouncer any time of the day.

Being a (very) lazy software guy, I'd rather write an entire library just to debounce a pin than to figure the resistor or capacitor values to use :P but I try to keep the code at least debuggable. Interrupts are somewhat fascinating, but they're the gate to the hell of heisenbugs. So I try to avoid them as much as possible. Netsted ISRs ? Maybe when I'll start my own aRTOSuino :D

dc42: Although TCSC47's suggestions are valid and workable, the whole point of microcontrollers is that by using software they can do in a single device many functions that previously we had to build separate hardware to do.

I think your sentence is missing "if it's better, or faster, or cheaper without compromising functionality" between "do" and "."

On the other hand, if timing is critical, then misguided attempts to guess when a switch has settled into a stable position, that involve timing loops, are exactly the sort of thing that a couple of resistors and a capacitor will fix for you easily and cheaply.

Yes, I used an italic full stop. I hope anyone who remembers Algol-60 smiled.

TCSC47: Is there any reason why the standard two Nand gate flip flop with a change over switch can not be used for switch debounce? This is the circuit I have almost always used for such applications.

That type of circuit was discussed in the page I linked to earlier in the thread, which also mentioned the drawback that a double-throw switch is needed. But that aside, it works fine of course.

Nantonos:

TCSC47: Is there any reason why the standard two Nand gate flip flop with a change over switch can not be used for switch debounce? This is the circuit I have almost always used for such applications.

That type of circuit was discussed in the page I linked to earlier in the thread, which also mentioned the drawback that a double-throw switch is needed. But that aside, it works fine of course.

My favorite from days past was to wire the grounded common contact of a SPDT switch to the direct set and reset pins of a common 74LS74 flip-flop for perfect hardware debouncing. I would sometimes even add a simple RC to one of those two pins to ensure the flip-flop would power up to the state I wished it to be.

Lefty

tuxduino: I thought about that. But then you'd have nested interrupts

No you don't - and you can't really tell what the better option is (hardware or software) unless you explore all options, can you?

Debouncing can be regarded as a simple case of low pass filtering and this is (should be) second nature for a software engineer to implement (10 lines of code). With or without interrupts, this can be applied effectively and reliably in either case. Put in the effort to learn how and then decide what the best option is for your project.

No you don't

The scenario is this: the signal one is trying to debounce fires the interrupt - let's call this the "button interrupt". The button interrupt gets disabled, but other interrupts do not, so one can delay() inside the ISR to do sw debouncing. So while the "button ISR" is running, timer0 fires. And its ISR gets executed. That's a nested interrupt scenario, isn't it ? (btw, I'm not the OP).

if interrupts are disabled hence millis() can’t advance ?

That would be the case if millis() is poorly coded. Interrupts get disabled all the times. millis() presumably is programmed on timer interrupts. So even if the interrupt is disabled, the flag is still there and the timer keeps going, not missing a beat.

However, if one of the isrs takes too long to execute (during which the timer’s flag is set multiple times), millies() will lose count.

You can test this by running a loop inside an isr for an extended period (longer than millis()) and then read millis() to see if it is missing the beat.

tuxduino: That's a nested interrupt scenario, isn't it ?

You make this a lot more complicated than it really is. Your comments are comparable to someone hinting about using a voltage divider and having to explain the concept of a resistor, the fact that we need two of them, the use of power rails and how it all comes together. And surely, if none of these concepts are known, it is complicated. When working with electronics we need basic skills and the same applies to coding.

When we push a mechanical button, this typically results in a burst of pin state changes until the contacts make firm connection. The basic requirement of debouncing is to record this as a single event. Without some form of debouncing, we would otherwise record multiple key push events. We can avoid this with a single order external low pass filter (a RC circuit) or we can handle it in software (because we’re using a microcontroller) without additional components.

The software approach simply requires that we record the time of the first pin state change (someone pushed the button), and ignore additional pin state changes until a time period has elapsed. That’s all there is. How we detect the pin state change (interrupt, polled or otherwise) is irrelevant in this context.

Then to basic coding skills - we do not call the delay() function in ISR’s (in fact we have no use for a delay function at all in well written code). In ISR’s, we simply record the event (write to a global variable) and leave actual processing of the event for the loop() function. Rather than using delay(), we use the recorded time of the event and calculate the difference between current time and event time every time through our loop() function. In the first few milliseconds after the event, we simply ignore additional pin state changes (we already acted on the first state change). Once the debounce period has expired (say 5ms or so) we’re back to where we started and ready to act on new button push events.

We could also contain the debounce logic (no delay) within the ISR itself based on time between successive change events and so only report debounced events to the outside world (the loop function). This is just a matter of style.

Another approach again is to disable pin state interrupts within the ISR itself and then re-enable in the loop function once the debounce period has expired.

BenF, thanks for your thorough explanation.

It all started with this comment:

Grumpy_Mike:

Have you considered adding a software debounce ?

It is a lot more tricky on an interrupt pin, because the delay is in the ISR which is never a good thing.

I'd never call a delay() inside an ISR, but that comment got me thinking about what would happen if I did.

dhenry:

if interrupts are disabled hence millis() can't advance ?

That would be the case if millis() is poorly coded. Interrupts get disabled all the times. millis() presumably is programmed on timer interrupts. So even if the interrupt is disabled, the flag is still there and the timer keeps going, not missing a beat.

However, if one of the isrs takes too long to execute (during which the timer's flag is set multiple times), millies() will lose count.

You can test this by running a loop inside an isr for an extended period (longer than millis()) and then read millis() to see if it is missing the beat.

I was still referring to the scenario where delay() is called inside an ISR. Millis does depend on timer0 interrupt (relevant code is in hardware/arduino/cores/arduino/wiring.c).