임베디드 면접 용어 사전
인터럽트debouncing · 채터링

디바운싱

기계 접점이 튀며 생기는 중복 입력을 시간·다수결로 걸러내는 처리.

기계식 스위치가 접점에서 여러 번 튀는 현상(bounce) 을 걸러내는 처리.

왜 생기는가

버튼을 누르면 금속 접점이 물리적으로 진동한다

이상적:  ─────┐
              └─────────

실제:    ─────┐ ┌┐ ┌─┐
              └─┘└─┘ └────    ← 1~20ms 동안 수십 번 튄다

금속이 부딪히며 튀는 물리 현상이라 소프트웨어로 없앨 수 없고, 걸러내야 한다.

무엇이 문제인가

버튼 한 번 눌렀는데
→ 인터럽트가 20번 발생
→ 카운터가 20 증가
→ 메뉴가 20칸 이동

인터럽트 방식일수록 심각하다. 폴링은 주기가 길면 자연스럽게 걸러지기도 하지만, 인터럽트는 모든 에지를 잡는다.

소프트웨어 해결 ① — 시간 기반 (가장 흔함)

#define DEBOUNCE_MS 30

volatile uint32_t last_tick = 0;

void EXTI0_IRQHandler(void) {
    uint32_t now = HAL_GetTick();
    if (now - last_tick > DEBOUNCE_MS) {
        last_tick = now;
        button_pressed = 1;            // 유효한 입력으로 처리
    }
    EXTI->PR = EXTI_PR_PR0;            // 플래그는 항상 지운다
}

첫 에지를 받아들이고 이후 30ms를 무시한다. 응답이 즉각적이면서 바운스를 걸러 낸다.

now - last_tick 으로 비교하는 이유 — 부호 없는 뺄셈은 오버플로가 나도 올바른 차이를 준다. now > last_tick + 30 으로 쓰면 49일 뒤 틱 오버플로에서 오작동한다.

소프트웨어 해결 ② — 샘플링 + 안정성 확인

// 1ms 타이머 인터럽트에서 호출
void debounce_poll(void) {
    static uint8_t history = 0;
    history = (history << 1) | read_pin();

    if (history == 0xFF) state = PRESSED;    // 8회 연속 High
    if (history == 0x00) state = RELEASED;   // 8회 연속 Low
}

"연속 N회 같은 값이어야 인정" 하는 방식이다. 시프트 레지스터 하나로 구현되어 매우 가볍고, 노이즈에도 강하다. 여러 버튼을 다룰 때 특히 깔끔하다.

소프트웨어 해결 ③ — 상태 머신

IDLE → (에지 감지) → DEBOUNCING → (타이머 만료 후 재확인) → PRESSED

  • (값이 다르면) → IDLE

길게 누르기, 더블 클릭까지 함께 처리해야 하면 상태 머신이 적합하다.

하드웨어 해결

  • ① RC 필터 — 저항 + 커패시터로 신호를 둔하게 만든다

    • R=10kΩ, C=100nF → 시정수 1ms
    • 가장 싸고 간단하지만 에지가 완만해져 슈미트 트리거 입력이 필요
  • ② 슈미트 트리거 — 히스테리시스로 중간 전압에서의 떨림 방지

    • 많은 MCU의 GPIO 입력에 내장되어 있다
  • ③ SR 래치 — SPDT(3단자) 스위치에 사용. 완벽하지만 부품이 늘어난다

실무에서는 RC 필터 + 소프트웨어 디바운싱을 함께 쓰는 경우가 많다. 하드웨어로 큰 노이즈를 줄이고 소프트웨어로 마무리한다.

시간 상수 선택

너무 짧으면(< 10ms) → 바운스를 못 거른다

  • 너무 길면(> 100ms) — → 사용자가 반응이 느리다고 느낀다 일반적으로 20~50ms

스위치 종류마다 다르다. 데이터시트에 바운스 시간이 명시되어 있으면 그것의 2배 정도로 잡는다. 택트 스위치는 보통 5~10ms, 릴레이는 더 길다.

릴리스도 디바운싱해야 한다

누를 때만 처리하고 뗄 때를 무시하는 실수가 흔하다. 뗄 때도 똑같이 튀므로, 릴리스 에지를 감지하는 로직이 있다면 동일하게 걸러야 한다.

인터럽트 vs 폴링 — 디바운싱 관점

인터럽트 : 즉각 반응. 대신 바운스마다 진입해 오버헤드

  • 첫 에지 후 인터럽트를 잠시 끄고 타이머로 재활성화하는 기법도 있다

폴링(주기 10~20ms) : 자연스럽게 디바운싱된다. 코드가 단순

  • 사람이 누르는 버튼에는 폴링이 더 적합한 경우가 많다

버튼처럼 사람이 조작하는 입력은 10ms 지연이 문제되지 않으므로 타이머 폴링이 오히려 좋은 선택이다. 이 판단을 말할 수 있으면 좋다.

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 인터럽트 — 벡터·우선순위·latency·재진입·ISR 금기·디바운싱