In the previous entry we saw what interrupts are and how to use them to respond to hardware events on pins.
We also made it clear that physical devices, such as buttons, optical sensors, etc., exhibit a bouncing effect that interferes with the use of interrupts, and that we need to eliminate it or we won’t be able to use interrupts with these devices.
The process of eliminating this bounce is called “debounce”. In this entry, we will learn what bounce is and how to eliminate it with hardware and software debounce.
What is debounce?
Electronic devices generate a signal when changing state that, while not perfectly square, is generally more or less “clean”. For example, let’s look at the signal generated by Arduino when changing the state of a digital output from HIGH to LOW.
However, the real world is not so pretty. Many physical devices often generate noise on the signal edges. As an example, let’s look at the voltage variation that occurs when the state change is generated by a button.
Notice the amount of noise that occurs after the edge. Essentially, in the range of a few microseconds, the signal is pure noise. All those peaks can cause multiple interrupts to trigger.
Testing the bounce
To test the bounce, we will simply use a wire to connect Pin 2 and Ground (you can also use a button or a switch).
We activate the internal Pull UP resistor on Pin 2 and define an interrupt for the falling event on the PIN, and in the associated ISR function, we simply increment a counter.
In the main loop, we check if the counter has been modified, and if it has, we display its value on the serial port.
const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;
void setup()
{
pinMode(intPin, INPUT_PULLUP);
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(intPin), debounceCount, FALLING);
}
void loop()
{
if (counter != ISRCounter)
{
counter = ISRCounter;
Serial.println(counter);
}
}
void debounceCount()
{
ISRCounter++;
}
When testing our setup and connecting PIN 2 to GROUND, we would expect the variable to increment one by one. But we will see that it actually jumps several numbers each time (even several tens).
This is the effect of bounce. The noise in the signal is generating multiple interrupts every time we connect the wire.
Eliminating the bounce
We have two ways to apply debounce. By adding electronic devices that filter the signal (hardware debounce) or by modifying our code to eliminate the bounce (software debounce).
Hardware debounce
Applying hardware debounce has the advantage of not increasing the execution time of our code. Additionally, it is generally a more robust solution. On the downside, it increases the complexity of our setup.
The simplest way to apply hardware debounce is to place a capacitor in parallel with the device (button, switch, sensor…). A capacitor on the order of 1uF should be sufficient to filter out most of the noise.
The connection schematic is as follows.
In general, it is always advisable to add a hardware filter when using physical inputs with interrupts.
Software debounce
Software debounce has the advantage of not requiring additional components. We resolve the bounce solely by modifying our program code.
On the downside, it slightly increases the execution time and complexity of the code. Additionally, if we do not apply the code correctly, we may ignore “true” interrupts.
The simplest way to apply software debounce is to check the time between interrupt triggers. If the time is less than a certain threshold (threshold), we simply ignore the interrupt. Essentially, we have defined a “dead zone” where we ignore the generated interrupts.
To apply software debounce, we modify the ISR function as follows.
const int timeThreshold = 150;
const int intPin = 2;
volatile int ISRCounter = 0;
int counter = 0;
long startTime = 0;
void setup()
{
pinMode(intPin, INPUT_PULLUP);
Serial.begin(9600);
attachInterrupt(digitalPinToInterrupt(intPin), debounceCount, FALLING);
}
void loop()
{
if (counter != ISRCounter)
{
counter = ISRCounter;
Serial.println(counter);
}
}
void debounceCount()
{
if (millis() - startTime > timeThreshold)
{
ISRCounter++;
startTime = millis();
}
}
A time of 100-200ms is correct for a button, but in other cases, we will need to adjust the time so that we eliminate the bounce but do not ignore two possible “true” nearby events.
In a real setup, the best approach is to use a combination of both systems, while correctly adjusting the capacitor value and software filter times to suit our system.
Download the code
All the code from this entry is available for download on Github.