ISR에서 delay를 쓰면 안 되는 이유

카드뉴스

ISR 안의 delay는 시스템 흐름을 붙잡을 수 있습니다

인터럽트 서비스 루틴, 즉 ISR은 이벤트가 발생했을 때 CPU가 잠깐 들어가 필요한 처리를 하고 다시 원래 흐름으로 돌아가는 공간입니다. 버튼 입력, Timer 주기, UART 수신, ADC 완료처럼 즉시 반응해야 하는 상황에서 사용되지만, 그만큼 짧게 끝나는 구조가 중요합니다. ISR 안에 delay를 넣으면 CPU가 그 시간 동안 인터럽트 안에 머물게 되고, main loop나 다른 처리 흐름이 밀릴 수 있습니다.

짧은 delay처럼 보여도 반복되면 문제가 커집니다. 버튼 인터럽트가 여러 번 발생하거나 Timer 인터럽트가 주기적으로 들어오는 상황에서 ISR이 오래 걸리면 다음 이벤트의 처리 시점이 늦어질 수 있습니다. 모든 MCU에서 항상 같은 방식으로 막힌다고 단정할 수는 없지만, interrupt latency와 jitter가 커지는 방향으로 작용하는 것은 분명히 주의해야 합니다.

HAL_Delay 같은 틱 기반 delay는 특히 조심해야 합니다

STM32에서 자주 보는 `HAL_Delay()` 같은 함수는 보통 시스템 tick 값이 증가하는 흐름에 의존합니다. 그런데 ISR 안에서 delay를 호출하면 tick을 갱신하는 인터럽트 우선순위나 현재 실행 중인 인터럽트 상태에 따라 예상과 다르게 오래 기다리거나 빠져나오지 못하는 문제가 생길 수 있습니다. 그래서 ISR 내부에서 틱 기반 delay를 쓰는 코드는 디버깅하기 어려운 정지 현상으로 이어질 수 있습니다.

문제는 delay만이 아닙니다. 긴 반복문, printf 출력, blocking UART 송신, I2C나 SPI 대기 함수, 동적 메모리 할당, 시간이 오래 걸리는 계산도 ISR 안에서는 부담이 됩니다. 인터럽트는 이벤트를 처리하는 출입구에 가깝고, 오래 걸리는 작업을 수행하는 작업 공간으로 쓰기에는 적합하지 않습니다.

ISR에서는 표시만 하고 실제 처리는 밖으로 넘깁니다

안정적인 구조는 ISR에서 필요한 최소 정보만 남기는 것입니다. 버튼이 눌린 시각을 저장하거나, 수신된 바이트를 빠르게 버퍼에 넣거나, 플래그를 세우거나, RTOS를 쓴다면 queue나 task notification으로 작업을 넘길 수 있습니다. 실제 디바운싱 판단, 문자열 파싱, 센서 계산, 통신 응답은 main loop나 task에서 처리하는 편이 전체 시스템 반응을 예측하기 쉽습니다.

버튼 바운스를 잡기 위해 ISR 안에서 10ms를 기다리는 방식도 피하는 것이 좋습니다. 대신 인터럽트에서는 이벤트 발생 시간만 기록하고, Timer 기반 주기 처리나 main loop에서 일정 시간이 지난 뒤 상태를 다시 읽어 유효 입력으로 확정할 수 있습니다. ISR 설계의 목표는 모든 일을 그 자리에서 끝내는 것이 아니라, 중요한 이벤트를 놓치지 않으면서 시스템 전체가 계속 움직이게 만드는 것입니다.

문제를 확인할 때는 ISR 실행 시간을 측정합니다

인터럽트 문제가 의심될 때는 감으로만 판단하지 말고 실행 시간을 재보는 것이 좋습니다. ISR 시작과 끝에서 GPIO를 토글해 오실로스코프로 폭을 보거나, 디버거의 cycle counter를 이용하면 어느 정도 시간이 걸리는지 확인할 수 있습니다. Timer 주기보다 ISR이 길거나, 여러 인터럽트가 겹칠 때 지연이 커진다면 구조를 나눠야 합니다. 짧은 ISR, 명확한 플래그, 밖에서 처리하는 흐름은 실시간성을 지키기 위한 기본 습관입니다.

댓글 남기기