거북이처럼 천천히

Interrupt - 기초 (1) 본문

Embedded Programming/Atmega 128A (실습)

Interrupt - 기초 (1)

유로 청년 2024. 5. 29. 20:11

Button 3개를 통해 서로 다른 Interrupt을 설정하여 LED를 컨트롤하기

 

1) 목표: PORT D 에 PD0, PD1, PD2에 스위치, PORT A 에 LED를 연결한 뒤, 서로 다른 interrupt을 설정하여 각각의 버튼을 눌렀을 때, 서로 다른 Interrupt 때 LED를 컨트롤 할 수 있게끔 설계하기

  • PD0 : Low-level sensitive 에서 Interrupt를 발생하도록 설계
              LED가 짝수 핀들에 다이오드 전부 불이 들어오도록 한 뒤, 짝수 번째 다이오드에 전부 불이
              들어오면 홀수 핀들에 다이오드 전부 불이 들어오도록 설계한다.
  • PD1 : Rising-edge sensitive 에서 Interrupt를 발생하도록 설계
              LED의 0번째 핀부터 7번째 핀까지 LED가 누적되어 출력되도록 설계한다.
  • PD2 : Falling-edge sensitive 에서 Interrupt를 발생하도록 설계
               LED를 총 5번을 깜빡깜빡 거리도록 설계한다.

 

 

 

2) 선행 지식: EICRA와  EIMSK Register에 대한 지식 필요

[ EICRA Register ]
[ EIMSK Register ]

 

  • EICRA Register : 외부 인터럽트의 동작을 제어하기 위해 해당 레지스터를 사용하며, 외부에서 설정한 인터럽트를 감지하게 되면 현재 작업을 수행을 멈추고, ISR (Interrupt service routine) 영역으로 제어권을 넘겨 해당 작업을 수행하게 된다. ISR 영역의 작업을 모두 수행하고 난 뒤, 다시 제어권을 반환하게 된다.

    레지스터에서 주로 인터럽트의 활성화/비활성화, 인터럽트의 트리거 조건(상승 엣지, 하강 엣지, 또는 레벨 트리거) 설정 등의 제어 비트들이 포함됩니다. 다음 표를 통해 인터럽트의 트리거 조건을 정할 수 있다.

 

  • EIMSK Register : 이 레지스터는 Pin configurations를 보고, 어떤 Port 어떤 핀에서 Interrupt 신호를 인가할 수 있는지 확인하여 해당 핀에 연결한다. 그리고 EIMSK Register를 통해 연결된 핀에 맞게 각 비트값을 설정해주게 되면 해당 인터럽트가 활성화하게 된다.

    즉, 레지스터를 통해 외부 인터럽트의 발생 여부를 제어하고, 특정 인터럽트를 활성화 또는 비활성화할 수 있다.


비트 값을 1로 설정하면 그에 맞는 핀의 인터럽트를 활성화 시킬 수 있다.
빨간 색 사각형을 보면 각각의 핀들에 대해서 "INT" 키워드를 볼 수 있는데, 해당 핀들을 통해 Interrupt 신호를 받을 수 있다.

 

 

 

3) Source code

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

// Substitute constant.
#define BUTTON_DDR		DDRD
#define LED_DDR			DDRA
#define LED_PORT		PORTA
#define TIME			200

// Interrupt service routine.
ISR(INT0_vect) {
	uint8_t led_data = 0x00;
	
	for(int i=6; i>=0; i-=2) {
		LED_PORT = led_data | (0x80>>i);
		_delay_ms(TIME);
		
		led_data |= (0x80>>i);
	}
	
	for(int i=7; i>=0; i-=2) {
		LED_PORT = led_data | (0x80>>i);
		_delay_ms(TIME);
		
		led_data |= (0x80>>i);
	}
}

ISR(INT1_vect) {
	uint8_t led_data = 0;
	
	for(uint8_t i=0; i<8; i++) {
		LED_PORT = led_data | (0x01 << i);
		_delay_ms(TIME);
		
		led_data |= (0x01 << i);
	}
}

ISR(INT2_vect) {
	for(uint8_t i=0; i<5; i++) {
		LED_PORT = 0xff;
		_delay_ms(100);
		LED_PORT = 0x00;
		_delay_ms(100);
	}
}


// Function prototype.
void init_button();
void init_led();

// Main method
int main(void) {
	init_button();
	init_led();
	
	while(1) {
		LED_PORT = 0x00;
	}
} 

// Initialization of Button.
void init_button() {
	BUTTON_DDR &= ~(1<<DDRD0 | 1<<DDRD1 | 1<<DDRD2);
	
	// 전역 인터럽트 활성화
	sei();
	
	// PD0 : Low-level sensitive.
	// PD1 : Rising edge sensitive.
	// PD2 : Falling edge sensitive.
	// EICRA = 0b0010 1100
	EICRA = 0x2C;
	
	// EIMSK = 0b0000 0111
	EIMSK = 0x07;
}

// Initialization of LED.
void init_led() {
	// PORTA의 8핀에 모두 LED 연결
	LED_DDR = 0xff;
}

 

  • 각 버튼의 Interrupt가 발생하면 각 버튼의 ISR (Interrupt service routine) 영억으로 제어권이 넘어가 해당 작업을 수행한다. 그리고, 작업이 완료되면 다시 main 함수로 제어권을 넘기고, 마치게 된다.


4) 구현 영상