[Embedded Software] 경희대 임베디드 소프트웨어 과제3 - Switch & Interrupt

경희대학교 컴퓨터공학과 조진성 교수님의 ‘임베디드 소프트웨어’ 강의의 과제입니다.


개발 환경




실습 4


4.1 GPIO 포트 E 전체를 입력으로 만들어 여기로 입력되는 신호 값을 실시간으로(1초 단위) LED에 디스플레이 한다.(SW1, SW2을 누르면서 LED의 변화를 확인)

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>

int main() {
	DDRA = 0xFF;
	
	// 스위치 두 개는 E포트 4,5번(0번부터 시작하였을 때)에 연결
	// SW1 = 4번, SW2 = 5번
	// DDRE |= ~(0x30); 과 같다.
	DDRE |= 0xCF;

	while(1) {
		PORTA = PINE;
		_delay_ms(100);
	}
}


4.2 SW1 을 누르면 모든 LED 불이 켜지고 다시 스위치를 누르면 모든 LED 불이 꺼지며(toggle) SW2 를 누르면 현 상태에서 1이 증가한 값을 LED에 디스플레이한다.

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define OFF 0
#define ON 1

// volatile을 붙이면 컴파일러의 최적화를 하지 않겠다는 의미이다.
// 메모리 맵 입출력을 할 시, 개발자가 의도한대로 코드가 작동하기 위해
// (= 컴파일러에 의해 코드가 의도와 다르게 작동하는 것을 막기 위해)
// 사용된다.
volatile int int4_flag = OFF;

// 인터럽트4를 사용하여 LED 출력을 조정한다.
// JKIT보드에서 인터럽트4는 SW1(E포트 4번)이다.
// 인터럽트 발생 시, int4_flag 값에 따라 LED를 ON/OFF
SIGNAL(SIG_INTERRUPT4) {
	// Interrupt Disable
	cli();
	if(int4_flag == OFF) {
		int4_flag = ON;
		PORTA = 0xFF;
	}
	else {
		int4_flag = OFF;
		PORTA = 0x00;
	}
	_delay_ms(100);
	
	// Interrupt Enable
	sei();
}

// 인터럽트5를 사용하여 LED 출력을 조정한다.
// JKIT보드에서 인터럽트5는 SW2(E포트 5번)이다.
// 인터럽트 발생 시, LED를 1증가하여 출력
SIGNAL(SIG_INTERRUPT5) {
	cli();
	if(int4_flag == OFF) {
		PORTA += 1;
	}
	_delay_ms(100);
	sei();
}

int main() {
	DDRA = 0xFF;
	PORTA = 0x00;
	
	// 스위치 모두 사용
	DDRE = 0x00;
	
	// EICR(External Interrupt Control Register)
	// EICRA : 외부 인터럽트 0~3의 트리거 설정
	// EICRB : 외부 인터럽트 4~7의 트리거 설정
	// EICRB = 0x02이면 하강에지(SW가 눌렸을 때)에서 인터럽트 발생
	EICRA = 0x00;	
	EICRB = 0x02;
	
	// EIMSK(External Interrupt MaSK register)
	// 외부 인터럽트의 개별적인 허용 레지스터
	// EIMSK = 0x30이면 4,5번 인터럽트를 사용한다는 의미이다.
	EIMSK = 0x30;

	sei();

	while(1)
	;
}


실습 5


5.1 SW1 을 누르면 모든 LED 불이 켜지고 다시 SW1 을 누르면 모든 LED 불이 꺼진다.

#define F_CPU 16000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define OFF 0
#define ON 1

volatile int int4_flag = OFF;

// 인터럽트4를 이용하여 int4_flag 변수 제어
SIGNAL(SIG_INTERRUPT4) {
	cli();
	
	if(int4_flag == OFF) {
		int4_flag = ON;
	}
	else {
		int4_flag = OFF;
	}
	_delay_ms(100);

	sei();
}

