[초급] 폴링 vs 인터럽트, 버튼을 톡 치면 왜 가끔 안 먹을까?
폴링은 MCU가 정해진 때마다 직접 핀을 보러 가는 방식이라, 메인 루프 한 바퀴보다 짧게 왔다 간 신호는 놓칠 수 있습니다. 인터럽트는 신호가 오는 순간 하드웨어가 하던 일을 잠깐 멈추고 담당 함수부터 실행하게 하는 방식이라 짧은 신호도 놓치지 않아요.
먼저 알아둘 용어
| 용어 | 원래 이름 | 쉽게 말하면 |
|---|---|---|
| MCU | Micro Controller Unit | 계산·기억·입출력을 칩 하나에 담은 작은 컴퓨터예요 |
| 메인 루프 | Main Loop | while문 안에서 할 일을 한 바퀴씩 계속 도는 평소 업무 목록이에요 |
| 폴링 | Polling | 정해진 때마다 직접 가서 확인하기, 현관문을 주기적으로 열어 보는 거예요 |
| 인터럽트 | Interrupt | 일이 생기면 하드웨어가 하던 프로그램을 잠깐 멈춰 세우는 초인종이에요 |
| 인터럽트 플래그 | Interrupt Flag | 벨이 울렸다는 표시등, 끌 때까지 켜져 있어요 |
| ISR | Interrupt Service Routine | 벨이 울리면 실행되는 담당 함수예요 |
이런 일 겪어본 적 있나요?
버튼을 꾹 누르면 잘 되는데, 톡 치면 열 번에 몇 번은 반응이 없습니다. 화면 갱신 코드를 넣어 메인 루프 한 바퀴가 100ms로 늘어난 뒤부터예요. 톡 친 버튼은 40ms만 눌려 있다가 떨어지니, 루프가 핀을 보러 왔을 땐 이미 손을 뗀 뒤인 거죠.
폴링과 인터럽트, 왜 알아야 할까요?
통신 데이터, 모터 회전 신호, 비상 정지처럼 한 번 놓치면 끝인 신호는 현장에 흔합니다. 반대로 천천히 변하는 값까지 전부 인터럽트로 받으면 코드만 복잡해지고요. 두 방식의 차이를 알아야 신호마다 알맞은 방법을 고를 수 있습니다.
폴링 vs 인터럽트, 택배로 한 번에 이해하기
택배 기사님이 문 앞에서 잠깐만 기다리고 떠난다고 해 볼게요. 정해진 시간마다 현관문을 열어 보는 게 폴링, 초인종이 울리면 나가는 게 인터럽트입니다. 벨이 울리면 하드웨어가 “울렸음” 표시를 켜 두는데, 이걸 인터럽트 플래그라고 불러요. 그래서 신호가 짧아도 기록이 남습니다. 벨 소리를 듣고 달려가서 하는 일, 즉 담당 함수가 ISR이에요.
▼ 루프가 핀을 보는 순간 · 초록 점: 신호 감지 · 빈 원: 봤는데 신호 없음 · 주황 점: 벨이 울린 순간
그림 1. 같은 신호 5개를 두 방식으로 받는 모습이에요. 위의 폴링은 ▼ 순간에 신호가 없으면 그냥 지나치고, 아래 인터럽트는 신호가 오자마자 잡습니다. 숫자를 바꾸면 애니메이션도 바뀌어요.
폴링은 얼마나 놓칠까? [예상]
신호가 머무는 동안 루프가 핀을 한 번은 봐야 잡을 수 있습니다. 신호가 언제 올지 모른다면 말로는 이렇게 돼요.
사례(100ms, 40ms)는 1 − 40 ÷ 100 = 0.6, 100번 중 60번을 놓친다는 예상입니다. 인터럽트는 0번이 예상이고요.
정말 그럴까? 폴링과 인터럽트로 100번 세어 보기
다른 보드로 40ms짜리 신호를 무작위 간격(최소 300ms)으로 100번 보내고 두 방식으로 세었습니다. 진짜 기계식 버튼은 누르는 순간 접점이 떨려 여러 번 눌린 것처럼 보일 수 있어서, 깨끗한 신호로 시험했어요.
// Polling: look at the pin once per loop
int pressCount = 0;
bool wasPressed = false;
while (true)
{
DoOtherWork(); // takes about 100 ms
bool isPressed = (ReadPin(BUTTON_PIN) == PRESSED);
if (isPressed && !wasPressed)
{
pressCount++;
}
wasPressed = isPressed;
}
// Interrupt: the hardware calls Button_ISR the moment the button is pressed
volatile int pressCount = 0; // shared with the ISR
ISR Button_ISR()
{
ClearInterruptFlag(BUTTON_PIN); // turn the bell light off
pressCount++; // keep it short
}
main()
{
EnablePinInterrupt(BUTTON_PIN, ON_PRESS);
while (true)
{
DoOtherWork(); // no need to watch the pin
}
}
이름 뜻: ReadPin은 핀 읽기, ClearInterruptFlag는 인터럽트 플래그 끄기, EnablePinInterrupt는 버튼을 누를 때 벨이 울리도록 켜기예요. 함수 이름은 쓰는 MCU에 맞게 바꾸면 됩니다.
[결과] 예상이 맞았을까? 폴링 vs 인터럽트
신호 100번 기준. 빗금 막대는 [예상], 빨간 막대는 [결과]예요. [결과]는 사례 조건(100ms, 40ms)일 때만 보여줍니다.
폴링은 57번을 놓쳐 예상 60번과 거의 같았어요. 3번 차이는 계산이 틀린 게 아니라, 60% 확률로 일어나는 일을 100번 시험할 때 흔한 흔들림입니다(보통 ±5번, √(100 × 0.6 × 0.4) ≈ 4.9). 인터럽트는 100번 모두 잡았습니다. 결론은 간단해요. 루프 한 바퀴보다 짧게 머무는 신호는 인터럽트로 받습니다.
현장 팁
FAQ: 폴링 vs 인터럽트
Q. 그럼 입력은 전부 인터럽트로 받으면 되나요?
아니요. 온도처럼 천천히 변하거나 한 번 놓쳐도 다음에 읽으면 되는 값은 폴링이 더 단순하고 흐름을 따라가기도 쉽습니다. 짧게 왔다 가거나 절대 놓치면 안 되는 신호만 인터럽트로 받으면 돼요.
Q. 폴링을 더 자주 하면 되지 않나요?
메인 루프 한 바퀴를 신호보다 짧게 만들 수 있다면 폴링으로도 잡힙니다(위 애니메이션에서 놓칠 확률 0%). 다만 루프 안에 무거운 일이 있으면 줄이기 어렵고, 자주 볼수록 MCU 시간이 확인에만 쓰여요.
정리
'임베디드 실무' 카테고리의 다른 글
| MISRA-C 규칙, 왜 이렇게 까다로울까? 양산 전 코딩 규칙 입문 [초급] (1) | 2026.10.01 |
|---|---|
| 디버거는 어떻게 코드를 한 줄씩 따라갈까 [초급] (0) | 2026.09.29 |
| [초급] 풀다운이 더 직관적인데, 스위치엔 왜 풀업 저항을 쓸까? (0) | 2026.09.27 |
| [초급] 차를 세워 뒀을 뿐인데 왜 배터리가 방전될까? 제어기 슬립(Sleep) 모드가 필요한 이유 (0) | 2026.09.26 |
| [초급] 워치독은 왜 필요할까? 아무도 리셋 버튼을 눌러 줄 수 없을 때 (0) | 2026.09.23 |
