18. NVIC 인터럽트와 EXTI
버튼을 폴링으로 읽으면 CPU가 묶이고 짧은 입력을 놓칠 수 있습니다. 인터럽트는 평소 메인이 제 일을 하다가 사건이 생기면 즉시 핸들러(ISR)로 점프했다 돌아옵니다. Cortex-M에서 인터럽트를 총괄하는 NVIC와, 외부 핀 변화를 인터럽트로 바꾸는 EXTI를 다룹니다. PA0 버튼을 EXTI0 인터럽트로 받아 LED를 토글하고, ISR에서 펜딩 플래그(PR)를 반드시 클리어하는 패턴을 익힙니다.
이 강의에서 배우는 것
- 1폴링과 인터럽트의 차이와 장단점을 설명한다
- 2NVIC의 역할과 NVIC_EnableIRQ()를 이해한다
- 3EXTI로 핀의 에지(상승/하강)를 인터럽트로 만든다
- 4AFIO->EXTICR로 핀을 EXTI 라인에 매핑한다
- 5ISR을 작성하고 펜딩 플래그(PR)를 올바르게 클리어한다
소개
NVIC(Nested Vectored Interrupt Controller)는 각 인터럽트의 활성화·우선순위·펜딩을 관리하고, EXTI(External Interrupt Controller)는 16개 라인(EXTI0~15)의 상승/하강 에지를 감지해 인터럽트를 냅니다.
핵심 개념
1) NVIC와 EXTI 레지스터
| 레지스터 | 역할 |
|---|---|
| EXTI->IMR | 인터럽트 마스크(라인 허용) |
| EXTI->RTSR/FTSR | 상승/하강 에지 트리거 |
| EXTI->PR | 펜딩 플래그 — 1을 써서 클리어 |
NVIC_EnableIRQ(EXTI0_IRQn); /* CMSIS: NVIC에서 인터럽트 활성화 */2) AFIO->EXTICR — 핀→라인 매핑
/* EXTI 라인 n 은 EXTICR[n/4] 의 (n%4)*4 위치 4비트. 0=PA, 1=PB, 2=PC... */
AFIO->EXTICR[0] &= ~AFIO_EXTICR1_EXTI0; /* EXTI0 ← PA0 */
/* 매핑에는 AFIO 클럭(RCC_APB2ENR_AFIOEN)이 필요하다 */3) ISR과 펜딩 클리어
void EXTI0_IRQHandler(void) {
if (EXTI->PR & EXTI_PR_PR0) {
EXTI->PR = EXTI_PR_PR0; /* 1 을 써야 클리어! 안 하면 무한 재진입 */
GPIOC->ODR ^= (1u << 13);
}
}핸들러 이름은 startup 벡터 테이블과 일치해야 합니다(EXTI0_IRQHandler). 펜딩(PR)을 클리어하지 않으면 핸들러를 나가자마자 다시 같은 인터럽트가 걸립니다.
핵심 예제
#include "stm32f10x.h"
void EXTI0_IRQHandler(void){
if (EXTI->PR & EXTI_PR_PR0) { EXTI->PR = EXTI_PR_PR0; GPIOC->ODR ^= (1u<<13); }
}
int main(void){
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPCEN | RCC_APB2ENR_AFIOEN;
GPIOA->CRL &= ~0xFu; GPIOA->CRL |= 0x8u; GPIOA->ODR &= ~1u; /* PA0 입력 풀다운 */
GPIOC->CRH &= ~(0xFu<<20); GPIOC->CRH |= (0x2u<<20); /* PC13 출력 */
AFIO->EXTICR[0] &= ~AFIO_EXTICR1_EXTI0; /* EXTI0 ← PA0 */
EXTI->RTSR |= EXTI_RTSR_TR0; /* 상승 에지 */
EXTI->IMR |= EXTI_IMR_MR0; /* 마스크 해제 */
NVIC_EnableIRQ(EXTI0_IRQn);
while (1) { /* 메인은 한가하다 */ }
}시뮬레이터 Command 창에서 `PORTA |= 0x0001`(PA0 상승 에지)을 실행하면 EXTI0_IRQHandler로 진입해 PC13이 토글됩니다.
자주 하는 실수
Q. 인터럽트가 한 번 걸리고 영원히 핸들러에서 못 나와요.
A. EXTI->PR을 클리어하지 않았습니다. PR은 1을 써야 클리어됩니다(EXTI->PR = EXTI_PR_PR0;). 빠뜨리면 나가자마자 재진입합니다.
Q. 핸들러가 아예 호출되지 않아요.
A. ① AFIO 클럭(AFIOEN), ② EXTI->IMR 마스크 해제, ③ NVIC_EnableIRQ, 그리고 핸들러 이름이 EXTI0_IRQHandler로 정확한지 확인하세요.
Q. ISR 안에서 긴 작업을 해도 되나요?
A. 안 됩니다. ISR은 짧게 유지하고, 긴 작업은 플래그만 세워 메인에서 처리하세요(22편 링버퍼에서 활용).
정리
- 인터럽트는 사건 발생 시 자동 호출돼 CPU를 효율적으로 쓴다
- NVIC가 인터럽트를 총괄하고 NVIC_EnableIRQ로 켠다
- EXTI는 핀 에지를 인터럽트로 바꾸며 IMR/RTSR/FTSR/PR로 제어한다
- 핀→라인 매핑은 AFIO->EXTICR(AFIO 클럭 필요)로 한다
- ISR에서 펜딩(PR)을 반드시 클리어하고 핸들러는 짧게 유지한다
과제
- FTSR로 하강 에지 트리거로 바꿔 동작 차이를 관찰
- PB0(EXTICR 매핑을 PB로) 버튼으로 EXTI0을 설정해 보기
- ISR에서는 volatile 플래그만 세우고 메인 루프에서 LED를 처리하도록 리팩터링