인터럽트(Interrupt)란?
1. 개요
인터럽트는 프로그램 실행 도중 발생하는 이벤트로, 현재 실행 중인 코드의 흐름을 중단하고 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)으로 이동하여 처리할 수 있다.
2. 동작 방식
간단한 예시로, 집에서 집안의 일들을 하는 도중에 어떤 외부인이 초인종을 눌렀다고 생각해보자. 즉시 집안일을 관두고 외부인과 대응해야한다. 대응 이후에는 다시 마저 하던 일을 하게 될 것이다. 이것이 바로 인터럽트다. 이 인터럽트의 가장 큰 장점은 리얼 타임 시스템(Real-Time System)에서 빛을 발한다는 점이다. 시스템 가동 중에 예기치 못한 상황이 발생된다면, 인터럽트를 통해서 해당 상황을 즉각적으로 처리할 수 있다.
3. ATmega128A에서의 인터럽트
간단하게 인터럽트에 대해 소개했다. ATmega128A에서의 인터럽트는 어떻게 사용되는지 알아보자.
(1) ATmega128A 인터럽트의 구성 요소
- 외부 인터럽트 (External Interrupt)
- 타이머/카운터 인터럽트 (Timer/Counter Interrupt)
- USART 인터럽트 (USART Interrupt)
- SPI 인터럽트 (SPI Interrupt)
- ADC 변환 완료 인터럽트 (ADC Conversion Complete Interrupt)
- EEPROM 완료 인터럽트 (EEPROM Ready Interrupt)
- 인터럽트 0 ~ 7의 합성 인터럽트 (Combined Interrupt of Interrupt 0 to 7)
위와 같은 인터럽트들이 ATmega128 내,외부에서 발생되면 해당 인터럽트 서비스 루틴을 처리하는데, 여기서 인터럽트 서비스 루틴(Interrupt Service routine)이란, 인터럽트가 발생했을 때, 일을 처리하기 위한 하나의 함수라고 보면 되겠다.
(2) ATmega128A 인터럽트 사용 방법
인터럽트를 작성할 때 알아야 할 두 가지를 먼저 알아보자.
ISR(Interrupt Service routine) : GCC 컴파일러에서 인터럽트 서비스 루틴을 정의할 때 사용하는 매크로이다. 인터럽트 작성 시, 먼저 선언해야하는 매크로가 되겠다.
인터럽트 벡터(Interrupt Vector) : ISR을 선언하고 매개변수로 꼭 인터럽트 백터를 넣어주어야 한다. 매개변수로 넣을 각 인터럽트 벡터는 아래의 표를 참고하여 넣길 바란다.(띄어쓰기는 _로 대체)
ISR(TIMER0_OVF_vect) { //(4)의 타이머 오버플로우 인터럽트 벡터
//인터럽트 루틴 처리 과정을 구현하는 구간
}
저번 글에서 배웠던 타이머/카운터의 인터럽트 서비스 루틴을 작성하는 기본 예시이다. 사용 방법은 다음과 같다. 먼저 ISR 매크로를 호출하고, 매개 변수에 인터럽트 벡터를 넣어주면 된다. 그리고 해당 인터럽트가 발생할 때, 처리해야할 동작을 코드로 구현하면 된다.
4. ATmega128A에서의 외부 인터럽트 레지스터
위 인터럽트 벡터 표에서 알 수 있듯이, INT 0부터 INT 7까지 8개의 레지스터를 가지고 있기에, 이 외부 인터럽트를 사용하기 위해서는 레지스터를 필수적으로 설정해야한다. 달랑 ISR 매크로에 인터럽트 벡터를 매개변수로 준다고 해서 인터럽트가 발생되지 않는다. MCU에게 해당 포트의 레지스터를 활성화 시킨다고 알려줘야 MCU도 레지스터를 보고 판단하여 인터럽트를 발생시키기에 필수적으로 설정을 시켜주어야한다.
SREG (Status Register):
SREG는 AVR 마이크로컨트롤러의 상태 레지스터이다. SREG 레지스터에는 프로그램 상태 정보가 포함된다. 예를 들어, 이 레지스터의 하위 비트는 중단 가능 상태를 나타내며, 인터럽트를 허용하거나 금지할 수 있다.
EMISK (External Interrupt Mask):
EMISK는 ATmega128의 외부 인터럽트 마스크 레지스터이다. 이 레지스터는 외부 인터럽트를 활성화하거나 비활성화하는 데 사용된다. 이 레지스터의 각 비트는 해당 인터럽트 번호와 관련이 있다.
EICRA (External Interrupt Control Register A, INT 0번~ INT 3번 설정 레지스터):
EICRA는 ATmega128의 외부 인터럽트 제어 레지스터 중 하나이다. EICRA는 외부 인터럽트 0번부터 3번까지의 레지스터 설정을 담당한다. 이 레지스터는 외부 인터럽트를 설정하는 데 사용된다. 예를 들어, 이 레지스터의 ISCn0과 ISCn1 핀이 가장 중요하다. 이 핀으로 인터럽트가 상승 에지나 하강 에지에서 발생하도록 설정할 수 있다.
EICRB (External Interrupt Control Register B, INT 4번~ INT 7번 설정 레지스터):
EICRB는 ATmega128의 또 다른 외부 인터럽트 제어 레지스터이다. EICRB는 외부 인터럽트 4번부터 7번까지의 레지스터 설정을 담당한다.
EIFR (External Interrupt Flag Register):
EIFR은 ATmega128의 외부 인터럽트 플래그 레지스터이다. 이 레지스터는 외부 인터럽트가 발생했을 때 해당 인터럽트 플래그 비트가 설정된다. 인터럽트 서비스 루틴에서 이 플래그를 사용하여 인터럽트 상태를 확인할 수 있다.
4. ATmega128A에서의 외부 인터럽트 코드 예시
외부 인터럽트를 사용하여 SW1를 누르면 PORTA 0~3까지의 LED를 ON시키고, SW2를 누르면 PORTA 4~7까지의 LED를 ON 시키는 코드의 예시이다.
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(INT4_vect) { // SW1(INT4) 인터럽트 벡터
PORTA |= 0x0F; // PORTA 0~3까지의 LED를 ON
PORTA &= 0xF0; // PORTA 4~7까지의 LED를 OFF
}
ISR(INT5_vect) { // SW2(INT5) 인터럽트 벡터
PORTA |= 0xF0; // PORTA 4~7까지의 LED를 ON
PORTA &= 0x0F; // PORTA 0~3까지의 LED를 OFF
}
int main(void) {
DDRD &= ~((1<<PD4)|(1<<PD5)); // PD4, PD5를 입력으로 설정
PORTD |= (1<<PD4)|(1<<PD5); // PD4, PD5의 Pull-up 활성화
DDRA |= 0xFF; // PORTA를 출력으로 설정
EICRB |= (1<<ISC41)|(1<<ISC51); // INT4과 INT5를 falling edge로 설정
EIMSK |= (1<<INT4)|(1<<INT5); // INT4과 INT5를 활성화
sei(); // 전역적으로 인터럽트 활성화
while(1) {
// main 함수에서 처리해야 할 내용이 있다면 이곳에서 구현합니다.
}
return 0;
}
위 코드에서 INT4_vect와 INT5_vect는 각각 SW1(INT4)과 SW2(INT5)의 인터럽트 벡터이다. 이 벡터들은 각각 SW1과 SW2를 눌렀을 때 실행되며, PORTA 레지스터의 값을 변경하여 LED를 켜고 끄는 작업을 수행한다. EICRB와 EIMSK 레지스터를 사용하여 INT4과 INT5의 인터럽트 발생 조건과 활성화를 설정하고, sei() 함수를 사용하여 전역적으로 인터럽트를 활성화한다. 이후에는 main 함수에서 처리해야 할 내용이 있다면 그 내용을 구현하면 된다.
'AVR' 카테고리의 다른 글
[AVR] 6. ATmega128A의 PWM (0) | 2023.04.18 |
---|---|
[AVR] 4. ATmega128A의 타이머/카운터 (0) | 2023.04.05 |
[AVR] 3. ATmega128A UART 통신과 I2C 통신 (0) | 2023.04.05 |
[AVR] Pull-Up/Pull-Down 저항이란? (0) | 2023.03.31 |
[AVR] 2. ATmega128A 버튼 제어 (0) | 2023.03.30 |