UNIT 07 · LESSON 4 OF 6

Debouncing Buttons and Switches

How do you turn that burst into exactly one event, and what does it cost?

INTERACTIVEDebouncing in software
A bouncing switch signal and the debounced outputrawoutput020406080100ms1 press event and 1 release event for one real press; the press was reported 8 msafter the first contactDefer: robust against bounce and noise, at the cost of waiting the debounce time.
A bouncing switch signal and the debounced outputrawoutput020406080100ms1 press event and 1 release event for one realpress; the press was reported 8 ms after the firstcontactDefer: robust against bounce and noise, at the costof waiting the debounce time.

Try this

Algorithm
defer: 1 press and 1 release events, press reported after 8 ms.

The raw contact bounces for a few milliseconds on press and release. The input is sampled every millisecond. With no debouncing every bounce is an event. A defer algorithm reports a change only after the input has been stable for the debounce time; an eager one reports the first edge at once and then ignores the input for the debounce time. Names follow QMK’s documentation; its default debounce time is 5 ms.

What you will be able to do
  • Implement a time-based defer debounce and predict its latency for a given bounce and debounce time.
  • Compare defer and eager debouncing in latency and noise immunity, and choose one for an application.
  • Explain why interrupting on every edge of a bouncing input is a problem and how to use interrupts safely.
  • Size an RC debounce filter for a Schmitt-trigger input from the bounce duration.
  • Extend a debounced button into press, release and long-press events.
Before you start
  • Switch bounce and Schmitt-trigger inputs (unit 1, lessons 2 and 5).
  • Reading inputs and pulls (lesson 3).
Steps in this lesson
  1. Sample, then decide
  2. A defer debounce in C
  3. Interrupts on every edge are a trap
  4. Debouncing in hardware
  5. Worked example: latency of a defer debounce
  6. Common misconceptions

The puzzle

A counter on the screen should go up by one per press. Sometimes it jumps by three. The switch is fine: every mechanical contact bounces for a few milliseconds as the metal settles (unit 1, lesson 5), and a fast microcontroller sees every bounce as a separate press. How do you turn that burst into exactly one event, and what does it cost?

STEP 1

Sample, then decide

The robust approach is to stop reacting to edges and instead look at the input at a steady rate, for example every millisecond, then decide what the switch is doing from its recent history. Two families of decision rule exist:

  • Defer. Report a change only after the input has stayed at the new level for the debounce time. Bounces reset the wait. Robust against bounce and against short noise spikes, but every event is delayed by at least the debounce time.
  • Eager. Report the first change immediately, then ignore the input for the debounce time. No delay, but a single noise spike is reported as a press.

↑ This step uses the figure at the top of the page.

For eager debouncing the debounce time must outlast the whole bounce; for defer it must outlast the longest quiet gap inside the bounce, which is shorter, but a margin is wise either way; 5 ms is a common default (QMK’s keyboards and Linux’s gpio-keys driver both use it), while Arduino’s teaching example uses a generous 50 ms. Measure your switch: in Ganssle’s tests of 18 switches the bounce averaged about 1.6 ms (two outliers excluded), the worst of the rest reached 6.2 ms, and one outlier bounced for 157 ms on opening.

STEP 2

A defer debounce in C

The version with timestamps, called every millisecond or simply every pass of the main loop:

typedef struct { bool stable, last_raw; uint32_t changed_ms; } button_t;

bool button_update(button_t *b, bool raw, uint32_t now_ms) {   /* true on a new stable state */
    if (raw != b->last_raw) { b->last_raw = raw; b->changed_ms = now_ms; }
    if (raw != b->stable && (uint32_t)(now_ms - b->changed_ms) >= DEBOUNCE_MS) {
        b->stable = raw;
        return true;
    }
    return false;
}

Timestamps make the debounce time independent of how often the function is called (the call interval must still be well below the debounce time, or the decision rests on only a sample or two), and the unsigned subtraction survives counter wrap as long as the millisecond counter wraps at 2³² (unit 6, lesson 6). Initialise stable and last_raw from a first reading of the pin, or an idle-high button reports a spurious change at start-up. Long press and double click are the same idea one level up: timestamps of stable-state changes.

STEP 3

Interrupts on every edge are a trap

Configuring an edge interrupt and counting in the handler looks efficient, but a bouncing switch can fire dozens of interrupts in a few milliseconds, each one taking CPU time and pushing other interrupts later, and the handler still has to debounce. Better patterns: use the interrupt only to wake the chip or to start the sampling, then disable it until the debounced state has settled; or simply sample from a periodic timer tick.

STEP 4

Debouncing in hardware

An RC low-pass filter in front of a Schmitt-trigger input can remove bounce before the firmware sees it. While a contact bounce is open for a moment, the capacitor charges only partway; if it never reaches the upper threshold, the input never changes. The Schmitt trigger matters: a slowly moving voltage on a plain input would itself cause several transitions.

