Hi all, I would like a bit of advice on how I can trigger an interrupt only once even when my switch is very bouncy, but there are a few limitations
.
This project has a navigation switch that is connected to a Samd21 MCU. The project is battery powered, so the device goes to sleep for about 5 seconds, wakes up to do some actions and then goes back to sleep for another 5 seconds. It does this using the Samd21's built in RTC library. That is fine, but I also want the device to wake up when the user pushes the navigation switch, so you can interact with it. I have been trying to figure out how to debounce the switch so the interrupt only triggers once, here are a few things I have thought of:
Software debouncing-
Normally I would just use the millis() function or the bounce2 library to debounce a switch, but that doesn't work in this case, since millis() and delays() don't work in interrupt service routines. I also cant see how long it has been since the interrupt was last run since the board will have just woken from sleep, and millis() doesn't count time during sleep. I also thought about using the RTC for timing, but it only has a resolution of 1 second from what I can see, which is obviously to long to debounce a switch.
Hardware debouncing-
I have also tested a circuit to debounce the switch. I am using the one described in this video. I have put a photo below of what the debounce circuit looks like, and what values seems to work best for me. It helps drastically with the bounce, but no matter what values I try for the capacitor and resistors, I still get a few extra triggers roughly every 10 presses of the switch. I would need to get that down to almost 0 false readings, otherwise I will still need software debouncing. The final option is to use something like the MAX6816 debounce IC, but they are very expensive, and takes up valuable space on my crowded PCB I am designing for this project.
Hopefully that all made sense! If anyone has any ideas as to how I can get this working, I would greatly appreciate it!
Don't use an interrupt, and all your problems go away. If an interrupt is required for wake up, disable it after wake up.
DrDiettrich:
Don't use an interrupt, and all your problems go away. If an interrupt is required for wake up, disable it after wake up.
Haha wow, that is actually a really good point.
I can just use the interrupt to wake the chip, and then use regular debouncing to detect the next press. I really should have thought of that. Thanks!
By the way, there's no reason why you can't use millis or micros in the ISR. You just have to keep in mind that millis will not be updated , and micros might overflow if your ISR takes too long.
You should never wait in your ISR, just check/set (volatile) variables, and probe pins.
Timing (between interrupts) using micros/millis are perfectly fine. Delays are not.
PieterP:
By the way, there's no reason why you can't use millis or micros in the ISR. You just have to keep in mind that millis will not be updated , and micros might overflow if your ISR takes too long.
You should never wait in your ISR, just check/set (volatile) variables, and probe pins.
Timing (between interrupts) using micros/millis are perfectly fine. Delays are not.
Ah ok, thats good to know. Yeah, I am going to keep my ISRs as short as possible. I hadn't thought of disabling the interrupt, so I was thinking I was going to have to use an interrupt for every time the button is pressed. I think it is definitely easier to just use an ISR to wake the chip, then use regular methods to read the state of buttons. I hadn't thought of using micros though, that could be useful.
Yes, you shouldn't use interrupts for buttons and other user input. Polling it in the loop should be more than fast enough.
PieterP:
Yes, you shouldn't use interrupts for buttons and other user input. Polling it in the loop should be more than fast enough.
I think you may have mis-read the original post. Check it again. The interrupt is absolutely required to wake from sleep. Depending on the depth of sleep used, the millis and micros timers don't update while sleeping.
Looking at that circuit I take it the switch connects to Vcc?
Drastically increasing the value of R29 can keep that pin high for quite some time. 100k would give you a few seconds of high time.
MorganS:
I think you may have mis-read the original post. Check it again. The interrupt is absolutely required to wake from sleep. Depending on the depth of sleep used, the millis and micros timers don't update while sleeping.
The interrupt is just for waking up. The debouncing is only needed while it is running. You don't need the interrupt when running normally.
wvmarle:
Looking at that circuit I take it the switch connects to Vcc?
Drastically increasing the value of R29 can keep that pin high for quite some time. 100k would give you a few seconds of high time.
Yes, the switch connects to VCC, and the micro is using the built in pull down resistor. From your advice I have now tested some higher resistors for R29, with some mixed results. Using a 100k gave quite a lot more false readings than using 680. Using a 22K for R29, have me a a similar number of false readings as 680. What I really need is an oscilloscope, so I can see whats going on!
100k is a bit extreme as it makes the pull-down quite weak, though the 10ยต cap should hold the voltage really stable, except maybe some very high frequency noise (if that's indeed the problem, add a 100 nF ceramic in parallel to your 10ยต).
You may also need a combination of hardware and software filtering, with in software ignoring any presses that are <10 ms or so.
Do you ever have it switch on while you do not press the button? Or is it only extra presses detected the moment you press the button?
There could be some noise in VCC since the project is being powered from a buck-boost converter. I am actually testing this debounce circuit on an Arduino Zero though, so there shouldn't be much noise there I would suspect. I have tried adding the 100nF, and it seems to increase false triggers, I guess becuase the capacitors in parallel have lower capacitance than just the 10uF one.
You may also need a combination of hardware and software filtering, with in software ignoring any presses that are <10 ms or so.
I have started to accept I will need both software and hardware filtering now :). I'm not sure if this will work, but my plan is this: Once interrupt is triggered, the ISR will disable the interrupt. The code then goes to the main loop since it just woke from sleep. Then I can read the switch using software debouncing to figure out the state, and also tell whether it is a long or short press of the switch. The code will do other stuff, then right before it goes back to sleep, it re attaches the interrupt.
Do you ever have it switch on while you do not press the button? Or is it only extra presses detected the moment you press the button?
In my testing, it has never triggered the interrupt without me pressing the button. If I dont use any hardware debouncing, it will trigger close to 100 times for just one press of the button. With hardware debouncing it will get a few extra triggers every few presses of the button.
Parallel capacitors are added together. Capacitors in series have lower overall capacitance. You're mixing it up with resistors, where parallel indeed decreases resistance, and series increases it.
How is your wiring? All good (new) wires, soldered firmly? This more and more starts to sound like poor contacts somewhere.
wvmarle:
Parallel capacitors are added together. Capacitors in series have lower overall capacitance. You're mixing it up with resistors, where parallel indeed decreases resistance, and series increases it.
How is your wiring? All good (new) wires, soldered firmly? This more and more starts to sound like poor contacts somewhere.
Oh yeah, silly me! The wiring looks fine to me, it definitely isnt perfect though. MY breadboard is kinda crappy, so I think you might be right. I have run the same code to see how many times the interrupt is triggered on my breadboard circuit (hardware debouncing disconnected), and on my PCB I have made for the project. The breadboard circuit gets around 20-100 triggers per press, and the PCB gets only around 5-20. So it seems you're right! I will need to get some new breadboards and check my wires.
That bright yellow wire - can't see what's in between - looks like a great source of extra triggers to me, especially if it moves a bit.
That is the diode for the debounce circuit. I only have some SMD diodes, so I had to solder wires to the diode's small pads. It seems to work ok, but it is definitely a weak point. To eliminate all the connections I can, I have connected the switch directly to the Arduino without the debounce circuit and with two new wires, and it is triggering about 50 times per push of the button. It seems like it might just be the button I have on the breakout board from Sparkfun is more bouncy than the one I have soldered onto my PCB. I think with a combination of a better switch in the final project, the debounce circuit, and some software debouncing, I can get the job done.
Usually a 100 nF cap in parallel to the switch is enough to debounce.
That combined with the 30k pull-ups gives an RC of 3 ms keeping your button low for about 1 ms upon breaking contact. Normally enough to bridge mechanical bounce, though 50 sounds like a really high number to me. Do solder it properly 
Haha, just using a 100nF cap across the button like you suggested seems to work better than the whole circuit I was using
. I agree, 50 is pretty damn high, but I don't see anything that could cause that at this point other than the button itself. Thanks for all the help!
Usually simpler is better 
Diode? Can you draw the schematic? A pencil sketch photographed is ok.
The debounce should be as simple as just a resistor and capacitor.