Search topics...
GPIOInterrupts and Debouncingfoundational

How do you debounce a mechanical switch in firmware? Describe at least two approaches.

0 upvotes
Practice with AISoon
Study the fundamentals first — GPIO topic page

Mechanical switches bounce for 1-20 ms after actuation, producing multiple rapid transitions as the metal contacts physically settle. Without debouncing, a single button press can register as dozens of events — a menu advances 15 steps, a counter increments 20 times, or a state machine enters an undefined state.

Timer-based debounce (recommended): On the first detected edge (via polling or interrupt), record the event and start a one-shot software timer for 20-50 ms. During this debounce window, ignore all subsequent edges on that pin. When the timer expires, read the pin's current state — this stable reading is the true state. If using interrupts, disable the EXTI for that pin during the debounce period to prevent ISR re-entry, and re-enable it when the timer fires. This approach is non-blocking, robust, and works well in both bare-metal and RTOS environments.

c
// Timer-based debounce in EXTI ISR + software timer callback
void EXTI0_IRQHandler(void) {
EXTI->PR = EXTI_PR_PR0; // Clear pending
NVIC_DisableIRQ(EXTI0_IRQn); // Ignore bounces
start_debounce_timer(20); // 20 ms one-shot
}
void debounce_timer_callback(void) {
bool stable = (GPIOA->IDR & 1); // Read settled state
process_button(stable);
NVIC_EnableIRQ(EXTI0_IRQn); // Re-arm interrupt
}

Counter-based (integrating) debounce: In a periodic task running every 5-10 ms, read the pin state. Increment a counter when the reading matches the proposed new state; reset it when the reading differs. Accept the new state only after N consecutive identical readings (e.g., 3-4 samples = 15-40 ms of stability). This approach fits naturally into RTOS tick handlers or super-loop architectures and requires no additional timers. The trade-off is slightly higher latency and the requirement for a periodic polling context.

Source: GPIO Q&A