7강. 내부 인터럽트와 외부 인터럽트
본 글은 Atmega의 인터럽트에 대해서 얼아본다.
- 인터럽트?
'방해하다', '중단하다'라는 뜻을 가진 인터럽트는, 인터럽트가 실행 될 떄 메인 프로그램을 중지하고 다른 일을 수행하는걸 말합니다.
인터럽트가 발생하게 되면, 인터럽트 서비스 루틴(ISR)이 발생하면서 그 안에 있는 동작이 실행 된 후, 다시 메인 프로그램으로 돌아갑니다.
- 인터럽트 종류
Atmega에는 다양한 인터럽트가 있습니다. 대표적인 것만 보면,
> 내부 인터럽트 : 타이머, ADC, UART통신, SPI 통신 등, Atmega 내부의 동작에 의해 발생하는 인터럽트 입니다.
> 외부 인터럽트 : INT0~INT7로, 외부의 전기 신호에 의해 발생하는 인터럽트 입니다.
내부 인터럽트는 다룰게 많아서 나중의 글에서 하나씩 살펴보기로 하고,
이번 시간에는 외부 인터럽트를 알아보겠습니다.
- 외부 인터럽트
ATmega128a 기준으로, 외부 인터럽트는 8개가 있습니다.
PD0~PD3 핀에는 INT0~INT3, PE4~PE7 핀에는 INT4~INT7이 있네요.
- 외부 인터럽트의 입력 신호
atmega의 외부 인터럽트는 3가지 신호를 인식할 수 있습니다.
Rising Edge, Falling Edge, Low level이 그것입니다. High Level은 인식할 수 없습니다.
- 외부 인터럽트의 레지스터
레지스터는 간단히 의미하면 '설정 값'을 의미한다. 외부 인터럽트는 크게 3가지 레지스터가 있다.
1. EIMSK로 어느 핀의 외부 인터럽트를 사용할지 정한다.
2. EICRA, EICRB로 외부 인터럽트를 설정한다.
3. sei(); 함수 호출, 혹은 SREG로 외부 인터럽트 사용을 허가한다.
하나씩 알아보도록 하겠습니다.
> 1. EIMSK (External Interrupt Mask Register) : 외부 인터럽트 활성화
INT7~INT0 중, 어느 비트를 활성화 시킬지 정합니다. 비트 값이 1이면 활성화 됩니다.
> 2. EICRA, EICRB (External Interrupt Control Register A, B) : 외부 인터럽트 설정
EICRA는 INT0~INT3 외부 인터럽트를 설정할 수 있습니다.
EICRA의 Bit7,6은 INT3, Bit5,4는 INT2, Bit3,2는 INT1, Bit1,0은 INT0을 다룰 수 있고,
비트가 00이면 Low level, 01은 없고, 10이면 falling edge, 11이면 rising edge를 인식합니다.
EICRB는 INT4~INT7 외부 인터럽트를 설정할 수 있고, EICRA와 동일합니다.
다만, 비트가 01이면 falling edge/rising edge 둘 다 인식할 수 있는 기능이 추가되었습니다.
> 3. SREG (Statue REGister) : 상태 비트 설정
7번 비트가 'I'(Interrupt)를 1로 하면, 모든 인터럽트 종류에 대한 사용을 승인하는 레지스터이다
참고로 6,5,4,... 비트는 인터럽트와는 관련없는 기능이다.
레지스터를 활성화 시켜도 되고, 간단하게는 이렇게도 가능하다.
sei(); // SREG 레지스터의 7번 비트 활성화
cli(); // SREG 레지스터의 7번 비트 비활성화
> 기타. EIFR (External Interrupt Flag Register) : 인터럽트 결과 확인
인터럽트가 발생안한 핀을 0, 인터럽트가 발생한 핀을 1로 읽어와서 EIFR 레지스터에 나타내어준다.
참고로 이 레지스터는 Read Only이다. 결과값을 읽어오는 것만 가능하다.
- 예제 : 0번 외부 인터럽트를 Rising Edge로 인식하는 코드
// 0번 외부인터럽트를 rising edge로 사용해보기
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile int sw;
// 인터럽트 서비스 루틴.
// 인터럽트 발생시, 이 안의 코드가 수행된다.
ISR(INT0_vect) // 외부 인터럽트 0번 사용.
{
sw=1;
}
// 인터럽트 설정.
void external_interrupt_setting()
{
EIMSK=(1<<INT0); //0번 외부 인터럽트를 사용하겠다.
EICRA=((1<<ISC01)|(1<ISC00)); //0번 외부 인터럽트는 입력 신호가 rising edge일 때 발생한다.
}
int main(void)
{
DDRD = 0x00;
DDRA = 0xff;
external_interrupt_setting();
sei(); //인터럽트 사용을 승인한다..!
while (1)
{
if(sw==1)
{
// PORTA의 LED가 1초동안 켜졌다가 꺼진다.
PORTA = 0xff;
_delay_ms(1000);
PORTA = 0x00;
sw=0;
}
}
}