UART 수신 인터럽트가 긴 타이머 처리에 밀릴 때는 우선순위의 숫자, 구현 비트 수, 선점 그룹을 함께 확인해야 합니다. Cortex-M3·M4·M7에서 사용할 CMSIS 설정과 PC에서 실행할 계산 모델을 나누어 살펴봅니다. Arm의 NVIC 공식 문서는 작은 우선순위 숫자가 더 높은 우선순위라고 설명합니다.
논리 우선순위와 레지스터 바이트
디바이스 헤더의 __NVIC_PRIO_BITS가 4라면 논리 우선순위는 0~15입니다. 실제 우선순위 레지스터는 상위 4비트를 사용하므로 논리 값 6의 저장 바이트는 0x60입니다. CMSIS Cortex-M4 구현의 __NVIC_SetPriority는 이 이동을 수행합니다. 따라서 NVIC_SetPriority(UART_IRQn, 6)에 다시 이동한 0x60을 넣지 마세요. 이 예에서 0x60을 전달하면 하위 4비트가 0이어서 의도와 다른 최고 우선순위가 저장될 수 있습니다.
| 입력·관찰 위치 | 4비트 구현에서 값 | 의미 |
|---|---|---|
| CMSIS API 인자 | 6 | 논리 우선순위 |
| 레지스터 바이트 | 0x60 | 상위 비트에 배치한 값 |
| NVIC_GetPriority 반환 | 6 | 다시 논리 값으로 환산한 값 |
비트 수는 제품별 헤더와 데이터시트로 확인합니다. 같은 Cortex-M4라도 항상 4비트라고 가정하지 않습니다. NMI와 HardFault는 고정 우선순위이며 일반 IRQ처럼 설정할 수 없습니다.
PRIGROUP과 선점·하위 우선순위
선점 우선순위가 같은 IRQ끼리는 하위 우선순위 숫자만 다르다고 서로 선점하지 않습니다. 하위 우선순위는 같은 선점 그룹에서 동시에 대기 중인 인터럽트의 선택 순서를 정하는 데 사용합니다. 숫자 값만 보고 선점 관계를 판단하지 마세요. 여기서 그룹 번호는 AIRCR의 PRIGROUP 원시 값입니다. 벤더 HAL의 NVIC_PRIORITYGROUP_* 매크로 이름과 숫자를 혼동하지 않습니다.
CMSIS 계산을 정리하면 선점 비트 수는 min(__NVIC_PRIO_BITS, 7 - PRIGROUP)이고 나머지는 하위 우선순위 비트입니다. 아래 표는 4비트 구현을 가정한 결과입니다.
| PRIGROUP | 선점 비트 | 하위 비트 | 가능한 선점/하위 범위 |
|---|---|---|---|
| 0~3 | 4 | 0 | 0~15 / 0 |
| 4 | 3 | 1 | 0~7 / 0~1 |
| 5 | 2 | 2 | 0~3 / 0~3 |
| 6 | 1 | 3 | 0~1 / 0~7 |
| 7 | 0 | 4 | 0 / 0~15 |
실제 펌웨어에서는 초기화 단계에서 NVIC_SetPriorityGrouping(5)를 정한 후 NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 1, 2)의 반환 값 6을 NVIC_SetPriority에 전달합니다. 표의 계산은 CMSIS 인코딩·디코딩 함수와 대응합니다. 범위를 벗어난 입력은 CMSIS 내부 마스크로 잘릴 수 있으므로 호출 전에 검증하는 편이 좋습니다. Cortex-M0·M0+에는 이 그룹 설정 예제를 적용하지 않습니다.
C11로 4,032개 조합 검증하기
다음 코드는 MCU 레지스터에 접근하지 않는 계산 모델입니다. 그룹 값·입력 범위를 먼저 검사하고, 구현 비트 수 3~8과 PRIGROUP 0~7의 모든 유효 조합에서 인코딩 후 원래 선점·하위 값이 복원되는지 확인합니다. 잘못된 그룹, 비트 수, 우선순위도 거부합니다. nvic_priority.c로 저장해 gcc -std=c11 -Wall -Wextra -Werror nvic_priority.c -o nvic_priority로 빌드하세요. Linux에서는 ./nvic_priority, Windows에서는 ./nvic_priority.exe를 실행합니다.
검증 출력:
NVIC priority: 4032 round trips passed; group5 priority=6 register=0x60
이 결과는 계산과 입력 경계를 검증합니다. 실제 인터럽트 응답 시간, 마스킹, RTOS 임계 구역까지 실측한 결과는 아닙니다.
보드에서 확인할 순서
- 사용 중인 MCU 헤더의
__NVIC_PRIO_BITS를 확인합니다. - 시작 코드·HAL·RTOS가 마지막으로 설정한 그룹을
NVIC_GetPriorityGrouping()으로 읽습니다. 초기화 뒤 다른 코드가 그룹을 바꾸면 같은 숫자의 선점 의미도 바뀝니다. NVIC_GetPriority()와NVIC_DecodePriority()로 최종 값을 확인하고 설계한 선점 관계와 비교합니다.- GPIO 토글이나 타임스탬프로 ISR 시작 지연을 측정합니다. 긴 ISR, 전역 인터럽트 마스크, 같은 선점 그룹은 우선순위를 높여도 지연 원인이 될 수 있습니다.
- RTOS API를 호출하는 ISR은 해당 포트의 허용 우선순위 규칙을 별도로 적용합니다. 이 글의 논리 값과 RTOS 설정에 쓰는 이동된 레지스터 값을 구분하세요.
공식 자료 확인: 2026-10-11. 예제는 계산 모델을 실제 컴파일·실행해 확인했습니다.
댓글
첫 댓글을 남겨 보세요.