Debouncing switches - hardware or software?

Hello,

I need to connect a bunch of switches to my Arduino project, and in the past I have always used software debouncing - either my own or using the Bounce library.

The thing is, I'm looking at using interrupts to 'catch' the button-presses, so I figure that it might be best to debounce them before they trigger the interrupts, rather than having to track the bouncing in the ISR. Won't a bouncing switch trigger many ISR calls during the bounces? Does this matter?

I've seen some pretty simple techniques for doing this in hardware, for example a Resistor/Capacitor pair, but am not sure if this is suitable. That link also mentions putting a buffer (is that a NOT gate?) before the pin - not sure if I'd need that. I'll only have about 4 switches to start with.

I have also seen some Switch Debouncer IC's (eg the Maxim Single/Dual/Octal, CMOS Switch Debouncers), but am struggling to find them in the UK for a price I'd want to buy 10+ at, or they are always SOT (or even worse SSOP) packages.

So. Should I:

  • Just juggle the inputs using software
  • Use a simple RC pair for each switch
  • Try to find some debouncers
  • Scrap the ISR and just check for state in the loop*

What happens when I want to use a PISO (perhaps a 4041) to get more switches per pin?

(*at the moment my loop is quick enough to respond to user input)

First make sure you have a problem. Generally you only get a problem when you are sampling the switches very frequently, often in a project this is not a problem because so much other stuff is going on as well. Normally a 20mS delay after a switch change is detected is enough to eliminate most problems with bounce.

for example a Resistor/Capacitor pair, but am not sure if this is suitable.

Why what makes you think it is not?

struggling to find them in the UK for a price I'd want to buy

You could be being unrealistic.

What happens when I want to use a PISO (perhaps a 4041) to get more switches per pin?

Exactly the same problem.

First make sure you have a problem. Generally you only get a problem when you are sampling the switches very frequently, often in a project this is not a problem because so much other stuff is going on as well. Normally a 20mS delay after a switch change is detected is enough to eliminate most problems with bounce.

The switches I happen to have used this time have more bounce than anything else I've ever seen. That's just bad luck in this case, but I suppose I was thinking that as my Arduino project gets a bit more complex, it might be nice to have some of the complexity removed. I am currently looking at building a somewhat reusable project box for my Arduino, so I have a box, with some 2.5mm jacks connected to I/O, a few switches, some LEDs and maybe an LCD. I can then just push new code to the board, hook up whatever sensors I want for the current prototype, and off I go.

for example a Resistor/Capacitor pair, but am not sure if this is suitable.

Why what makes you think it is not?

I meant it more in a sort of "have I missed anything" way. That "why don't you just use this £0.05p IC that does magic on elevendyone switches for you" situation.

struggling to find them in the UK for a price I'd want to buy

You could be being unrealistic.

I'll accept that. Especially if it's an 8-way, then I'd only need one or two. It's more the surface-mount that bothers me, but I think a bit more digging will find me some DIP ones.

What happens when I want to use a PISO (perhaps a 4041) to get more switches per pin?

Exactly the same problem.

I thought it might, but figured it's worth checking :slight_smile:

I would debounce in the ISR. But this has to be done properly. It is not that hard and you will find code if you google around. A comprehensive overview is here Entprellung – Mikrocontroller.net. However this is in German.

I never have switches generate an interrupt directly, I set up a timer ISR to interrupt every few milliseconds so that I can poll them and debounce them in that ISR.

class DebouncedButton
{
  volatile bool state;
  volatile bool newPress;
  volatile uint8_t count;
  int pin;
public:
  void poll();

  // Call this to see if the button is down
  bool getState() 
  { 
    return state; 
  }

  // Call this to see if the button has been pressed again since we last asked
  bool getNewPress() 
  {
    if (newPress)
    {
      newPress = false;
      return true;
    }
    return false;
  }

  DebouncedButton(int p) : pin(p), state(false), count(0) {}

  void init();
};

// Call this from setup()
void DebouncedButton::init()
{
  pinMode(pin, INPUT);
  digitalWrite(pin, HIGH);    // enable pullup
  count = 0;
  state = false;
  newPress = false; 
}
  
// Call this every 1ms or so from a timer interrupt or in loop()
void DebouncedButton::poll()
{
  bool b = !digitalRead(pin);
  if (b != state)
  {
    ++count;
    if (count == debounceCount)
    {
      state = b;
      count = 0;
      if (state)
      {
        newPress = true;
      }
    }
  }
  else if (count > 0)
  {  
    --count;
  }
}

An easy way to generate the tick ISR is to use the MsTimer2 library.

Technically, ISRs in Arduino disable interrupts for the duration of the ISR, so if you knew that people wouldn't need to hit more than one button at a time, nor hit many buttons in rapid succession, keeping the ISR intentionally long would be a valid debouncing on its own.

The reason for this is that the interrupt would only be triggered by the first pulse. Following pulses would be ignored as long as the ISR was running. This works well if you can tell for how long the button will bounce, and make the ISR last at least that long.

Note that Arduino delay() doesn't work inside ISRs (because it relies on timer interrupts, iirc), but you can use _delay_ms(). The end result is similar (you might need to #include <avr/delay.h>, not sure).

Too fishy? Hardware debouncing would work too. Put a resistor and a capacitor in series (VDD-Res-Cap-GND), connect the interrupt pin and one side of the button to the point between the cap and the resistor, and connect the other side of the button to GND. What happens is that the capacitor is always charged to VDD, but when you hit the button you connect it to GND and it discharges. The time is given by the time constant Tau=RC, so for a 100ms falling time you could use something like 1kohm and 0.1uF. Adjust to your switch's bounce time, and set the interrupt to LOW.

To top it all off, you can put a schmitt trigger in front of the resistor/cap circuit, which would give you a clean on/off signal instead of the rising/falling curve of the RC circuit (which the arduino should be fine with, by the way, but who knows). I've used 4584's in the past, the 7414 is more common but it's inverting, so invert your interrupt too.

Just to make it clear, the debouncing I use is not in the timer ISR, it's the result of successive calls to the timer ISR. All ISR's should be kept as short as possible, otherwise you are likely to miss other important interrupts, e.g. timer and hardware serial. So inside an ISR, any sort of delay mechanism longer than a couple of microseconds is a bad idea.