← Back to Embedded C series
🔌
Applications
Applied · Prereq: lesson 20

21. Debouncing · Finite State Machine (FSM) Patterns

With peripherals learned, we move to firmware patterns. The first two appear in nearly every project — debouncing and finite state machines (FSM). A mechanical button's contacts bounce for a few ms on press (chatter), so one press reads as many. Debouncing filters the jitter to keep only real input; an FSM expresses behavior cleanly with "state+input→next state". It's pure logic with no hardware, verified exactly with gcc.

debouncechatterFSMstate machineintegratormodule
Duration
~1.5 hours
Level
📊 Intermediate
Prerequisite
🎯 Lesson 20
OUTCOME
Build an integrator (consecutive-sample-count) debouncer as a module and drive an enum+switch FSM from confirmed debounced edges, separating input cleanup from decision logic.

What you'll learn

  • 1Explain the cause and symptoms of button chatter (bounce)
  • 2Implement an integrator (consecutive-count) debouncer
  • 3Split the debouncer into a reusable·testable module
  • 4Understand an FSM's parts (states·inputs·transitions)
  • 5Drive an FSM from confirmed debounced edges

Introduction

A button's metal contacts micro-bounce on contact, so one press is electrically 0→1→0→1… for a few ms. Read raw, it counts as many. This lesson is pure, hardware-independent logic — a debouncer module (debounce.c/.h) with an LED-mode FSM on top.

Key concepts

1) Integrator debouncer

Sample the pin at a fixed period (e.g. SysTick 1–5ms), and change state only when a value differing from the stable state repeats THRESHOLD times. Any return to the original resets the counter, filtering glitches (non-blocking·robust).

c
uint8_t debounce_update(debounce_t *db, uint8_t raw) {
    uint8_t r = raw ? 1u : 0u;
    if (r == db->stable) { db->count = 0u; return 0u; }  /* no jitter */
    if (++db->count >= DEBOUNCE_THRESHOLD) {             /* N in a row */
        db->stable = r; db->count = 0u; return 1u;       /* confirmed change */
    }
    return 0u;
}

2) Finite state machine (FSM)

Current stateInput (press confirmed)Next state
OFFpressSLOW
SLOWpressFAST
FASTpressOFF

In C, implement with enum + switch. Explicit states make debugging·extension easy.

💡

The FSM takes only the "press edge" confirmed by the debouncer. Separating input cleanup (debounce) from decision (FSM) keeps code clean. On a board, just feed raw = (GPIOA->IDR >> pin) & 1 from a periodic interrupt.

Core example

c
/* noisy raw stream → debounce → cycle OFF→SLOW→FAST on each confirmed press edge */
typedef enum { MODE_OFF=0, MODE_SLOW=1, MODE_FAST=2 } led_mode_t;
for (i = 0; i < n; i++) {
    if (debounce_update(&db, raw[i]) && debounce_state(&db) == 1u) {
        mode = (led_mode_t)((mode + 1) % 3);   /* advance only on confirmed edge */
    }
}

Verify: a short glitch (1 sample) is ignored and the mode changes only on a real press (THRESHOLD in a row). Build: gcc pc_test.c debounce.c -o pc_test.

Common mistakes

Q. Even after debouncing it sometimes double-presses.

A. THRESHOLD is too small or the sample period too fast. Aim for sample period × THRESHOLD = 10–30ms (e.g. 5ms × 3 = 15ms).

Q. The mode keeps changing while I hold the button.

A. Act on the "changed-to-pressed edge", not the "pressed state". Check both debounce_update's return (changed) and that the new state is 1.

Q. My if-else FSM gets tangled.

A. Make states an enum and gather transitions in a switch. Scattered state across the code is hard to trace.

Summary

  • Chatter is contact bounce — one press reads as many
  • The integrator debouncer confirms only after N consecutive equal values, filtering glitches
  • A debouncer module is reusable·testable on any chip
  • An FSM expresses behavior with states·inputs·transitions via enum+switch
  • Separating input cleanup (debounce) from decision (FSM) keeps code clean

Exercises

  1. Vary THRESHOLD to 2/5 and observe how glitch filtering changes
  2. Add a long-press state to the FSM
  3. Debounce two buttons independently to drive separate FSMs
Example code / lecture materials

All lecture materials and example code (with homework and answers) are openly available on GitHub.

View on GitHub ↗