레이블이 AVR인 게시물을 표시합니다. 모든 게시물 표시
레이블이 AVR인 게시물을 표시합니다. 모든 게시물 표시

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

2010년 3월 18일 목요일

타이머/카운터0 Overflow 사용 예

- AVR 외부에 부착되어 있는 크리스탈(보통 16Mhz)를 내부의 정해져 있는 분주비(0, 8, 32, 64, 128, 256, 1024 중 선택)로 나누어서 나오는 시간에(예로 16Mhz / 1024 = 15625hz 이걸 주기로 바꾸면 1/15625 = 0.000064(64us)가 됩니다.)

 

- TCNT0(0 ~ 255까지 채울 수 있는 물통이고 위의 예로 가정하면 64us마다 레지스터가 1씩 차게 된다)라는 레지스터를 한 칸씩 채우게 되는데 이 TCNT0라는 레지스터가 꽉 차고 넘치는 순간(255인 상태에서 +1을 해줘서 다시 0이 되는 순간)에 인터럽트 함수의 호출이 이루어 진다고 생각하시면 됩니다.

 

- TCCR0

  • Bit 7 : PWM모드가 아닌 경우에만 유효
  • Bit 6 : WGM00, WGM01(파형 발생 모드) ==> 타이머/카운터 0의 동작모드를 지정하는 비트
  • Bit 5, 4 : COM01, COM00
  • Bit2, 0 : CS02, CS01, CS00(Clock Select)

- TCNT0

  • 8비트 카운터 값을 저장하고 있는 레지스터
  • 읽고 쓰기 동작을 할 때 접근 가능하다.

- TIMSK

  • Bit 1 : OCIE0
    • OCIE0 = 0 : 타이머/카운터0 출력 비교 매치 인터럽트 사용불가
    • OCIE0 = 1 : 타이머/카운터0 출력 비교 매치 인터럽트 활성화
  • Bit 0 : TOIE0
    • TOIE0 = 0 : 타이머/카운터0 오버플로우 인터럽트 사용불가
    • TOIE0 = 1 : 타이머/카운터0 오버플로우 인터럽트 활성화

- 예

  • 16Mhz / 64 = 250000, 주기 1/250000 = 4us
  • TCNT0 레지스터가 1씩 증가하는 데 걸리는 시간 = 4us
  • TCNT0에 미리 채워 넣은 값 = 6, 남은 공간 = 249
  • TCNT0를 꽉 채우기 위해 남은 공간은 155고 넘치는 순간 인터럽트가 발생하기 때문에 +1, 0.000004 * 250 = 0.001
  • 인터럽트 발생 주기 0.001초
  • 인터럽트 함수에서는 fnd_a(예)라는 변수가 1000이 되면 fnd_b가 증가(예) 한다.
  • 0.001초 마다 인터럽트 함수에 들어가는데, 거기에 fnd_b가 1 증가하기 위해서는 fnd_a가 1000이 되어야 한다.
  • 이는 대략 1초를 만들기 위함인데, 0.001 * 1000 = fnd_b가 1증가 하는 시간이 된다.
  • 즉 fnd_b가 1 증가하는데 걸리는 시간은 대략 1초가 된다.

 

출처 : AVR Study Cafe