2015년 6월 22일 월요일

ATmega8(A)의 외부 인터럽트를 이용한 전자 주사위 구현

 이번에는 스위치와 7세그먼트를 이용하여 1에서 6사이의 숫자를 임의로 표시해 주는 전자주사위를 구현해 보겠다. 처음에는 7세그먼트의 테두리가 빙글빙글 돌고 있다. SW1을 누르면 임의로 1-6사이의 숫자가 빠르게 표시되다가 점점 느려진다. 이 때 숫자가 표시될 때마다 부저가 짧게 울린다. 마지막 숫자가 표시되면 2초 동안 멈추어 있다. 이후 다시 테두리가 돌아간다.

#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "Am8USBasp.h"
void _delay_ms_var(uchar ucA);
#define NUMREPEAT 20
volatile uchar ucFlag = 0;
ISR(INT1_vect) {
   ucFlag = 1;
}
int main(void) {
   uchar ucaDice[6] = {0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d};
   uchar ucaCirc[6] = {0x03, 0x06, 0x0C, 0x18, 0x30, 0x21};
   uchar ucA=0, ucPrev, ucB, ucC;
   uint uiSeed = 0;
   InitAM8();
   GICR|=0b10000000; // External Interrupt(s) initialization
   MCUCR=0b00001000; // INT1: Falling Edge
   GIFR =0b10000000; // set interrupt flag as '0'
   sei();
   while(1) {
       if (ucFlag == 0) {
           ucA += (ucA==5) -5:1;
           SEG(ucaCirc[ucA]);
           _delay_ms(100);
           uiSeed++;
       } else { // if (ucFlag = 1)
           srand(uiSeed);
           ucPrev = rand()%6;
           for(ucB=1;ucB<=NUMREPEAT;ucB++) {
               do {
                   ucC = rand()%6;
               } while (ucC==ucPrev);
               SEG( ucaDice[ucC] );
               Beep;
               if (ucB == NUMREPEAT)
                   _delay_ms(2000);
               else
                   _delay_ms_var(ucB);
               ucPrev = ucC;
           }
           ucFlag = 0;
       } // else
   } // while(1)
}
void _delay_ms_var(uchar ucA) {
   do {
       _delay_ms(10);
   } while (--ucA>0);
}

이 예제에서 보면 while(1) 반복문이 크게 두 부분으로 나뉘어 있다는 것을 알 수 있는데 ucFlag==0 일 경우와 ucFlag==1 일 경우이다. 전자의 경우는 7세그먼트의 테두리가 돌아가도록 되어 있고 후자의 경우는 숫자들이 반복되어 표시되면서 주사위 숫자(1~6)중 하나를 표시하도록 되어 있다. 전역변수 ucFlag는 SW1이 눌려지면 ISR()함수 내부에서 1값으로 바뀌게 되어있으므로 volatile 키워드를 붙여서 정의하였음에 유의해야한다.

volatile uchar ucFlag = 0;

그 다음으로 설명할 점은 rand()함수의 사용법인데, 이 함수는 stdlib.h 함수에 정의되어 있으며 호출될 때마다 0 에서 RANDOM_MAX 상수 ( 0x7FFFFFFF 값으로 stdlib.h에 정의되어 있음) 사이의 정수를 임의로 발생시키는 함수이다. 그런데 이 함수는 srand()함수로 초기화를 시킨 이후에 사용을 해야지만 진정한 난수를 발생시킬 수 있다. srand()함수를 임의의 시드값으로 초기화 시키지 않으면 시스템이 재시작될 때마다 똑같은 난수가 발생하므로 진정한 난수라고 할 수 없는 것이다. 보통 PC환경에서는 이 시드값으로 시스템의 시간관련 값을 사용하는데 여기서는 그럴 수 없으므로 uiSeed변수를 두어 이 변수값은 계속 증가하게끔 하였다. 그리고 srand()함수를 초기화시킬 때 uiSeed 변수를 두어서 버튼이 눌려질 때 이 값으로 초기화를 시켜주면 그 시점에서의 uiSeed변수 값은 스위치를 누른 시점에 의존하므로 누를 때마다 달라질 것이다. 이렇게 하여 버튼이 눌려질 때마다 다른 시드값으로 초기화되어 진정한 난수가 발생되게 된다.

 그리고 _delay_ms()함수는 입력으로 상수 값만을 받으며 변수 값을 입력으로 줄 수 없다. 따라서 숫자가 표시되는 시간을 점점 늘려가도록 하는데 사용하기 위해서 _delay_ms_var()함수를 별도로 정의하여 (들어온 변수값 × 10ms) 시간 동안 지연할 수 있도록 작성하였다.

 이 프로그램을 PC에서 컴파일하면 958byte (전체 플래시롬의 약 11%)의 실행파일이 생성되었다. 따라서 ATmega8(A)에 꽉 차는 정도의 길이로 프로그램을 하려면 이 예제의 약 10배 정도 (대충 A4용지 10페이지 분량)는 작성해야 함을 알 수 있다.


댓글 없음:

댓글 쓰기