int main() {
	DDRA = 0xFF;
	DDRE = 0xEF;
	
	PORTA = 0x00;

	EICRA = 0x00;
	EICRB = 0x02;
	
	// 인터럽트 4번을 사용한다는 의미
	EIMSK = 0x10;

	sei();

	while(1) {
		if(int4_flag == OFF) {
			PORTA = 0x00;
		}
		else {
			PORTA = 0xFF;
		}		
	}
}


5.2 5.1에 더하여, FND의 숫자는 0에서 출발하여 SW2 를 누를 때마다 1씩 증가한다.

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <avr/interrupt.h>	
#include <util/delay.h>

#define	OFF 0
#define	ON 1

// 인터럽트4를 사용하여 LED 출력 조정
SIGNAL(SIG_INTERRUPT4) {
	volatile static int	int4_flag = OFF;
		
	cli();
	if(int4_flag == OFF)  {	
		int4_flag = ON;
		PORTA = 0xFF;	
	}
	else  {	
		int4_flag = OFF;
		PORTA = 0x00;	
	}
	_delay_ms(100);
	sei();
}

// 인터럽트5를 사용하여 FND 출력 조정
SIGNAL(SIG_INTERRUPT5) {
	static unsigned char FND_DATA[ ]= {
		0x3f, 0x06, 0x5b, 0x4f, 0x66,
		0x6d, 0x7d, 0x27, 0x7f, 0x6f };
		
	static unsigned int num = 0;
    
	cli();
	
	PORTC = FND_DATA[num];
	PORTG = 0x01;
	
	// 10으로 나눈 나머지를 사용하여 1씩 증가해도
	// 배열에 들어있는 값(0~9)가 반복되도록 한다.
	num = (num+1) % 10;
	_delay_ms(100);
	
	sei();
}

int main() {
	DDRA = 0xFF;
	DDRC = 0xFF;		 
	DDRE = 0xCF;
	
	PORTA= 0x00;

	EICRA = 0x00;
	// 인터럽트4, 5번 : 하강엣지
	EICRB = 0x0A;
	
	// 인터럽트 4, 5번 사용
	EIMSK = 0x30;
	
	sei();

	while (1)
		;	
}


실습 6


6.1 스위치 와 FND를 이용하여 1/100초 단위의 스톱워치를 제작하는데, SW1를 누르면 FND 디스플레이가 잠시 멈추었다가 한 번 더 누르면 다시 이어서 동작을 하도록 한다.

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <avr/interrupt.h>	
#include <util/delay.h>

#define	OFF 0
#define	ON 1

// volatile을 사용하여 컴파일러의 최적화가 적용이 안되게
// 변수를 모두 전역으로 선언
volatile int int_flag = OFF;
volatile int time_10sec = 0;
volatile int time_1sec = 0;
volatile int time_100ms = 0;
volatile int time_10ms = 0;

// 인터럽트4를 사용하여 int_flag 변수 조정
SIGNAL(SIG_INTERRUPT4) {
	cli();
	
	if(int_flag == OFF) {
		int_flag = ON;
	}
	else {
		int_flag = OFF;
	}
	_delay_ms(100);

	sei();
}

// 인터럽트5를 사용하여 time변수들 초기화
SIGNAL(SIG_INTERRUPT5) {
	cli();
	
	time_10sec = 0;
	time_1sec = 0;
	time_100ms = 0;
	time_10ms = 0;
	int_flag = OFF;
	_delay_ms(100);

	sei();
}

