Archive for June, 2018

Designing an RC debounce circuit

June 26, 2018 2 comments

While working on a project earlier this year which involved the monitoring the state of a number of switches, I had to find a way to deal with the mechanical bouncing of these switches. I found that despite my initial assumption that this would be easy to find detailed information on, I failed to find any clear guides or tutorials. At least as far as hardware-based debouncing methods went.

Though I was aware of software-based switch debounce algorithms, I decided against using those, on account of them adding complexity to the code, taking away system timers from the pool and the extra burden this would put on testing and debugging the software design. Instead I opted to use an RC circuit-based solution. Without easy tutorials being available, in the end I simply copied the complete design from someone else, because it seemed to work for that purpose.


The RC circuit

When debouncing switches in hardware, it matters which type of switch we are debouncing. The switches which I had to debounce for the project were SPST (Single-Pole, Single-Throw, with one output), meaning one has just a single signal wire to work with. This means that the delay created by an RC network charging or discharging is used to smooth out the erratic signal from a mechanical switch opening and closing.

With an SPDT (Single-Pole, Double-Throw) switch, one can use the same RC circuit, use an AND gate-based debounce circuit (not covered in this article), or use a hardware-based timer circuit.

The RC debounce circuit we’ll be looking at in this article is the following:

The way that this circuit works is that the capacitor (C1) is charged over R1 whenever the switch is in the open position, using the diode (D1) to bypass R2. This results in a logical ‘1’ being achieved after the delay created by the resistance value of R1.

When the switch closes, it discharges C1 over R2, with the latter’s value determining the delay, resulting in a logical ‘0’ being achieved. The output of the RC circuit is connected to U1, which is an inverse Schmitt trigger. This creates the expected logical ‘1’ when the switch is closed, and ‘0’ when it has been opened.

In addition to this, the Schmitt trigger (a CD40106 hex inverting Schmitt trigger IC) also adds hysteresis, essentially adding two trigger points to its output (which we connect to the input pin of our processor), which smooths out any remaining ripple by not switching to the opposite value immediately, but only after reaching the trigger point. This effectively creates a dead zone in between the logical values where any analogue noise has no effect.


Understanding the circuit

When I decided to use this project as a practical example for my upcoming book on embedded C++ development [1], I realised that I needed to dive a bit deeper into the how and why of these circuits, especially on how to calculate the appropriate values for the RC circuit.

Fundamental to RC networks is the RC time constant, or τ (tau), which is defined as:

\tau = { R C }

This time constant is defined in seconds, with one τ being the time it takes for the capacitor to charge up to 63.2%, and 2τ to 86%. For discharging the capacitor, 1τ would discharge it to 37%, and 13.5% after 2τ. This shows that a capacitor is never fully charged or discharged, but has the process simply slow down. Of relevance for us here is that 1τ roughly corresponds to the charge level required to reach the opposite logical output level and thus the effective delay we get for a specific RC value.

In addition, we can calculate the voltage of the capacitor at any given time, when charging and discharging respectively:

V(s) = { V_s (1 - e^{ -(t/RC) } ) }

V(s) = { V_s e^{-(t/RC) }}

Here t is the elapsed time in seconds, V_s the source voltage and e the mathematical constant (approximately 2.71828), also known as Euler’s number.


Running the numbers

For the earlier given circuit diagram, we can take its values and use the RC time constant to calculate the delay we achieve and thus what length of switch bounce we can compensate for. We’ll first look at the charging time (51k Ohm, 1 uF), then the discharging time (22k Ohm, 1 uF):

0.051 = { 51000 \cdot 0.000001 }

0.022 = { 22000 \cdot 0.000001 }

With the used RC values we achieve 51 milliseconds for charging (switch opening) and 22 milliseconds for discharging (switch closing). As 20 ms is a common bounce time for mechanical switches, the used values seem therefore reasonable. For any practical applications we would need to use the actual bounce time of the used switches to pick the appropriate values, however.



When it all comes down to it, designing an RC debounce circuit isn’t incredibly complex once one understands the principles and physics behind it. Using the RC time constant, it is a matter of picking an appropriate capacitor value, then sizing the resistors to reach the required charge and discharge times. The used Schmitt trigger IC isn’t terribly crucial, and can even be omitted in favour of an SoC’s built-in hysteresis.

The project which led me to research this topic resulted in me designing an entire debounce HAT [2] for the Raspberry Pi series of Single Board Computers (SBC):

This design uses all six Schmitt triggers in the CD40106 IC, allowing for up to six switches or equivalent to be connected. The integrated EEPROM allows the board to be automatically configured by the OS installed on the SBC by reading out the GPIO pins it is connected to, setting the appropriate direction and modes.

Naturally the RC values for this design can be altered at will to fit the requirements, so long as they fit the 0805 footprint.