임베디드 시스템에서 외부 이벤트나 장치와의 상호작용을 처리하는 방식에는 인터럽트(Interrupt) 와 폴링(Polling) 이 있습니다. 두 방법 모두 주변 장치의 상태를 확인하고 적절한 작업을 수행하기 위한 기법이지만, 동작 방식과 효율성 측면에서 큰 차이가 있습니다. 이 글에서는 인터럽트와 폴링의 개념, 차이점, 장단점, 그리고 언제 어떤 방식을 선택해야 하는지에 대해 자세히 살펴보겠습니다.
STM32 CPU의 버튼 입력 프로그램으로 간단하게 예시를 들겠습니다.
1. 인터럽트(Interrupt)란?
인터럽트는 특정 이벤트가 발생했을 때, CPU의 현재 작업을 중단하고 해당 이벤트를 처리하는 방식입니다. 즉, 필요한 순간에만 CPU가 개입하는 효율적인 처리 방법입니다.
🔹 인터럽트의 동작 원리
- 주변 장치(예: 타이머, 센서, 버튼)에서 특정 이벤트가 발생하면, 인터럽트 신호를 CPU에 보냅니다.
- CPU는 현재 실행 중인 작업을 잠시 멈추고, 인터럽트 서비스 루틴(ISR, Interrupt Service Routine) 으로 이동하여 이벤트를 처리합니다.
- 이벤트 처리가 끝나면 원래 실행하던 작업으로 복귀합니다.
🔹 인터럽트의 예시
- 버튼 입력 처리: 사용자가 버튼을 누르면 인터럽트가 발생하여 버튼 입력을 처리합니다.
- 타이머 기반 작업: 주기적인 이벤트(예: 1초마다 센서 데이터 읽기)를 타이머 인터럽트로 실행할 수 있습니다.
- 직렬 통신(UART, SPI, I2C): 데이터가 수신되었을 때 인터럽트가 발생하여 즉시 처리됩니다.
※ 예시 : 버튼 입력을 인터럽트 방식으로 처리
#include "stm32f4xx.h"
#define BUTTON_PIN GPIO_PIN_0
#define LED_PIN GPIO_PIN_5
#define BUTTON_PORT GPIOA
#define LED_PORT GPIOA
void SystemClock_Config(void);
void GPIO_Init(void);
void EXTI0_IRQHandler(void);
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
while (1) {
// 인터럽트 방식이므로 메인 루프에서 다른 작업을 수행할 수 있음
HAL_Delay(1000);
HAL_GPIO_TogglePin(LED_PORT, LED_PIN); // LED를 1초마다 토글
}
}
// GPIO 및 인터럽트 초기화
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_SYSCFG_CLK_ENABLE(); // 시스템 구성 클럭 활성화
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 버튼 핀을 인터럽트 모드로 설정
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
// LED 핀 설정 (출력 모드)
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(LED_PORT, &GPIO_InitStruct);
// 인터럽트 활성화
HAL_NVIC_SetPriority(EXTI0_IRQn, 2, 0);
HAL_NVIC_EnableIRQ(EXTI0_IRQn);
}
// 버튼 인터럽트 핸들러
void EXTI0_IRQHandler(void) {
if (__HAL_GPIO_EXTI_GET_IT(BUTTON_PIN) != RESET) {
__HAL_GPIO_EXTI_CLEAR_IT(BUTTON_PIN);
HAL_GPIO_TogglePin(LED_PORT, LED_PIN); // 버튼이 눌리면 LED 토글
}
}
🔹 인터럽트의 장점
✅ CPU가 불필요한 대기 시간을 가지지 않고, 중요한 작업에 집중할 수 있습니다.
✅ 이벤트가 발생한 즉시 반응하므로, 실시간 처리가 가능합니다.
✅ 전력 소모가 줄어들어 배터리 기반 시스템에서 유리합니다.
🔹 인터럽트의 단점
❌ 인터럽트가 너무 자주 발생하면, CPU가 여러 번 중단되어 성능 저하가 발생할 수 있습니다.
❌ 다중 인터럽트가 동시에 발생할 경우, 우선순위 설정이 필요하며 복잡도가 증가합니다.
2. 폴링(Polling)이란?
폴링은 CPU가 주기적으로 주변 장치의 상태를 확인하여 이벤트가 발생했는지 검사하는 방식입니다. 즉, CPU가 직접 반복적으로 확인하는 방식입니다.
🔹 폴링의 동작 원리
- CPU는 메인 루프에서 일정한 주기로 주변 장치의 상태를 읽습니다.
- 특정 이벤트가 감지되면 해당 처리를 수행합니다.
- 이벤트가 없으면 다시 루프를 반복합니다.
🔹 폴링의 예시
- 키보드 입력 확인: CPU가 주기적으로 키보드 입력을 확인하여 입력이 있을 때만 처리합니다.
- 센서 데이터 읽기: 온도 센서 데이터를 1초마다 읽고 처리하는 경우.
- LED 상태 확인 및 변경: 특정 조건이 만족되었을 때 LED를 ON/OFF하는 경우.
※ 예시 : 버튼 입력을 폴링 방식으로 처리
#include "stm32f4xx.h"
#define BUTTON_PIN GPIO_PIN_0 // 예제: 버튼이 GPIOA의 0번 핀에 연결됨
#define LED_PIN GPIO_PIN_5 // LED가 GPIOA의 5번 핀에 연결됨
void SystemClock_Config(void);
void GPIO_Init(void);
int main(void) {
HAL_Init();
SystemClock_Config();
GPIO_Init();
while (1) {
// 버튼 상태를 계속 검사 (폴링 방식)
if (HAL_GPIO_ReadPin(GPIOA, BUTTON_PIN) == GPIO_PIN_SET) {
// 버튼이 눌리면 LED 토글
HAL_GPIO_TogglePin(GPIOA, LED_PIN);
HAL_Delay(300); // 채터링 방지용 딜레이
}
}
}
// GPIO 초기화 함수
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE(); // GPIOA 클럭 활성화
GPIO_InitTypeDef GPIO_InitStruct = {0};
// 버튼 핀 설정 (입력 모드)
GPIO_InitStruct.Pin = BUTTON_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
// LED 핀 설정 (출력 모드)
GPIO_InitStruct.Pin = LED_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
🔹 폴링의 장점
✅ 코드가 상대적으로 단순하고, 디버깅이 쉽습니다.
✅ 예측 가능한 실행 흐름을 유지할 수 있어 동기적인 처리에 적합합니다.
✅ 인터럽트 우선순위나 중첩 인터럽트 같은 복잡한 구조를 신경 쓰지 않아도 됩니다.
🔹 폴링의 단점
❌ 이벤트가 발생하지 않아도 CPU가 계속해서 장치를 검사하므로 불필요한 연산이 많아지고 전력 소모가 증가합니다. ❌ 빠른 응답이 필요한 시스템에서는 이벤트를 놓칠 가능성이 있습니다.
❌ 다중 장치를 관리할 때 비효율적일 수 있습니다.
3. 인터럽트 vs. 폴링 – 차이점 정리
구분 | 인터럽트 (Interrupt) | 폴링 (Polling) |
CPU 활용 방식 | 이벤트 발생 시만 CPU가 개입 | CPU가 반복적으로 장치를 확인 |
응답 속도 | 빠름 (이벤트 발생 즉시 처리) | 상대적으로 느림 (다음 검사 주기까지 대기) |
전력 소비 | 낮음 (대기 상태 유지 가능) | 높음 (CPU가 계속 동작) |
구현 난이도 | 복잡함 (ISR, 우선순위 관리 필요) | 단순함 (루프 내에서 상태 확인) |
사용 사례 | 실시간 응답이 중요한 경우 | 간단한 상태 모니터링 |
4. 언제 어떤 방식을 선택해야 할까?
✅ 인터럽트가 적합한 경우
- 빠른 응답이 필요한 실시간 시스템
- 전력 소모를 줄여야 하는 배터리 기반 시스템
- 이벤트 발생 빈도가 낮고 예측할 수 없는 경우
- 직렬 통신(UART, SPI, I2C) 및 타이머 기반 처리
예제:
- 버튼이 눌렸을 때 즉시 동작해야 하는 경우
- 네트워크 패킷을 수신했을 때 빠르게 처리해야 하는 경우
- 저전력 웨어러블 기기에서 센서 데이터를 읽을 때
✅ 폴링이 적합한 경우
- 주기적으로 데이터를 확인해야 하는 경우
- 간단한 시스템에서 인터럽트 처리 복잡도를 줄이고 싶은 경우
- 이벤트 발생 빈도가 높아 인터럽트 사용이 오히려 비효율적인 경우
예제:
- 1초마다 온도 센서 데이터를 읽고 디스플레이에 표시하는 경우
- 단순한 LED 상태 제어(특정 주기마다 변경)
- 키보드 입력을 반복적으로 확인하는 경우
5. 결론
인터럽트와 폴링은 각각의 장점과 단점을 가지고 있으며, 시스템의 요구 사항에 따라 적절한 방법을 선택해야 합니다. 실시간 응답이 중요하고 전력 효율성이 필요한 경우에는 인터럽트를 사용하는 것이 적합하며, 단순한 루프 기반 제어나 일정한 주기로 데이터를 확인하는 경우에는 폴링이 유리할 수 있습니다.
실제 개발에서는 두 가지 방식을 적절히 혼합하여 사용하는 경우도 많습니다. 예를 들어, 중요한 이벤트는 인터럽트를 사용하고, 덜 중요한 상태 확인은 폴링을 적용하는 방식이 가능합니다. 적절한 기법을 선택하여 최적의 성능을 내는 것이 중요합니다.
인터럽트와 폴링에 대한 질문이 있다면 언제든지 댓글로 남겨 주세요!😊
'Embedded Program' 카테고리의 다른 글
임베디드 시스템에서의 UART, SPI, I2C 통신 프로토콜 (0) | 2025.02.06 |
---|---|
임베디드 시스템에서 효율적인 전력 관리 방법 (0) | 2025.02.04 |
마이크로컨트롤러(MCU) 선택 가이드 - 어떤 칩이 나에게 맞을까? (0) | 2025.02.03 |
펌웨어 (Firmware) 란? (2) | 2025.02.03 |
STM32 Flash Program (Write) (0) | 2024.04.11 |