int main() {
	unsigned char FND_DATA[ ] = {	0x3f, 0x06, 0x5b, 0x4f, 0x66,
									0x6d, 0x7c, 0x07, 0x7f, 0x67,
									0x77, 0x7c, 0x39, 0x5e, 0x79,
									0x71, 0x80, 0x40, 0x08 };

	DDRC = 0xFF;
	DDRG = 0x0F;
	DDRE = 0xCF;

	PORTC = FND_DATA[0];
	PORTG = 0x00;

	EICRA = 0x00;
	
	// 인터럽트 4, 5번 : 하강엣지
	EICRB = 0x0A;
	
	// 인터럽트 4, 5번 사용
	EIMSK = 0x30;

	sei();

	while(1) {
		if(int_flag == ON) {
			time_10ms++;
			if(time_10ms == 10) {
				time_100ms++;
				time_10ms = 0;
			}
			if(time_100ms == 10) {
				time_1sec++;
				time_100ms = 0;
			}
			if(time_1sec == 10) {
				time_10sec++;
				time_1sec = 0;
			}
			if(time_10sec == 6) {
				time_10sec = 0;
			}
		}
		PORTC = FND_DATA[time_10ms];
		PORTG = 0x01;
		_delay_ms(2);

		PORTC = FND_DATA[time_100ms];
		PORTG = 0x02;
		_delay_ms(3);

		PORTC = FND_DATA[time_1sec];
		PORTG = 0x04;
		_delay_ms(2);

		PORTC = FND_DATA[time_10sec];
		PORTG = 0x08;
		_delay_ms(3);
	}
}


실습응용 1


5.2의 코드는 ISR의 코드를 길게 수행하며 이는 바람직하지 않다. 이를 main함수로 이동하여 처리하도록 한다.

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <avr/interrupt.h>	
#include <util/delay.h>

#define OFF 0
#define	ON 1

volatile static unsigned int num = 0;
volatile static int int4_flag = OFF;
unsigned char FND_DATA[ ] = {	0x3f, 0x06, 0x5b, 0x4f, 0x66,
								0x6d, 0x7c, 0x07, 0x7f, 0x67};

// 인터럽트4는 int4_flag 변수 조정
SIGNAL(SIG_INTERRUPT4) {
	cli();

	if(int4_flag == OFF) {	
		int4_flag = ON;
	}
	else {	
		int4_flag = OFF;
	}
	_delay_ms(100);

	sei();
}

// 인터럽트5는 num변수 조정
SIGNAL(SIG_INTERRUPT5) {
	cli();

	num = (num + 1) % 10;
	_delay_ms(100);

	sei();
}

int main() {
	DDRA = 0xFF;		 
	DDRC = 0xFF;
	DDRE = 0xCF;	

	PORTA = 0x00;
	PORTC = FND_DATA[0]; 
	PORTG = 0x00;

	EICRA = 0x00;
	
	// 인터럽트 4, 5번 : 하강엣지
	EICRB = 0x0A;
	
	// 인터럽트 4, 5번 사용
	EIMSK = 0x30;

	sei();

	// main함수 안의 while루프를 통해 LED와 FND의 출력 조정
	while(1) {
		if(int4_flag == OFF) {
			PORTA = 0xFF;
		}
		else {
			PORTA = 0x00;
		}

		PORTC = FND_DATA[num];
		PORTG = 0x01;
		_delay_ms(2);
	}
}


실습응용 2


실습 6의 스톱워치를 기본으로, SW1이 눌려지면 그 순간 시간이 정지한 상태로 디스플레이하지만, 실제로는 스톱워치가 시간을 계속 카운트하고 있어서, SW1이 다시 눌려지면 현재 시간부터 디스플레이되도록 하는 프로그램을 작성하라.

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <avr/interrupt.h>	
#include <util/delay.h>

#define	OFF 0
#define	ON 1

// volatile을 사용하여 컴파일러의 최적화가 적용이 안되게
// 변수를 모두 전역으로 선언
volatile static int first_flag = ON;
volatile static int int_flag = OFF;
volatile int time_10sec = 0; 
volatile int time_1sec = 0;
volatile int time_100ms = 0;
volatile int time_10ms = 0;
volatile int stop_time_10sec = 0; 
volatile int stop_time_1sec = 0;
volatile int stop_time_100ms = 0;
volatile int stop_time_10ms = 0;
volatile static unsigned char FND_DATA[ ] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66,
									0x6d, 0x7c, 0x07, 0x7f, 0x67,
									0x77, 0x7c, 0x39, 0x5e, 0x79,
									0x71, 0x80, 0x40, 0x08 };

