Digital Signal Debouncing Guide: RC + Schmitt, Software Debounce and Noise Immunity

Digital Signal Debouncing Guide

RC + Schmitt, Software Debounce, Timer/SysTick, State Machines & Noise Immunity

1. Introduction — Why a Pressed Button Looks Like a Thousand Presses

A mechanical switch does not close cleanly. Its contacts bounce for hundreds of microseconds to several milliseconds, each bounce producing a fast burst of edges that a fast digital input samples as a train of presses. The same is true of relay contacts, connector insertions, and any mechanical contact. A debouncing design — RC filtering in hardware, software verification in firmware, or a combination — turns those noisy edges into exactly one clean transition, without adding perceptible latency and without missing genuinely fast events. This guide covers the physics of contact bounce, the RC + Schmitt trigger hardware debounce and its time-constant math, the software debounce patterns (sampling, counting, timer-based, state machine), the selection of the debounce time, the interaction with interrupt-driven inputs, the noise/EMI dimension, and a complete worked example with code. It complements the digital logic design and state machine guides on this site.

2. The Physics of Contact Bounce

When contacts meet, the impact energy rebounds them open and closed repeatedly until the mechanical energy dissipates. Typical bounce times: small tact switches 1–5 ms, larger panel buttons 1–10 ms, relays 1–15 ms, toggle switches 1–3 ms, and mechanical encoders far worse on their detent contacts. A small signal relay’s datasheet specifies a “bounce time” parameter (often 1–5 ms) explicitly, while a tact switch does not — make a measured assumption (scope the contact with a pull-up and a 1 MΩ load) rather than guess. Additionally, at the edges the contact resistance is not zero: the first nanoseconds of a close can present hundreds of ohms with a small capacitance, producing a sub-threshold excursion that a CMOS input may or may not register as an edge. The takeaway: the input is not a single edge but an oscillation of finite duration and amplitude — the design must accept the whole envelope as one event.

3. Hardware Debounce — RC + Schmitt

The classic analog debounce is a resistor-capacitor low-pass followed by a Schmitt-trigger input (a 74HC14, or an MCU pin with the input hysteresis enabled and a proper threshold). The R-C sets the time constant:

τ = R · C; settling to ~63% per τ, one full "bounce window" ≈ 3–5·τ

With a 10 kΩ pull-up and a 1 µF capacitor, τ = 10 ms, and the contact must be stable for ~30–50 ms — too slow for a button but fine for a slow signal. For a 5 ms debounce budget pick τ ≈ 1.5 ms: R = 10 kΩ and C = 150 nF gives τ = 1.5 ms and a 3τ settling under 5 ms. The Schmitt’s hysteresis prevents the slow RC edge from re-triggering the input during the transition. The critical constraint: the R-C must not be so slow that the input never reaches the logic threshold within the desired window, and the resistor must not load the driving stage — for a mechanical contact the pull-up is the only current source, and the pull-up sizing itself uses the same ohms-law arithmetic as any resistor network.

Switch bounce also causes a small charge injection; on CMOS inputs the absence of a pull-up (floating input) turns the high-impedance node into an antenna and any nearby switching (a motor, a relay coil) can inject a false edge — always give a monotonically defined level with a pull-up or pull-down, sized with the pull-up resistor calculator logic (the same rise-time and current trade-off applies), and check the RC values with the Ohm’s law calculator to hold the settling time inside the debounce budget.

4. Software Debounce Patterns

Pattern How it works Latency Cost
Sample & delay Read once, wait T, read again; both equal → accept ≥ T Lowest
Counting (integrator) Sample every Δt; accept after N equal samples N·Δt Low, robust
Timer-reset On any edge, restart a debounce timer; accept only if no edge until it expires ≈ T Medium
Edge + hold Detect edge, latch state; ignore further edges for T ≈ T Low
State machine IDLE → DEBOUNCING → PRESSED → RELEASED with transitions timed Tuning-free Higher code

The counting/integrator method is the workhorse: call it every T/N (or from a SysTick ISR), track the number of consecutive identical samples, and when the count reaches N, publish the stable state. It rejects both bounce and a single noise glitch without any timer reset gymnastics. The timer-reset method is elegant for interrupt-driven inputs: the first edge starts a timer, any new edge restarts it, and a full quiet period of T finally accepts the level — this is the pattern that maps naturally onto a hardware timer / watchdog-like peripheral. A finite state machine with explicit states handles the press/release/press-and-hold semantics cleanly (and composes with a long-press feature), which is the approach that the state-machine design guide recommends for anything beyond a single button.

5. Choosing the Debounce Time

