2010년 3월 26일 금요일

타이머 공부

< 일정한 시간에 인터럽트를 거는 2가지 방법 >

 

* TCNTx 레지스터

  • 클럭을 카운터한 값을 가지고 있는 레지스터
  • 16Mhz의 시스템 클럭을 쓰는 시스템에서는 TCNT0의 값은 1/16000000hz = 62.5ns마다 1씩 증가한다.(분주비 1일 경우)

* 노멀모드

  • 8bit 타이머의 경우 0부터 255까지 계속 증가하고 255를 넘으면 다시 0부터 증가를 반복한다.
  • 타이머 오버플로우 인터럽트를 허용해 놓으면 255에서 0으로 갈 때 인터럽트가 걸리게 된다.
  • 원하는 시간간격으로 인터럽트를 주기 위해 인터럽트 서비스 루틴 첫부분에 TCNT값을 쓰면 된다.
  • 예를 들어, main 클럭이 16Mhz이고 분주비가 256이면 62500hz가 되고 주기는 16us인데 x256(8bit)하면 4.09ms마다 인터럽트가 걸리게 된다. 그런데 2ms마다 인터럽트를 걸고 싶으면 인터럽트가 걸리자마자 TCNT의 값을 130으로 적어준다. 그럼 TCNT의 값은 130 ~ 255까지의 계수동작을 반복하고 2ms마다 인터럽트가 걸리게 된다.
  • 수학 공식으로 보기
    • 1 / 16000000 = 0.0000000625 = 62.5ns
    • 256분주일 때, 1 / 16000000 * 256 = 0.000016 = 16us
    • 8bit 타이머 오버플로우 인터럽트 허용시(TCNT0은 0부터 255까지(256개) 반복)
    • 16us * 256 = 4.096ms
    • TCNT0을 130부터 255까지 반복하게끔 했을 때
    • 16us * ( 255 - 130 ) = 2ms
  • TCNT0을 사용해서 원하는 시간 만드는 공식
    • TCNT0 = (타이머 TOP값(8bit 타이머이므로 2^8 = 256) - 1) - ( (원하는 시간) * (클럭 / 분주비) )
    • 2ms를 만들 때, TCNT0 = (256 - 1) - ( 0.002 * (16000000 / 256) ) = 130

 

< Normal mode 사용법 >

* 일반모드로 1초 만들기

#include <avr/io.h>
#include <avr/interrupt.h>
unsigned int count = 0;

/* Timer/Count 0 Overflow 인터럽트 2ms마다 인터럽트가 걸리므로 500번 인터럽트가 거리면 1초임 */
SIGNAL(SIG_OVERFLOW0)    // 0.002 * 500 = 1s
{
    TCNT0 = 0x82;            // 130
    if( ++count == 500 ){    // 1초마다 LED 점멸, 1초 켜짐, 1초 꺼짐
        PORTB = ~PINB;
        count = 0;
    }
}

int main(void)
{
    DDRB = 0xFF;
    PRTB = 0xFF;

    // TCNT0 = (256 - 1) - ( 0.002s * (16000000 / 256) ) = 130
    TCCR0 = 0x06;    // 16Mhz / 256 = 62.5Khz ==> 16us
    TCNT0 = 0x82;    // (256 - 125) = 130 ==> 0x82
    TIMSK = 0x01;    // Timer/Count Interrupt Mask Register => 0x01 : overflow enable
    sei();

    while(1)    ;
}


 

 

 

* CTC 모드

  • TCNT0의 값이 0부터 증가하다가 OCR0에 써준 값과 일치하면 다음 클럭에서 인터럽트가 걸리면서 TCNT의 값이 0으로 초기화된다.
  • 2ms마다 인터럽트가 걸리도록 하기
    • 1 / 16000000 = 0.0000000625 = 62.5ns
    • 256분주일 때, 1 / 16000000 * 256 = 0.000016 = 16us
    • 8bit 타이머 오버플로우 인터럽트 허용시 (TCNT0은 0부터 255까지 반복)
    • 16us * 256 = 4.096ms
    • TCNT0을 OCR0 = 124와 비교해서 TCNT0이 124가 되면 다음 클럭에서 인터럽트를 발생
    • 16us * (124 + 1) = 2ms
  • 공식 정리
    • OCR0 = ( (원하는 시간) * (클럭 / 분주비) ) - 1
    • OCR0 = ( 0.002 * (16000000 / 256) - 1 = 124

< CTC 모드 사용법 >

* 소스 구성

인터럽트 헤더파일

인터럽트 서비스 루틴
{
    인터럽트 걸렸을 때 동작
}

main()
{
    인터럽트 설정
   
    while(1){
        메인 동작
    }
}


* 타이머를 CTC 모드 0.5초 인터럽트를 만들기

#include <avr/io.h>
#include <avr/interrupt.h>
ISR(SIG_OUTPUT_COMPARE1A)
{
    PORTA ^= 0xFF;
}

int main(void)
{
    DDRA = 0xFF;    // PORTB 출력 모드
    TCCR1A = 0x00;
    TCCR1B = 0x0C; // CTC 모드, 256 분주
    TCCR1C = 0x00;
    OCR1AH = (31249 >> 8);    // 16Mhz / 256 / (1 + 31249) = 2hz = 0.5초
    OCR1AL = 31249 & 0xFF;
    TIMSK = 0x10;    // 타이머1 출력 비교 인터럽트 A 허용
    ETIMSK = 0x00;
    TIFR = 0x00;       // 플래그 클리어
    ETIRF = 0x00;
    sei();                // 글로벌 인터럽트 허용

    while(1)    ;
}


* 주기 =                F                 (F : 시스템 클럭)              16000000

                ---------------                                      ----------------    =  2hz

                 N x ( 1 + OCR )       (N : 분주비)               256 x (1 + 31249)

 

* 시간 = 1 / 주기 = 1 / 2 = 0.5초

* CTC 모드에서는 TCNT와 값이 일치하면 다음 클럭에서 인터럽트가 발생하기 때문에 31250이 아니라 31249가 된다.

 

 

출처 : 당근이 AVR

댓글 없음:

댓글 쓰기