보통 몇 MHz에서 몇 ms 짜리의 타이머를 만들때, Prescaler를 결정하려 계산을 하실때, 이미 여러분들이 만들어 놓은 공식을 이용을 하실겁니다. 저도 처음엔 잘 이해가 안가서 데이타시트를 보면서 응용을 해 보았더니 아래와 같은 이해하기 쉬운, 공식아닌 공식이 생기더군요. 정밀한 타이머 만드시면서 이해하시는데 도움이 되시길 바랍니다.
문서에 대한 문의사항이나 교정요청은 wfellow at gmail dot com 으로 부탁드립니다.^^
0) 공통사항.
이 문서의 모든 예를 16 MHz로 하겠습니다.
a. 16 MHz(16000000 Hz) 경우 1 clock당 시간 = 1 / 16000000 = 0.0000000625 sec
b. 1 sec 를 뛰려면 필요한 clock 갯수 = 1 / 0.0000000625 = 16000000 개
1 초라는 어마어마한 시간을 뛰려면 16만개의 클럭이 발생을 해야 한다는 것을 알 수 있습니다. 모든 타이머를 한개의 클럭마다 그 갯수를 센다면 엄청난 낭비가 되겠지요? 그래서 Prescaler라는 것이 있습니다. Prescaler가 8이라는 이야기는 8번마다 한번이라고 센다는 이야기 이지요. 보통 8, 64, 256과 1024의 네개의 Prescaler가 있습니다. 한번 계산을 해 볼까요?
c. Prescaler별 실제 소요 시간 계산.
8 ===> 0.0000000625 * 8 = 0.0000005 = 0.5 us
64 ===> 0.0000000625 * 64 = 0.000004 = 4 us
256 ===> 0.0000000625 * 256 = 0.000016 = 16 us
1024===> 0.0000000625 * 1024 = 0.000064 = 64 us
Prescaler값마다 소요되는 시간이 계산되는 부분은 별도로 설명하지 않아도 되겠지요? 단지 왜 Prescaler라는 것이 필요한지만 알면됩니다. 여기까지가 타이머를 만들기 위한 이해 부분에서 공통(이해)사항입니다. 어렵진 않죠?
1) 16 MHz 8 bit Timer/Counter에서 1 ms( 0.001 sec = 1 ms = 1000 us)를 만들기.
먼저 결정된 부분을 보시면 클럭(16MHz)과 목표시간(1ms)이 결정이 되어 있고, 도구로서 8 bit Timer/Counter를 이용하려 한다는 것을 알 수 있습니다. 그럼 Prescaler별로 계산을 해 봅시다.
8 ===> 0.001 / 0.0000005 = 2000 개
64 ===> 0.001 / 0.000004 = 250 개
256 ===> 0.001 / 0.000016 = 62.5 개
1024===> 0.001 / 0.000064 = 15.625 개
목표시간을 위의 공통사항에서 먼저 계산한 Prescaler별 실제 소요시간으로 나누었습니다. Prescaler가 8일 경우에는 약 2000번이나 발생을 하지만 이를 채택할 수는 없습니다. 왜냐구요? 8 bit Timer/Counter는 말 그대로 0 부터 255까지의 숫자만 셀 수 있으니까요^^. 그럼 실제로 OCR0의 값을 넣기 위해 계산을 다시 해 보겠습니다.
P(8) 8 bit: 255 - (255 - 2000 + 1) => X
P(64) 8 bit: 255 - (255 - 250 + 1) => 249 <=== 결정!
P(256) 8 bit: 255 - (255 - 62.5 + 1) => 61.5
P(1024)8 bit: 255 - (255 - 15.625 + 1) => 14.625
P(8)의 경우에는 8 bit에 담기 어려운 숫자이므로 버립니다. P(256)과 P(1024)는 소숫점으로 나오므로 아깝긴 하지만 버립니다. 그렇다면 결정되는 것은 P(64)하나뿐이지요. 이제 남은것은 레지스터의 설정뿐입니다.
[설정]
Clock: 16 MHz
Prescaler: 64
OCR0: 249
간단하죠?
2) 16 MHz 16 bit Timer/Counter에서 10 ms( 0.01 sec = 10 ms = 10000 us)를 만들기.
8 bit Timer/Counter의 예를 들었으니 16 bit Timer/Counter도 한번 계산해 보도록 합시다. 위의 공통사항은 변함이 없습니다. 먼저 결정된 부분을 보시면 클럭(16MHz)과 목표시간(10ms)이 결정이 되어 있고, 도구로서 16 bit Timer/Counter를 이용하려 한다는 것을 알 수 있습니다. 자, 그럼 Prescaler별로 계산을 해 봅시다.
8 ===> 0.01 / 0.0000005 = 20000 개
64 ===> 0.01 / 0.000004 = 2500 개
256 ===> 0.01 / 0.000016 = 625 개
1024===> 0.01 / 0.000064 = 156.25 개
위의 예제를 이해하셨다면 비슷한 값이 나오는 것을 보고서 무릎을 탁 치셨을 겁니다. Prescaler 8에서는 20000번이 필요하다고 나오는데 일단 아래처럼 계산을 한번 해 봅시다.
P(8) 16 bit: 65535 - (65535 - 20000 + 1) => 19999
P(64) 16 bit: 65535 - (65535 - 2500 + 1) => 2499
P(256) 16 bit: 65535 - (65535 - 625 + 1) => 624 <=== 결정!
P(1024)16 bit: 65535 - (65535 - 156.25 + 1) => 155.25
계산을 해보니 이번에는 딱 맞아떨어지는 숫자가 많이 보입니다. 그러나 그중 최소의 숫자로서 목표한 시간을 표현할 수 있는 것은 Prescaler 256이므로 이를 사용하도록 합니다. (16 bit Timer/Counter이므로 0 부터 65535까지의 값을 가질 수 있습니다.)
[설정]
Clock: 16 MHz
Prescaler: 256
OCR1: 624
3) 실제코딩.
위처럼 계산을 해서 값은 나왔는데 이를 자신의 소스에 어떻게 적용을 할까요? 한번 해 봅시다.
자신의 코드중에서 타이머 관련 설정하는 부분의 내부에 아래와 같이 합니다.
예제1) 16 MHz, 1 ms, 8 bit Timer/Counter의 설정.
TCCR0 = 0x0c; /* CTC, mode = 2 */
OCR0 = 249; /* Interrupt with every 249th prescaled clock. */
TCNT0 = 0x00; /* Clear Timer/Counter0 value. */
TIMSK = 0x02; /* Regist. Enable OC0 interrupt. */
예제2) 16 MHz, 10 ms, 16 bit Timer/Counter의 설정.
TCCR1A, TCCR1B, TCCR1C의 설정은 데이타시트를 참고해서 직접 해보시기 바랍니다.
OCR에 넣을 값이 16 bit이기 때문에 아래와 같이 합니다. 주의할 점은 상위바이트부터 입력한다는 것뿐입니다.
OCR1AH = (624 >> 8);
OCR1AL = 624 & 0xFF;
TCNT를 클리어 하려면 아래와 같이 합니다.
TCNT1H = 0x00;
TCNT1L = 0x00;
TIMSK를 설정하여 인터럽트를 발생시킵니다.
자신이 원하는 타이머를 선택 및 제반사항을 설정하였다면 실제구동부를 작성합니다.
저는 winavr, avr-gcc를 사용하므로 간단히 아래의 예제와 같이 합니다.
ISR( TIMER0_COMP_vect )
{
; /* 이곳에 수행하고자 하는 부분을 넣으십시요. */
}
4) 그외에 궁금하신 부분이 있으시면?
MCU의 datasheet와 avr-gcc의 manual을 참조하십시요. 흔히 하는 이야기가 아니라, 궁금하신것은 모두 이 문서들에 다 나와 있습니다. ISR() 매크로가 실행될 때 cli()가 되고 종료할 때 sei()가 된다는 간단한 내용같은 것들을 인터넷에 그냥 물어보시겠습니까? 서로간에 시간낭비하지 말자구요^^
5) 그래도 궁금한걸 묻고 싶으시다구요?
저는 하드웨어는 잘 모릅니다. 저항값 계산도 헷갈리지까요,..ㅜ.ㅠ 그러나 밥벌어 먹는거와 관련된, 소프트웨어 다루는 것은 좀 압니다.(;p) 정히 물으시려면 이 블로그에 남겨 주셔염. 더 잘 아시는 다른 분들이 답변하실 수도 있습니다.^^
[출처] timer/counter |작성자 cok2529