// 인터럽트4를 사용하여 stop_time 변수의 값 생성
SIGNAL(SIG_INTERRUPT4) {
	cli();
	
	if(int_flag == OFF) {	
		int_flag = ON;

		if(first_flag == ON) {
			first_flag = OFF;
		}
	}
	else {	
		int_flag = OFF;
		stop_time_10sec = time_10sec;	
		stop_time_1sec = time_1sec;	
		stop_time_100ms = time_100ms;	
		stop_time_10ms = time_10ms;			
	}
	
	sei();
}

// 인터럽트5를 사용하여 변수 초기화
SIGNAL(SIG_INTERRUPT5) { 
	cli();

	stop_time_10sec = time_10sec = 0;
	stop_time_1sec = time_1sec = 0;
	stop_time_100ms = time_100ms = 0;
	stop_time_10ms = time_10ms = 0;
	int_flag = OFF;
	first_flag = ON;

	sei();
}

int main() {
	DDRC = 0xFF;
	DDRG = 0x0F;
	DDRE = 0xCF;
	
	PORTC = FND_DATA[0];
	PORTG = 0x0F;
		
	EICRA = 0x00;
	
	// 인터럽트 4, 5번 : 하강 엣지
	EICRB = 0x0A;
	
	// 인터럽트 4, 5번 사용
	EIMSK = 0x30;

	sei();

	while(1) {
		if(first_flag == OFF) {
			time_10ms++;

			if (time_10ms == 10) {
				time_100ms++;
				time_10ms = 0;
			}
			if (time_100ms == 10) {
				time_1sec++;
				time_100ms = 0;
			}
			if (time_1sec == 10) {
				time_10sec++;
				time_1sec= 0;
			}
			if (time_10sec == 6) {
				time_10sec = 0;
			}
		}

		if(int_flag == ON) { 
			PORTC = FND_DATA[time_10ms];
			PORTG = 0x01;
			_delay_ms(2);

			PORTC = FND_DATA[time_100ms];
			PORTG = 0x02;
			_delay_ms(3);

			PORTC = FND_DATA[time_1sec] | FND_DATA[16];
			PORTG = 0x04;
			_delay_ms(2);

			PORTC = FND_DATA[time_10sec];
			PORTG = 0x08;
			_delay_ms(3);
		}
		else if(int_flag == OFF) {
			PORTC = FND_DATA[stop_time_10ms];
			PORTG = 0x01;
			_delay_ms(2);

			PORTC = FND_DATA[stop_time_100ms];
			PORTG = 0x02;
			_delay_ms(3);

			PORTC = FND_DATA[stop_time_1sec] | FND_DATA[16];
			PORTG = 0x04;
			_delay_ms(2);

			PORTC = FND_DATA[stop_time_10sec];
			PORTG = 0x08;
			_delay_ms(3);
		}
	}
}


실습응용 3


실습응용 1을 inturrpt 대신 polling 방식으로 구현

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <util/delay.h>

#define	OFF 0
#define	ON 1

int main() {
	static unsigned char FND_DATA[ ] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66,
									0x6d, 0x7c, 0x07, 0x7f, 0x67,
									0x77, 0x7c, 0x39, 0x5e, 0x79,
									0x71, 0x80, 0x40, 0x08 };
	static unsigned int num = 0;
	static int	int4_flag = OFF;

	DDRA = 0xFF;		 
	PORTA= 0x00;
	DDRE = 0xCF;
	
	while(1) {
		unsigned int result = 0x30 & PINE;

		if(0x10 == result) {
			num = (num+1) % 10;
		}
		else if(0x20 == result) {
			if(int4_flag == OFF) {	
				int4_flag = ON;		
			}
			else {	
				int4_flag = OFF;
			}
		}

		if(int4_flag == ON)	{
			PORTA = 0xFF;
		}
		else {
			PORTA = 0x00;	
		}

		PORTC = FND_DATA[num];
		PORTG = 0x01;
		
		_delay_ms(200);	
	}
}