INTERACTIVEDebouncing with an RC filter
An RC-filtered input charging during a contact bounce, against the input threshold3.30VT+ = 1.8 Vcontact closes againτ = RC = 1 ms; reaching 1.8 V takes τ · ln(3.3 / 1.5) = 788 µsafter a 0.50 ms opening the input reaches 1.30 V: it stays below the threshold, so thebounce is filtered outThe same delay applies to a genuine release: the filter adds about that much latencyto every edge.
An RC-filtered input charging during a contact bounce, against the input threshold3.30VT+ = 1.8 Vcontact closes againτ = RC = 1 ms; reaching 1.8 V takes τ · ln(3.3 /1.5) = 788 µsafter a 0.50 ms opening the input reaches 1.30 V: itstays below the threshold, so the bounce is filteredoutThe same delay applies to a genuine release: thefilter adds about that much latency to every edge.
R
C
τ = 1 ms; a 0.50 ms bounce reaches 1.30 V (filtered).

A resistor and capacitor slow the input so that brief bounces cannot swing it across the threshold of a Schmitt-trigger input. While the button is held the capacitor is discharged; when a bounce opens the contact for a moment the capacitor starts charging through R. The input only changes if it reaches the upper threshold VT+ (1.8 V here, illustrative) before the contact closes again.

The filter also delays the genuine release edge by about the same time (the closing edge discharges the capacitor through the contact almost at once), and that discharge must not exceed the contact’s rating; a small series resistor helps and also slows the closing edge.

STEP 5

Worked example: latency of a defer debounce

A switch bounces for 3 ms; the loop samples every 1 ms with a 5 ms defer debounce. The last bounce ends 3 ms after the first contact, and the state is accepted 5 ms after that:

treport≈tbounce+tdebounce=3+5=8 mst_{\text{report}} \approx t_{\text{bounce}} + t_{\text{debounce}} = 3 + 5 = 8\ \text{ms}

rounded to the sampling grid. For a user, 8 ms is imperceptible. For a switch that detects the end of a mechanical travel, it may matter, which is where eager debouncing, or an eager press with a deferred release (QMK’s asym_eager_defer_pk), is used.

MYTHS AND FACTS

Common misconceptions

A good switch doesn’t bounce

Every mechanical contact bounces; good ones bounce for less time.

Debouncing is just a delay after the edge

Reporting the first edge and then ignoring the input (the eager method) does not filter noise, and neither does waiting a fixed time and reading once; a defer debounce requires the level to be stable.

An interrupt per edge is the efficient way to read a button

It multiplies interrupts by the number of bounces and still needs debouncing.

A capacitor across the switch is enough

Without a Schmitt-trigger input the slow edge can cause multiple transitions, and discharging a bare capacitor through the contacts can damage them.

Check yourself

Answer in your head, then open the card.

A defer debounce of 10 ms samples every 2 ms. A press bounces for 4 ms. When is the press reported?

About 4 + 10 = 14 ms after first contact, rounded up to the next 2 ms sample.

An industrial input has occasional 1 ms noise spikes but no switch. Defer or eager?

Defer: an eager algorithm would report each spike as an event, while a defer debounce longer than the spikes ignores them.

With R = 10 kΩ and C = 100 nF, how long does the capacitor take to charge from 0 V to 1.8 V at 3.3 V?

τ = 1 ms; t = τ · ln(3.3 / (3.3 − 1.8)) = 1 ms × ln 2.2 ≈ 0.79 ms, taking the figure's illustrative 1.8 V threshold (use your chip's specified Schmitt threshold). Bounce openings shorter than that are filtered.

Why does the C function compare (uint32_t)(now_ms − changed_ms) rather than now_ms > changed_ms + DEBOUNCE_MS?

The subtraction of unsigned values gives the right elapsed time even when the millisecond counter wraps; the addition can overflow and make the comparison wrong around the wrap.

Sources (4)
  1. QMK Firmware documentation, docs/feature_debounce_type.md — defer: “When DEBOUNCE milliseconds of no changes has occurred … changes are pushed … noise-resistant”; eager: “any key change is reported immediately. All further inputs for DEBOUNCE ms are ignored … not noise-resistant”; timestamp-based is usually superior to cycle-based; default debounce time 5 ms
  2. Arduino, arduino-examples, examples/02.Digital/Debounce/Debounce.ino — a timestamp defer debounce: lastDebounceTime is reset on every change of the raw reading, and the state is accepted when millis() − lastDebounceTime > debounceDelay (50 ms)
  3. Linux kernel, Documentation/devicetree/bindings/input/gpio-keys.yaml — debounce-interval: “Debouncing interval time in milliseconds. If not specified defaults to 5.”
  4. Jack Ganssle, “A Guide to Debouncing”, Part 1 (August 2004, rev. 2 April 2007) — measurements on 18 switches: excluding two outliers the average bounce was 1557 µs with a maximum of 6200 µs; one switch bounced 157 ms on opening (as cited in unit 1, lesson 5)