The debounce time is a trade of robustness against latency:

  • Push buttons (human): 10–50 ms is invisible to the user; 5 ms is often enough and 20 ms is the safe default.
  • Rotary encoders: the detent bounce can be several milliseconds; debounce the A/B edges with 1–5 ms and decode by quadrature state change (never by counting individual edges from a bouncing line).
  • Keyboard-style arrays: 10–20 ms because the scan cycle interacts with the bounce; suppress with per-key state plus a scan-period multiple.
  • Relay / high-power contacts: use the datasheet bounce time × 2 (e.g. 5 ms spec → 10–15 ms window), since the mechanical energy is larger.
  • Fast signals / counters: software debounce adds latency you cannot afford; use a hardware Schmitt + RC with a time constant matched to the interference rather than the contact.

An important subtlety: debounce time is not a “filter” for legitimate fast toggling. If the input genuinely toggles faster than the debounce window, the design will merge real events into one. Set the window from the measured bounce, not from a habit.

6. Interrupts vs Polling for Inputs

An edge-triggered EXTI interrupt on a mechanical input is a trap: the bounce generates an interrupt storm (dozens of interrupts in a few milliseconds) and the ISR overhead can starve the rest of the system. Two robust strategies: (a) poll the input from a periodic timer at 1–5 ms and run the counting debounce (no interrupts, deterministic load); or (b) keep the interrupt but on the first edge disable the EXTI line, start a debounce timer, and re-enable only after the window has expired — the “one interrupt per physical event” pattern. For wake-from-low-power inputs the second pattern is required (you must wake on the edge), but the ISR should do the minimum: note the event, start the timer, and let the main loop or a task read the debounced level later.

7. Worked Example — 8-Button Panel, 5 ms Bounce, Zero Missed Presses

Target: an industrial panel with 8 mechanical buttons, each with 5 ms measured bounce, sampled by a 1 ms SysTick, 20 ms accept window, no user-perceptible delay, low CPU.

  • Measure first: scope one button with a 10 kΩ pull-up: bounce observed over ~4 ms → choose N = 20 samples at 1 ms (20 ms) for margin on the worst button.
  • Firmware: a per-button structure {level, count, stable}; in the 1 ms SysTick ISR read the port once (all 8 bits in one read), compare each bit to the last stable state, and increment/decrement the per-button counter; when the counter reaches 20, publish the new stable level and reset the counter. Cost: ~40 bytes and a few hundred cycles per tick.
  • No per-button EXTI: polling at 1 ms is well below the bounce edge rate and keeps the interrupt load deterministic. Report CPU: 8 compares + 8 counters on a 48 MHz M0+ ≈ 1–2 µs per tick = 0.2% CPU.
  • Latency check: worst-case perception 20 ms + one scan — invisible to the human, and the press is never missed because the counter integrates through the bounce.
  • Noise: with the pull-up (e.g. 10 kΩ) and a 100 nF capacitor at the pin, verify with a scope that a nearby relay switching no longer induces a spike above the CMOS threshold; the RC corner must be ≥ 10× the interference frequency. If the panel runs near motor cables, the hardware RC is the first line of defense and the software integrator the second.
  • Encoder alternative: if a rotary encoder shares the panel, decode it by reading the A/B pair each 1 ms and stepping a quadrature state machine — never count edges from the raw pins.

8. Common Mistakes

  • Trusting a single read: “if (PIN & MASK)” without delay or counting — the classic double/triple-count bug on every physical press.
  • Debouncing with a blocking delay: a delay(20) inside the main loop stalls everything else; use a non-blocking counter or timer.
  • Float input: no pull-up/pull-down — the pin picks up noise and any adjacent switching looks like a press.
  • Edge EXTI without masking: the bounce storm eats CPU and can overflow event queues; mask after the first edge.
  • Debounce time chosen by habit: too long merges real rapid presses; too short passes bounce. Measure the actual contact.
  • Debouncing every input identically: a fast sensor and a slow button need different windows; parameterize per input.

9. FAQ

Q: Do I need both an RC and software debounce? A: If the environment is noisy or the cable is long, yes — the RC removes the fast energy and the software confirms the level; in a quiet design one of the two often suffices for a button.

Q: What value should the RC have? A: τ = R·C sized so 3–5·τ covers the measured bounce, e.g. 10 kΩ × 150 nF = 1.5 ms; keep the input’s leakage and the pull-up current in check, and never let τ exceed the intended response time.

Q: Can I debounce in the EXTI ISR? A: Only the minimal action (mask, start timer, flag); doing the settling inside the ISR serializes the bounce as interrupt load and risks missing other events.

Q: How do I handle a “press and hold”? A: Debounce the transition edge first, then an independent long-press timer from the stable-pressed state — the two functions must not share the same counter logic.

10. Conclusion

Debouncing is a small piece of firmware that decides whether a product feels solid or flaky. Measure the bounce, size the RC and the software window to it, integrate with a counting or timer-reset pattern rather than a single read, and never let an EXTI storm into the core. Do that and one physical press produces exactly one logical event — the foundation of every keypad, panel and control on the machine.

发表评论