임베디드 면접 용어 사전
인터럽트

NVIC

Cortex-M의 중첩 벡터 인터럽트 컨트롤러. 우선순위 그룹·선점·펜딩을 관리한다.

Cortex-M에 내장된 인터럽트 컨트롤러(Nested Vectored Interrupt Controller). 이름 그대로 중첩(Nested)벡터(Vectored) 를 지원한다.

이름의 세 요소

  • Nested — : 우선순위가 높은 인터럽트가 낮은 것을 중단시킬 수 있다 Vectored : 인터럽트마다 핸들러 주소가 정해져 있어 바로 점프

    • (하나의 핸들러에서 원인을 찾는 방식이 아니다) Interrupt Controller : 활성화·우선순위·대기 상태를 관리

Vectored의 이점 — 원인을 판별하는 코드가 필요 없어 지연이 짧다. AVR 등 일부 MCU는 공통 핸들러에서 원인을 조사해야 한다.

주요 레지스터

NVIC_EnableIRQ(USART1_IRQn);              // ISER — 활성화
NVIC_DisableIRQ(USART1_IRQn);             // ICER
NVIC_SetPriority(USART1_IRQn, 5);         // IPR  — 우선순위
NVIC_ClearPendingIRQ(USART1_IRQn);        // ICPR — 대기 취소
NVIC_GetPendingIRQ(USART1_IRQn);          // ISPR

우선순위 — 숫자가 작을수록 높다

0 = 가장 높음 15 = 가장 낮음 (4비트 구현 기준)

직관과 반대라 자주 헷갈린다. "1순위, 2순위"처럼 생각하면 된다.

우선순위 그룹 — 면접에서 자주 묻는다

우선순위 비트를 선점(preempt) 우선순위부(sub) 우선순위로 나눈다.

NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_2);

4비트를 2:2 로 분할
┌───────────┬──────────┐
│ 선점 2비트│ 부 2비트 │
└───────────┴──────────┘
  0~3       0~3
역할
선점 우선순위다른 인터럽트를 중단시킬 수 있는가를 결정
부 우선순위선점 우선순위가 같을 때 누가 먼저 처리될지

선점 우선순위가 같으면 → 중첩되지 않는다. 순서대로 처리 선점 우선순위가 다르면 → 높은 쪽이 낮은 쪽을 중단시킨다

부 우선순위는 중첩을 만들지 못한다. 이미 실행 중인 것을 끊지는 못하고, 대기 중인 것들 사이의 순서만 정한다.

실무 설계 그룹 4 (선점 4비트, 부 0비트) → 모든 인터럽트가 서로 선점 가능 그룹 0 (선점 0비트, 부 4비트) → 중첩 없음. 순서만 관리

STM32 HAL은 기본적으로 그룹 4를 쓴다.

중첩의 위험 — 스택

  • 우선순위가 다른 인터럽트 5개가 겹치면
  • 자동 스태킹 32바이트 × 5 = 160바이트
    • 각 핸들러의 지역 변수

MSP 크기를 정할 때 최악의 중첩을 고려해야 한다. 중첩을 원하지 않으면 우선순위를 모두 같게 두면 된다.

인터럽트 마스킹

__disable_irq();   // PRIMASK = 1 — 모든 인터럽트 차단 (NMI, HardFault 제외)
__enable_irq();

__set_BASEPRI(0x40);   // 이 값보다 낮은 우선순위만 차단   ★
__set_BASEPRI(0);      // 해제

★ BASEPRI가 실무에서 중요하다. 전체를 막으면 긴급한 안전 인터럽트까지 막히지만, BASEPRI를 쓰면 일반 인터럽트만 막고 최우선 인터럽트는 살려 둘 수 있다.

FreeRTOS가 이 방식을 쓴다 — configMAX_SYSCALL_INTERRUPT_PRIORITY보다 높은 우선순위의 ISR은 커널이 절대 막지 않으며, 대신 그 ISR에서는 RTOS API를 부르면 안 된다.

대기(pending) 상태 — 놓치기 쉬운 함정

인터럽트가 발생했지만 아직 처리되지 않은 상태 = pending

  • 인터럽트를 비활성화해도 pending 플래그는 남는다
  • 다시 활성화하는 순간 즉시 발생한다
NVIC_DisableIRQ(EXTI0_IRQn);
// … 설정 변경 …
NVIC_ClearPendingIRQ(EXTI0_IRQn);   // ★ 이걸 안 하면 즉시 튄다
NVIC_EnableIRQ(EXTI0_IRQn);

부트로더에서 앱으로 점프할 때 이 정리를 빠뜨려 앱이 시작하자마자 없는 핸들러로 가는 사고가 흔하다.

플래그 클리어 순서 — 무한 인터럽트의 원인

void EXTI0_IRQHandler(void) {
    handle();
    EXTI->PR = EXTI_PR_PR0;      // ★ 주변장치 플래그를 반드시 지운다
}

주변장치의 플래그를 안 지우면 핸들러를 나가자마자 다시 진입해 무한 루프에 빠진다. "인터럽트가 계속 발생한다"의 원인 1위다.

더 미묘한 문제 — 플래그를 지운 직후 바로 리턴하면, 쓰기가 버퍼에 남아 있어 CPU가 아직 플래그가 있다고 보고 재진입할 수 있다.

EXTI->PR = EXTI_PR_PR0;
__DSB();                          // 쓰기 완료를 보장

드물지만 고속 인터럽트에서 실제로 발생한다.

함께 보면 좋은 용어

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