← Embedded C 강의 목록으로
🔌
STM32
STM32 · 선수: 17강

18. NVIC 인터럽트와 EXTI

버튼을 폴링으로 읽으면 CPU가 묶이고 짧은 입력을 놓칠 수 있습니다. 인터럽트는 평소 메인이 제 일을 하다가 사건이 생기면 즉시 핸들러(ISR)로 점프했다 돌아옵니다. Cortex-M에서 인터럽트를 총괄하는 NVIC와, 외부 핀 변화를 인터럽트로 바꾸는 EXTI를 다룹니다. PA0 버튼을 EXTI0 인터럽트로 받아 LED를 토글하고, ISR에서 펜딩 플래그(PR)를 반드시 클리어하는 패턴을 익힙니다.

인터럽트NVICEXTIAFIOEXTICR펜딩
소요 시간
약 1.5~2시간
난이도
📊 중급
선수 조건
🎯 17강
결과물
AFIO->EXTICR로 핀을 EXTI 라인에 매핑하고 RTSR/IMR·NVIC_EnableIRQ로 인터럽트를 켜며, 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을 써서 클리어
c
NVIC_EnableIRQ(EXTI0_IRQn);   /* CMSIS: NVIC에서 인터럽트 활성화 */

2) AFIO->EXTICR — 핀→라인 매핑

c
/* 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과 펜딩 클리어

c
void EXTI0_IRQHandler(void) {
    if (EXTI->PR & EXTI_PR_PR0) {
        EXTI->PR = EXTI_PR_PR0;        /* 1 을 써야 클리어! 안 하면 무한 재진입 */
        GPIOC->ODR ^= (1u << 13);
    }
}
⚠️

핸들러 이름은 startup 벡터 테이블과 일치해야 합니다(EXTI0_IRQHandler). 펜딩(PR)을 클리어하지 않으면 핸들러를 나가자마자 다시 같은 인터럽트가 걸립니다.

핵심 예제

c
#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)을 반드시 클리어하고 핸들러는 짧게 유지한다

과제

  1. FTSR로 하강 에지 트리거로 바꿔 동작 차이를 관찰
  2. PB0(EXTICR 매핑을 PB로) 버튼으로 EXTI0을 설정해 보기
  3. ISR에서는 volatile 플래그만 세우고 메인 루프에서 LED를 처리하도록 리팩터링
예제 코드 / 강의 자료

전체 강의 자료와 예제 코드(과제·정답 포함)는 GitHub에서 자유롭게 받아볼 수 있습니다.

GitHub에서 보기 ↗