10. 외부·타이머 인터럽트
폴링은 정확하지만 기다리는 동안 CPU가 묶입니다. 인터럽트는 평소 메인 코드가 제 일을 하다가 사건(버튼·타이머 오버플로)이 생기면 CPU가 잠깐 끼어들어 ISR을 실행하고 돌아옵니다. "사건이 생길 때만 반응"하는 이 방식이 임베디드의 핵심 패러다임입니다. IE 레지스터로 인터럽트를 켜고, C51의 interrupt N 문법으로 ISR을 쓰며, 공유 변수에 volatile이 왜 필요한지 익힙니다.
이 강의에서 배우는 것
- 1인터럽트(ISR·벡터·비동기 사건)를 폴링과 비교해 설명한다
- 2IE 레지스터(EA/EX0/ET0)로 인터럽트를 허용한다
- 3C51의 void isr(void) interrupt N 문법으로 ISR을 작성한다
- 4외부 인터럽트(INT0)와 타이머 인터럽트를 설정한다
- 5ISR↔메인 공유 변수에 volatile을 붙이는 이유를 안다
소개
사건이 발생하면 하드웨어가 자동으로 현재 코드를 멈추고 인터럽트 벡터의 함수(ISR)로 점프합니다. ISR이 끝나면 멈췄던 자리로 돌아옵니다. 폴링의 대기 낭비가 사라지고 반응이 즉각적입니다.
핵심 개념
1) 인터럽트 소스와 번호 / IE
| 번호 | 소스 |
|---|---|
| 0 | 외부 인터럽트 0 (INT0, P3.2) |
| 1 | 타이머 0 오버플로 |
| 2 | 외부 인터럽트 1 (INT1) |
| 3 | 타이머 1 오버플로 |
| 4 | 시리얼(UART) |
규칙: 개별 인터럽트를 켜고(EX0=1 등), 마지막에 전역 EA=1 을 켭니다. EA가 0이면 무엇을 켜도 동작하지 않습니다.
2) ISR 문법과 트리거 모드
void int0_isr(void) interrupt 0 /* 외부0 = 0, 타이머0 = 1 ... */
{
/* 짧고 빠르게! printf·지연 같은 무거운 작업은 피한다 */
}
/* IT0=0: 레벨 트리거(Low 동안 계속), IT0=1: 하강 에지(누르는 순간 한 번) */3) volatile — 공유 변수
volatile unsigned char press_count; /* ISR ↔ main 공유 */
/* ISR 이 비동기로 바꾸므로 매번 메모리에서 다시 읽게 해야 한다 */ISR은 짧게 유지하세요. 무거운 작업은 ISR에서 volatile 플래그만 세우고 메인 루프에서 처리합니다.
핵심 예제
#include <reg52.h>
sbit LED = P1 ^ 0;
volatile unsigned char press_count = 0;
void int0_isr(void) interrupt 0 { /* 하강 에지마다 호출 */
press_count++;
LED = (bit)!LED;
}
void main(void) {
IT0 = 1; /* 하강 에지 트리거 */
EX0 = 1; /* 외부0 허용 */
EA = 1; /* 전역 허용(마스터) */
while (1) { /* 메인은 자유 */ }
}시뮬레이터: Port 3에서 P3.2를 1→0으로 바꿀 때마다 int0_isr이 호출되어 P1.0 LED가 토글되고 press_count가 1씩 증가합니다.
자주 하는 실수
Q. ISR이 전혀 호출되지 않아요.
A. 십중팔구 EA=1(전역 허용)을 안 켰습니다. 개별 인에이블만으로는 부족합니다. interrupt N 번호가 소스와 맞는지도 확인하세요(외부0=0, 타이머0=1).
Q. ISR에서 바꾼 변수가 메인에서 안 바뀐 것처럼 보여요.
A. 공유 변수에 volatile 을 빠뜨렸습니다. 컴파일러가 메인에서 그 변수를 캐싱하면 ISR의 변경을 못 봅니다.
Q. 한 번 눌렀는데 ISR이 여러 번 불려요.
A. IT0=0(레벨)이면 누르는 동안 계속 트리거됩니다. IT0=1(에지)로 바꾸세요. 채터링이면 디바운싱(21편).
정리
- 인터럽트는 사건 발생 시에만 ISR을 실행해 폴링의 대기 낭비를 없앤다
- IE의 개별 비트(EX0/ET0)를 켜고 마지막에 EA=1로 전역 허용한다
- C51 ISR은 void isr(void) interrupt N (외부0=0, 타이머0=1…)
- 외부 인터럽트는 IT0로 레벨/에지 트리거를 고른다
- ISR↔메인 공유 변수엔 volatile, ISR은 짧게 유지한다
과제
- 타이머0 오버플로 인터럽트(interrupt 1)로 LED를 일정 주기로 토글하는 ISR 작성
- press_count 가 5가 될 때마다 메인에서 다른 LED를 토글(ISR은 카운트만)
- IT0를 0(레벨)으로 바꿔 보고 동작이 어떻게 달라지는지 관찰·설명