지노랩 /JinoLab
인터럽트 안에서 …FromISR() 버전을 꼭 써야 하는 이유 본문
FreeRTOS의 “ISR-Safe API” 완전 정복 가이드
1. 두 개의 세계, 두 개의 규칙
구분 | Task Context (Thread Mode) | Interrupt Context (Handler Mode) |
실행 시점 | 스케줄러가 선택한 뒤 CPU에서 실행 | 하드웨어 이벤트가 즉시 CPU를 점유 |
가능 동작 | 스케줄러에게 “나 잠시 Block 시켜” 요청 가능 (예: vTaskDelay(), xQueueReceive()) | Block 금지 — 인터럽트 루틴은 짧고 빠르게 끝나야 함 |
API 이름 | 일반 FreeRTOS API | …FromISR() 접미사 API |
핵심: 인터럽트 안에서는 “Block 동작”이 불가능하다.
따라서 동일 API를 공용으로 쓰면, 내부에서 “지금 Task냐 ISR이냐” 를 매번 분기해야 하고, 일부 파라미터가 무용지물이 된다.
2. …FromISR() 버전이 존재하는 3가지 이유
이유 | 상세 설명 |
① 구현 단순화 | Task 모드 전용 API는 “Block 또는 스케줄링” 로직만 포함ISR 전용 API는 “큐/세마포어 조작 + 컨텍스트 전환 요청”만 담당 |
② 불필요한 인자 제거 | xSemaphoreTake()는 time-out 파라미터 필요 → ISR에서는 쓸모없음xSemaphoreTakeFromISR()는 해당 인자 제거로 API 시그니처 간결 |
③ 아키텍처 의존성 제거 | 어떤 MCU는 “현재 모드가 ISR인지 확인” 기능이 없음 → API를 분리하면 포팅이 쉬워진다 |
3. 직접 써 보면 더 선명해지는 차이
(1) Task 전용 버전
void vSenderTask(void *arg)
{
for (;;)
{
xQueueSend(xLogQ, &data, portMAX_DELAY); // 큐가 꽉 차면 Block
}
}
(2) ISR 전용 버전
void USART1_IRQHandler(void)
{
BaseType_t xHigherWoken = pdFALSE;
uint8_t ch = USART1->DR; // 수신 바이트
xQueueSendFromISR(xLogQ, &ch, &xHigherWoken);
// 필요하다면 PendSV 트리거
portYIELD_FROM_ISR(xHigherWoken);
}
- portMAX_DELAY 같은 인자는 ISR 버전엔 존재하지 않는다.
- BaseType_t *pxHigherPriorityTaskWoken 인자는 ISR 종료 직전 컨텍스트 스위치 필요 여부를 알린다.
4. “3rd-party 코드가 API를 호출해 버리는데요?”
외부 라이브러리 함수가 일반 FreeRTOS API를 내부에서 사용한다면 ISR 안에서 직접 호출하면 안 된다.
해결 패턴
- ISR은 데이터만 큐/버퍼에 넣고 즉시 반환
- 전용 Task가 깨어나 라이브러리 함수를 호출해 후처리
void ADC_IRQHandler(void)
{
BaseType_t xHigherWoken = pdFALSE;
uint16_t raw = ADC1->DR;
xQueueSendFromISR(xAdcQ, &raw, &xHigherWoken);
portYIELD_FROM_ISR(xHigherWoken);
}
void vAdcTask(void *arg)
{
uint16_t raw;
for (;;)
{
xQueueReceive(xAdcQ, &raw, portMAX_DELAY); // Block 대기
ThirdParty_Filter(raw); // ISR-safe 아님 → 여기서 호출
}
}
5. 사용 시 체크리스트 ✅
- ISR에서 일반 API 호출 금지 (configASSERT()가 잡아낼 수도, HardFault가 날 수도!)
- BaseType_t xHigherPriorityTaskWoken 파라미터 NULL로 두지 말 것 – 컨텍스트 스위치 누락 위험
- configMAX_SYSCALL_INTERRUPT_PRIORITY 보다 우선순위가 낮은(숫자가 큰) ISR 에서만 호출
6. 결론
“인터럽트 안에서는 무조건 …FromISR() 버전만!”
이렇게 규칙을 분리하면 코드가 가독성·안정성·이식성 모두 좋아집니다.
ISR은 가볍게, Task는 여유 있게 — FreeRTOS가 의도한 ‘RTOS다운’ 구조를 그대로 살려보세요!
'임베디드 시스템 > RTOS' 카테고리의 다른 글
FreeRTOS Idle Hook로 ‘공짜’ 저전력 얻기 (0) | 2025.06.28 |
---|---|
FreeRTOS Hook Function (0) | 2025.06.28 |
FreeRTOS 인터럽트 우선순위 설정 제대로 이해하기 (2) | 2025.06.27 |
FreeRTOS 하드웨어 인터럽트 우선순위 정리 (2) | 2025.06.26 |
FreeRTOS Task 우선순위 vs. 하드웨어(인터럽트) 우선순위 (0) | 2025.06.26 |