그라운드 임베디드 EN 유튜브 구독

노트 / MCU 펌웨어

인터럽트와 NVIC: 우선순위와 짧은 처리

우선순위 그룹, 선점과 서브 우선순위, 처리 함수를 짧게 유지하는 이유, 공유 변수를 안전하게 다루는 법.

GROUND EMBEDDED · 정리 노트펌웨어 07인터럽트와 NVIC 1volatile uint32_t ticks; 2uint32_t get_ticks(void) { 3 __disable_irq(); uint32_t t = ticks…// 32… 4 return t;

NVIC 설정 세 가지

  1. 주변장치에서 인터럽트 허용 (예: USART1->CR1 |= RXNEIE)
  2. NVIC에서 그 인터럽트 번호 허용 (NVIC_EnableIRQ(USART1_IRQn))
  3. 우선순위 설정 (NVIC_SetPriority)

하나라도 빠지면 인터럽트가 들어오지 않습니다.

우선순위

main낮은 ISR높은 ISR
선점. 낮은 우선순위 핸들러가 도는 중에 더 높은 인터럽트가 오면 끊고 들어갔다가, 끝나면 돌아와 이어서 합니다.

STM32는 숫자가 작을수록 높은 우선순위입니다. 높은 인터럽트는 낮은 인터럽트 처리 중에도 끼어듭니다(선점).

인터럽트 우선순위 예
과전류 보호 0 (가장 높음)
전류 제어 ADC 완료 1
통신 수신 5
버튼 10

RTOS를 쓰면 RTOS API를 부르는 인터럽트는 configMAX_SYSCALL_INTERRUPT_PRIORITY보다 낮은 우선순위여야 합니다.

처리 함수 원칙

  • 플래그 세우기, 버퍼에 넣기, 레지스터 값 저장만 하고 나옵니다.
  • printf, HAL_Delay, 긴 계산 금지
  • 인터럽트 원인 플래그를 지웁니다

공유 변수

volatile uint32_t ticks;
uint32_t get_ticks(void) {
    __disable_irq(); uint32_t t = ticks; __enable_irq();   // 32비트는 원자적이지만 구조체라면 필수
    return t;
}

여러 바이트로 된 값이나 여러 변수를 한꺼번에 읽을 때는 잠깐 인터럽트를 끄거나 복사본을 씁니다.

인터럽트를 켜려면 세 곳을 모두 열어야 한다

  1. 주변장치: 어떤 사건에서 요청을 낼지 (예: USART1->CR1 |= USART_CR1_RXNEIE)
  2. NVIC: 그 요청 선을 받을지, 우선순위는 얼마인지
  3. 전역 허용: CPU가 인터럽트를 받는 상태인지
NVIC_SetPriority(USART1_IRQn, 2);
NVIC_EnableIRQ(USART1_IRQn);
USART1->CR1 |= USART_CR1_RXNEIE;

셋 중 하나라도 빠지면 핸들러가 불리지 않습니다. 순서는 우선순위를 먼저 정하고 나서 허용하는 쪽이 안전합니다.

3D 실험실여기까지 읽은 내용을 직접 돌려 볼 수 있습니다→

우선순위: 숫자가 작을수록 급하다

Cortex-M에서는 값이 작을수록 우선순위가 높습니다. 0이 가장 급합니다.

예시 우선순위 이유
과전류 보호 0 늦으면 부품이 탄다
모터 전류 제어 (PWM 주기) 1 주기가 흔들리면 제어가 무너진다
통신 수신 2 한 바이트 시간 안에만 읽으면 된다
시스템 틱 3 조금 늦어도 된다

높은 우선순위 인터럽트는 낮은 것의 핸들러를 중간에 끊고 들어옵니다(선점). 같은 우선순위끼리는 끊지 않고 차례를 기다립니다.

그림으로 보기: 선점

높은 IRQ낮은 IRQ메인낮음 시작높음 끼어듦낮음 계속메인 복귀
우선순위가 높은 인터럽트가 낮은 핸들러를 끊고 먼저 실행된 뒤, 낮은 쪽이 이어서 끝납니다.

임계 구역은 짧게

메인 루프와 핸들러가 같은 데이터를 다룬다면, 메인 쪽에서 그 데이터를 만지는 동안만 인터럽트를 막습니다.

uint32_t prim = __get_PRIMASK();
__disable_irq();
copy = shared_value;            // 몇 줄 이내
__set_PRIMASK(prim);            // 원래 상태로 복원

무조건 __enable_irq()로 푸는 대신 원래 상태를 복원하는 이유는, 이 코드가 이미 인터럽트가 꺼진 곳에서 불렸을 때 멋대로 켜 버리지 않기 위해서입니다.

현업에서 자주 터지는 문제

  • 우선순위 비트 수: 많은 MCU가 8비트 중 상위 4비트만 구현합니다. 직접 레지스터에 쓸 때는 자리를 맞춰야 하고, NVIC_SetPriority를 쓰면 알아서 처리됩니다.
  • RTOS와 함께 쓸 때: RTOS API를 부르는 인터럽트는 RTOS가 정한 값보다 우선순위가 낮아야(숫자가 커야) 합니다. 어기면 드물게 죽는 버그가 됩니다.
  • 핸들러 안의 긴 작업: 그동안 낮은 우선순위 인터럽트가 모두 밀려 UART 바이트를 놓칩니다.
  • 공유 변수의 volatile 누락: 최적화 빌드에서만 증상이 나타납니다.
3D 실험실에서 직접 돌려 보기값을 바꾸며 이 노트의 원리를 확인합니다→영상으로 이어 보기회로와 하드웨어 시리즈 8편전자제품 속 회로 소자는 어떻게 동작할까? · 저항 · 커패시터 · 인덕터 완벽 정리PROJECT COURSE레지스터 설정을 실제 보드에서 해 보고 싶다면밸런싱 로봇 하나를 요구사항, 회로와 PCB, 펌웨어, 앱까지 실무 순서대로 만드는 강의입니다. 10파트 165강.강의 살펴보기 →FREE PDF무료 전자책 3권 받기이 주제를 한 권으로 이어서 읽고 싶다면. BLDC 모터 제어, 직렬통신, 센서를 책 분량으로 묶었습니다. 이메일만 남기면 바로 받습니다.무료로 받기 →