이번에는 스위치와 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 20volatile 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) initializationMCUCR=0b00001000; // INT1: Falling EdgeGIFR =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페이지 분량)는 작성해야 함을 알 수 있다.
'하드웨어 > ATmega8(A)' 카테고리의 다른 글
ATmega8(A) T/C0의 레지스터와 프리스케일러 (0) | 2015.06.23 |
---|---|
ATmega8(A)의 타이머/카운터와 T/C0 개요 (0) | 2015.06.23 |
ATmega8(A)의 외부 인터럽트를 이용한 스위치 실험1 (0) | 2015.06.22 |
ATmega8(A)의 외부 인터럽트(interrupt) 설정 (0) | 2015.06.22 |
ATmega8(A) 인터럽트(interrupt) 프로그래밍 (0) | 2015.06.22 |