실습응용 2를 interrupt 대신 polling으로 구현

#define F_CPU	16000000UL

#include <avr/io.h>		
#include <util/delay.h>

#define	OFF 0
#define	ON 1

volatile static int first_flag = ON;
volatile static int int_flag = OFF;
volatile int time_10sec = 0; 
volatile int time_1sec = 0;
volatile int time_100ms = 0;
volatile int time_10ms = 0;
volatile int stop_time_10sec = 0; 
volatile int stop_time_1sec = 0;
volatile int stop_time_100ms = 0;
volatile int stop_time_10ms = 0;
static unsigned char FND_DATA[ ] = { 0x3f, 0x06, 0x5b, 0x4f, 0x66,
									0x6d, 0x7c, 0x07, 0x7f, 0x67,
									0x77, 0x7c, 0x39, 0x5e, 0x79,
									0x71, 0x80, 0x40, 0x08 };
unsigned int prev_upButton = 0x10;

int main() {
	DDRC = 0xFF;
	DDRG = 0x0F;		 
	DDRE = 0xCF;
	PORTC = FND_DATA[0];
	PORTG = 0x0F;

	while(1) {
		if(0x00 == (PINE & 0x20)) {
			stop_time_10sec = time_10sec = 0;
			stop_time_1sec = time_1sec = 0;
			stop_time_100ms = time_100ms = 0;
			stop_time_10ms = time_10ms = 0;
			int_flag = OFF;
			first_flag = ON;		
		}

		if((0x00 == (PINE & 0x10)) && (prev_upButton == 0x10)) {
			if(int_flag == OFF) {	
				int_flag = ON;
				if(first_flag == ON) {
					first_flag = OFF;
				}
			}
			else {	
				int_flag = OFF;
				stop_time_10sec = time_10sec;	
				stop_time_1sec = time_1sec;	
				stop_time_100ms = time_100ms;	
				stop_time_10ms = time_10ms;			
			}
			
			prev_upButton = 0x00;
		}
		else if((0x10 == (PINE & 0x10)) && (prev_upButton == 0x00)) {
			prev_upButton = 0x10;
		}
			
		if(first_flag == OFF) {
			time_10ms++;

			if(time_10ms == 10) {
				time_100ms++;
				time_10ms = 0;
			}
			
			if(time_100ms == 10) {
				time_1sec++;
				time_100ms = 0;
			}
			
			if(time_1sec == 10) {
				time_10sec++;
				time_1sec= 0;
			}
			
			if(time_10sec == 6) {
				time_10sec = 0;
			}
		}

		if(int_flag == ON) { 
			PORTC = FND_DATA[time_10ms];
			PORTG = 0x01;
			_delay_ms(2);

			PORTC = FND_DATA[time_100ms];
			PORTG = 0x02;
			_delay_ms(3);

			PORTC = FND_DATA[time_1sec] | FND_DATA[16];
			PORTG = 0x04;
			_delay_ms(2);

			PORTC = FND_DATA[time_10sec];
			PORTG = 0x08;
			_delay_ms(3);
		}
		else if(int_flag == OFF) {
			PORTC = FND_DATA[stop_time_10ms];
			PORTG = 0x01;
			_delay_ms(2);

			PORTC = FND_DATA[stop_time_100ms];
			PORTG = 0x02;
			_delay_ms(3);

			PORTC = FND_DATA[stop_time_1sec] | FND_DATA[16];
			PORTG = 0x04;
			_delay_ms(2);

			PORTC = FND_DATA[stop_time_10sec];
			PORTG = 0x08;
			_delay_ms(3);
		}	
	}
}