난데없이 왠 계산?
미적분은 아니니까 너무 걱정마시고, 그냥 곱셈, 나눗셈만 합니다. 그래도 충분 합니다.
아래를 먼저 보시죠. 와 닿으시나요? 아니면 멍~~ 하시나요?
하나하나 차근하게 설명합니다.
1번은,
클럭주파수입니다. 즉, 수정발진자 주파수라고 생각하면 됩니다.
주파수의 역수가 1주기 이므로, 8Mhz의 주기는 0.000000125초 입니다. 인간의 시간 관념에서 보면 순식간이죠!!
2번은,
Timer0에 설정하는 분주비(Prescale)입니다. 우선 Prescale = 1/1024 로 설정하면 마스터클럭이 1024번 진동할때 Prescale을 통해서는 1번 진동합니다. 그리고, 이것이 TCNT0를 하나씩 증가시키는 것이겠죠. 계산을 하면, Prescale이후의 주파수는 7813Hz이고 주기는 0.000128초 입니다. 주파수의 경우는 1024로 나누면 되고, 주기는 1024만큼 곱해주면 바로 나오겠죠.
3번은,
각 단위시간(ms)을 재기 위해서 TCNT0의 값을 몇 정도 카운트해야 하는가를 계산한 것입니다. 1ms(0.001se)의 도달하려면 0.000128(sec)가 7.813번 있으면 되지요. 그렇다면 반올림해서 8로 잡고, TCNT0레지스터를 0부터 8까지 카운트하고 Overflow발생시키고 나서 TCNT0를 초기화(zero)하고 다시 0부터 8까지 카운트하면 되겠죠?
근데, 문제는 지난 글에 설명했듯이 TCNT0의 Overflow는 레지스터가 꽉 차고있는 상태에서 마지막 물 한방울이 더해질때 발생하잖아요.. 그렇기 때문에 약간의 기교가 필요한 것입니다. 그래서 4번이 필요한 것입니다.
4번은,(기교)
3번에서 계산한 약8번의 카운트를 0부터 시작하는 것이 아니라 TCNT0= 248부터 시작하면 어떨까요? 그러면, 8번 카운트할때 255 --> 0로 변하면서 Overflow가 발생하지 않을까요?
예, 맞습니다. 그렇게 하면됩니다. TCNT0의 값을 248부터 시작하고 오버플로우가 발생하면 TCNT0의 값을 다시 248로 셋팅하면 됩니다. 바로 이것이 4번의 내용입니다.
참고로, 위의 4번은 10ms를 발생하려면 약78번 카운트하면 됩니다.(계산해 보세요) 그러므로 TCNT0 = 256 - 78 로 하면 됩니다.
그럼, 1초는 어떻게 하나요? 10ms를 100번 카운트하면 되지 않을까요? 그럼 됩니다.
아니면 타이머0를 사용하면 굳이 위으 4번과 같이 하지 않더라도 간단히 할 수 있습니다. 왜냐하면 Timer1은 16비트 크기의 TCNT1을 갖고 있습니다. 그러므로, TCNT1 = 65536 - 7813 으로 하면 정확히 1초마다 Overflow가 발생하며, 그때 실행하는 ISR(Timer1_OVF_vect)에서 원하는 일을 하면 됩니다.
아래에 샘플 테스트 코드를 좀 적었습니다.
10ms마다 PD5 포트를 ON/OFF(토글)시킵니다. 참고하세요.
// 2. Timer0 Init : Fast PWM Mode
void init_TM0_NORMAL_mode()
{
// TOP = 255(0xFF)
// WGM02:00 = 000 (Normal mode)
// CS02:00 = 101 (Prescale = 1024 ) --> 122Hz(about 8ms = 8000000/1024 = 122)
// Pin mode = None inverting mode
TCCR0A = 0x00; // Mode : Normal Mode
TCCR0B = _BV(CS02) | _BV(CS00); // Prescale = 1/1024
TCNT0 = 256- 78; // 10ms 발생하기 위해서 78번 카운트(즉, 178 ~ 255)
TIMSK0 = _BV(TOIE0); // 오버플로우 인터럽트 인에이블(TOIE0)
}
// 타이머0 오버플로우 인터럽트 서비스 루틴
ISR(TIMER0_OVF_vect)
{
TCNT0 = 256 - 78;
PORTD ^= _BV(PD5); // PD5 토글
}
아래 프로테우스에서는 분홍색이 10ms단위로 ON/OFF하는 PORTD5 입니다.
노란색은 100ms로 ON/OFF합니다. main에 있는 while(1) 루프 안에 있습니다.
어떤가요??
댓글 없음:
댓글 쓰기