Event가 발생했을때, 처리하고 복귀하는 매커니즘.
인터럽트 Vector 주소로 이동하여 코드를 실행한다.
ISR(인터럽트 서비스 루틴)
메모리의 맨 앞부분에 주소가 고정되어 있다.
주소가 낮을수록 우선순위가 높다.
Reset>Int0>Int1>...
리셋포함 총 35개
요청-주 프로그램 중단-상태저장-ISR실행-상태복원-복귀
#include <avr/interrupt.h> 헤더파일 필요
ISR(벡터이름){} 형식임.
전역 인터럽트 활성화 sei()
반대는 cli()
#include <avr/io.h>
#include <avr/interrupt.h>
#define LED_PIN PB5 // 아두이노 우노 내장 LED (D13)
#define BTN_PIN PD2 // 외부 인터럽트 0 (D2)
volatile int state = 0; // 현재 LED의 상태
void int0_init(void) {
// 1. PD2 핀을 입력으로 설정 및 내부 풀업 저항 활성화
DDRD &= ~(1 << BTN_PIN);
PORTD |= (1 << BTN_PIN);
// 2. 인터럽트 감지 조건 설정 (EICRA 레지스터)
// ISC01 = 1, ISC00 = 0 : 하강 에지(Falling Edge) 감지
// 스위치를 눌러 전압이 GND로 떨어지는 순간을 포착함
EICRA |= (1 << ISC01); // 1번 비트를 1로
EICRA &= ~(1 << ISC00); // 0번 비트를 0으로
// 3. INT0 외부 인터럽트 허용 (EIMSK 레지스터)
EIMSK |= (1 << INT0);
// 4. 전역 인터럽트 활성화
sei();
}
// INT0 외부 인터럽트 서비스 루틴(ISR)
ISR(INT0_vect) {
// LED 상태 반전 (Toggle)
PORTB ^= (1 << LED_PIN);
}
int main(void) {
// LED 핀을 출력으로 설정
DDRB |= (1 << LED_PIN);
// 인터럽트 초기화
int0_init();
while(1) {
// 메인 루프는 비워둠.
// CPU는 다른 작업을 하다가 버튼이 눌리면 즉각 ISR을 실행함.
}
return 0;
}
평소엔 while문(아무것도 동작 안하는 무한루프)를 돌다가, ISR로 이동하면 LED상태를 토글시킨다.
우선순위부터 처리하고, 나머지는 대기(pending상태임)
기존 코드에서, 버튼 디바운싱 부분 추가. Timer0이용(8bit)
#include <avr/io.h>
#include <avr/interrupt.h>
#define LED_PIN PB5 // 아두이노 내장 LED (D13)
#define BTN_PIN PD2 // 외부 인터럽트 0 (D2)
volatile int debounce_count = 0; // 전역 변수 추가
void init_hardware(void) {
// 1. 입출력 핀 설정 (D2 풀업 저항 활성화)
DDRB |= (1 << LED_PIN);
DDRD &= ~(1 << BTN_PIN);
PORTD |= (1 << BTN_PIN);
// 2. INT0 외부 인터럽트 설정 (하강 에지)
EICRA |= (1 << ISC01);
EICRA &= ~(1 << ISC00);
EIMSK |= (1 << INT0);
// 3. Timer0 초기화 (아직 타이머는 가동하지 않음)
TCCR0A = 0x00; // 일반(Normal) 모드
TCCR0B = 0x00; // 타이머 정지 상태
sei(); // 전역 인터럽트 활성화
}
// [핵심 1] INT0 외부 인터럽트 (버튼이 눌리는 순간 1회만 실행됨)
ISR(INT0_vect) {
// 1. 바운싱으로 인한 중복 실행을 막기 위해 INT0 감지 임시 차단
EIMSK &= ~(1 << INT0);
// 2. 실제 원하는 작업 수행
PORTB ^= (1 << LED_PIN);
// 3. 디바운싱 타이머(Timer0) 가동 설정
TCNT0 = 0; // 타이머 카운트 0으로 초기화
TIFR0 |= (1 << TOV0); // 이전 오버플로우 플래그 강제 지움
TIMSK0 |= (1 << TOIE0); // Timer0 오버플로우 인터럽트 켜기
// 프리스케일러 1024 적용하여 타이머 시작
// (16MHz / 1024 = 15625Hz. 256번 카운트 시 약 16.38ms 경과)
TCCR0B = (1 << CS02) | (1 << CS00);
}
// [핵심 2] Timer0 오버플로우 인터럽트 (약 16.38ms 후 스위치 안정화 시점)
ISR(TIMER0_OVF_vect) {
// 1. 타이머 정지 및 인터럽트 끄기 (1회용 지연 역할 완료)
TCCR0B = 0x00;
TIMSK0 &= ~(1 << TOIE0);
// 2. 대기하는 16ms 동안 발생했던 노이즈(바운싱) 인터럽트 플래그 강제 삭제
EIFR |= (1 << INTF0);
// 3. INT0 외부 인터럽트 다시 켜기 (새로운 버튼 클릭 받을 준비 완료)
EIMSK |= (1 << INT0);
}
int main(void) {
init_hardware();
while(1) {
// 메인 루프는 완전히 비어 있습니다.
// 딜레이 없이 100%의 CPU 성능을 다른 기능(UART, 센서 읽기 등)에 쏟을 수 있습니다.
}
return 0;
}
delay는 함수는 가급적 지양하는 것이 좋음