ATmega128에서 사용하는 타이머는 총 4가지가 있습니다.
TIMER0 | TIMER1 | TIMER2 | TIMER3 |
---|---|---|---|
TCCR0 | TCCR1A | TCCR2 | TCCR3A |
TCNT0 : 8bit | TCCR1B | TCNT2 : 8bit | TCCR3B |
Timsk : 0bit | TCNT1 : 16bit | Timsk : 6bit | TCNT3 : 16bit |
Timsk : 2bit | ETimsk : 2bit |
각각의 타이머는 비트수가 다르며 환경에 맞는 타이머를 활용하면 됩니다.
오늘 집중적으로 알아볼 타이머는 타이머 0과 타이머 1로 어떤 비트값을 가지고 있는지, 어덯게 코딩해서 값을 설정해야 하는지 조금 더 자세히 알아볼까 합니다.
우선 타이머 0의 비트값에 따른 클럭은 다음과 같습니다.
CS02 | CS01 | CS00 | 클럭 |
---|---|---|---|
0 | 0 | 0 | 클럭은 발생하지 않는다.(타이머, 카운터 정지) |
0 | 0 | 1 | clk TOS |
0 | 1 | 0 | clk TOS / 8 |
0 | 1 | 1 | clk TOS / 32 |
1 | 0 | 0 | clk TOS / 64 |
1 | 0 | 1 | clk TOS / 128 |
1 | 1 | 0 | clk TOS / 256 |
1 | 1 | 1 | clk TOS / 1024 |
타이머 1의 비트값에 따른 클럭은 다음과 같고요.
CSn2 | CSn1 | CSn0 | 클럭 |
---|---|---|---|
0 | 0 | 0 | 클럭은 발생하지 않는다.(타이머, 카운터 정지) |
0 | 0 | 1 | clk TOS |
0 | 1 | 0 | clk TOS / 8 |
0 | 1 | 1 | clk TOS / 64 |
1 | 0 | 0 | clk TOS / 256 |
1 | 0 | 1 | clk TOS / 1024 |
1 | 1 | 0 | clk TOS / Tn핀으로 들어오는 외부 클럭 (하강 에지 클럭) |
1 | 1 | 1 | clk TOS / Tn핀으로 들어오는 외부 클럭 (상승 에지 클럭) |
여기에서 필요로 하는 클럭을 가지고 인터렙터가 걸리는 시간을 조절 할 수 있습니다. t = 1/크리스탈 * 사용하고자 하는 클럭 * x 위 식을 계산한 다음에 나온 x 값을 코딩할 때 TCNT0에 차이값을 구해서 집어넣으면 됩니다.
예시를 들어 설명하자면, 타이머 0를 사용하고 크리스탈 값이 14.745600이라 하고, 10ms 마다 인터럽트가 걸리게 하려면 비스틑 111, x의 값은 144가 나옵니다. 8비트로 운영되는 타이머 0이기 때문에 가능한 최대의 카운트는 256이죠. 따라서 256 - 144 = 122이므로 TCNT0 = 122가 되는겁니다.
타이머 1로도 해봅시다. 같은 크리스탈, 같은 시간 마다 인터럽트가 걸린다고 하면 클럭비트는 101, x의 값은 같은 144가 될 겁니다. 16비트인 타이머 1 이므로 65536 - 144 = 65392 이므로 TCNT0 = 65392가 됩니다.
코드를 통해 보자면 다음과 같습니다.
//간단하게 타이머를 사용하는데 필요한 코드들만 작성해 보겠습니다.
BYTE TimerFlag;
WORD Timer1Cnt;
BYTE Timer1Flag;
//main()
int main(void)
{
//택포인터 초기화. 필수적인 요소로 main클래스 의 가장 앞에 위치합니다.
SPH = 0x00;
SPL = 0xff;
//포트 초기화
IntPort();
//타이머 초기화
InitTimer0();
While()
{
//반복문 입니다. 이 반복문 안에서 타이머 인터럽트가 걸리면서 타이머가 작동합니다.
if(TimerFlag)
{
key44_Scan();
TimerFlag = 0; //다시 타이머가 작동할 수 있도록 타이머 플래그를 초기화 합니다.
}
if(Timer1Flag)
{
Timer1Flag = 0;
}
if(key44_event ==1)
{
key44_job_Exe();
key44_event = 0; //KEY EVT flag도 초기화 시켜줍니다.
}
}
}
ISP(TIMER0_OVF_vect)
{
//위의 타이머 0표를 보고 계산한 값을 집어넣습니다.
TCNT0 = 112; //TCNT =144 ->(1024/14.7456MHz)*(256-112)=10msec
//타이머 플래그 10미리 세컨드 마다 걸리게끔 설정한 겁니다.
TimerFlag = 1; //Set 10mec EVTflag ! (EXE @main routine)
Timer1Cnt++;
//타이머를 1초마다 카운트 하게끔 설정합니다. 앞서 타이머 플래그를 10mec로 설정하였으므로 100이 곱해져야 1이됩니다.
//따라서 1초마다 타이머가 걸리게 하기 위해 Timer1Cnt >= 100로 설정한겁니다
if (Timer1Cnt >= 100) //10 * 100 = 1sec
{
//타이머가 1초가 지나 인터럽트가 걸리면 마로 타이머를 0으로 초기화 시켜 다음 1초가 되었을때 다시 인터럽트 걸리게 합니다.
Timer1Cnt = 0;
//타이머 플래그를 true로 반환합니다. 이거 bool이랑 같은 타입니다.
Timer1Flag = 1; //Set 1sec EVT flag !(EXE @main routine)
}
}
void InitTimer0(void)
{
//무슨 클럭 선택 비트 사용하는 건지 설정합니다. 위의 표에 있는 값을 보고 적습니다. 여기선 1024를 사용하였으므로 111인 0x07을 집어넣습니다.
TCCR0 = 0x07; //Trigger => Fclock/1024
//=144 하지만 여기서의 초기화는 그렇게 의미있지 않습니다. 위에서 이미 초기화 했기 때문입니다.
TCNT0 = 256-112; //clear Timer Counter Register
TIMSK = 0x01; //Timer0 Interrupt Enable Mask Register
}
//key 관련 함수들은 생략하도록 하겠습니다.
위와 같은 방법으로 타이머 0를 초기화 하고 사용할 수 있습니다!
타이머 1을 사용하는 경우는 위의 코드는 모두 같으며 밑에서 선언하는 ISP(TIMER0_OVF_vect) 와 void InitTimer0(void)에 들어가는 값만 간단히 바꿔주면 됩니다.
ISP(TIMER1_OVF_vect)
{
//위의 타이머 1표를 보고 계산한 값을 집어넣습니다.
TCNT1 = 65392; //TCNT =144 ->(1024/14.7456MHz)*(256-112)=10msec
//타이머 플래그 10미리 세컨드 마다 걸리게끔 설정한 겁니다.
TimerFlag = 1; //Set 10mec EVTflag ! (EXE @main routine)
Timer1Cnt++;
//타이머를 1초마다 카운트 하게끔 설정합니다. 앞서 타이머 플래그를 10mec로 설정하였으므로 100이 곱해져야 1이됩니다.
//따라서 1초마다 타이머가 걸리게 하기 위해 Timer1Cnt >= 100로 설정한겁니다
if (Timer1Cnt >= 100) //10 * 100 = 1sec
{
//타이머가 1초가 지나 인터럽트가 걸리면 마로 타이머를 0으로 초기화 시켜 다음 1초가 되었을때 다시 인터럽트 걸리게 합니다.
Timer1Cnt = 0;
//타이머 플래그를 true로 반환합니다. 이거 bool이랑 같은 타입니다.
Timer1Flag = 1; //Set 1sec EVT flag !(EXE @main routine)
}
}
void InitTimer0(void)
{
//무슨 클럭 선택 비트 사용하는 건지 설정합니다. 위의 표에 있는 값을 보고 적습니다. 여기선 1024를 사용하였으므로 101인 0x05을 집어넣습니다.
TCCR1 = 0x05; //Trigger => Fclock/1024
//=144 하지만 여기서의 초기화는 그렇게 의미있지 않습니다. 위에서 이미 초기화 했기 때문입니다.
TCNT1 = 65536 - 144; //clear Timer Counter Register
TIMSK = 0x01; //Timer0 Interrupt Enable Mask Register
}
위와 같은 방법으로 타이머 1 역시 초기화 해 봤습니다.
위의 코드에서는 크리스탈의 값을 14.745600으로 고정해놨는데요, 사용하는 크리스탈에 맞는 클럭 비트 선택을 하면 됩니다. 크리스탈 값을 바꿔야 하는 상황을 예로 들자면, 타이머 0의 110 비트를 사용하고 싶은데, 이 비트값을 14.745600의 값을 가지는 크리스탈을 사용하면 8bit인 타이머 0로는 x값인 576을 다 타운터 할 수 없으므로 사용할 수가 없죠. 이런 상황에서 크리스탈을 변경하는 것으로도 해결 할 수 있습니다.
해결책 1 : 크리스탈을 낮춘다.
크리스탈 값을 1.843200으로 낮춘 후 다시 한번 계산하면
10ms = 1/1.843200 * 256 * x 이므로 x = 72가 되죠. 사용할 수 있는 x값이 되었습니다.
해결책 2 : 타이머를 바꾼다.
타이머 1, 3은 16bit이므로 65535까지 카운트 할 수 있죠.
해결책 3 : t값은 낮춘다.
인털럽트가 걸리는 시간을 더 짧게 합니다.
t = 0.002로 하면, 0.002 = 1/14.745600 * 256 * x 이므로 x = 115입니다.
오늘은 간단하게 타이머 0, 타이머 1에 대해 알아봤습니다. ATmega128에서 필수적으로 사용하는 요소이니 잘 알아두면 좋을것 같습니다.