'Timer'에 해당되는 글 2건

  1. 2016.11.23 Timer 3개 모두사용하는 예제
  2. 2016.11.15 AVR Timer
2016. 11. 23. 22:53

ATMega328P 에는 총 3개의 타이머가 있습니다.


타이머0 - 8비트

타이머1 - 16비트

타이머2 - 8비트


모드는 모두 CTC 모드로 작성 했습니다.


Clear Timer on Compare match


CTC모드는 TCNT 값이 0부터 증가 해서 OCR 에 정의된 값 까지 갑니다.

(여기서 8비트는 최대 255 까지 밖에 ㅜㅜ)


1초를 구하기 위해 16비트는 설정만으로 가능하지만, 8비트는 모두 세지 못합니다.


그래서 타이머 인터럽트 내에 몇번 반복 시켜서 1초를 구하게 됩니다.



8비트로 1초 구하는 방법...

CPU가 16 MHz 로 동작을 가정합니다.


1. 1초에 16000000 Hz 진동합니다.

2. prescale 256 으로 셋팅했으므로 256 Hz 당 1 타이머 count 가 증가합니다.

    즉, 16000000 / 256 = 0.000016 초에 1 Hz씩  ( 1 hz / 16 us)

3. OCR에 정의된 값까지 증가하고 0으로 되돌아 갑니다.

    125번 반복하면 걸리는 시간 = 0.000016 x 125 = 0.002  125번 반복에 0.002초 ( 2 ms) 

    125번 이지만 0 부터 시작하므로 -1을 하여 124를 설정합니다.

4. 타이머 인트럽트가 2ms 당 1번씩 호출되므로 500번 마다 작동하도록 설정하면 1초 마다 동작하도록 설정 됨.


복잡한 것 같지만... 차근차근 읽어보면 그냥 산수 입니다. ^__^


16비트 타이머의 경우 OCR 에 정의할 수 있는 값이 거대해서 인트럽트에서 별도 반복 없이도 1초 구하는 것이 가능합니다.



동작영상



타이머 3개 모두 동작시킨 풀 소스코드

예제는 16비트 1초, 8비트 1/2초, 8비트 1/4초 로 세팅되어 있습니다.

https://github.com/haebi/AVRExample/blob/master/atmega328p/timer2/timer2.cpp


#define F_CPU 16000000UL // Set CPU Frequency 16 MHz


#define setbit(PORTX, BitX) PORTX |= (1 << BitX) // set bit to 1

#define clrbit(PORTX, BitX) PORTX &= ~(1 << BitX) // clear bit


#include <avr/io.h>

#include <util/delay.h>

#include <avr/interrupt.h>


int count0 = 0;

int tick0  = 0;


int count1 = 0;


int count2 = 0;

int tick2  = 0;


void setTimer0()

{

// 1sec for 8-bit

// 1 / 16000000 = 0.0000000625 * 256 (prescale) = 0.000016 (usec)

// 0.000016 (usec) * 125 (multiplier) = 0.002 * 1000 = 2 (ms)

// 2 (ms) * 500 (count) = 1 (sec)


TCNT0   = 0;

// Set CTC compare value with a prescaler of 256

OCR0A   = 124; // 125 -1 


// 0 1 0 - Configure timer 0 for CTC mode [p.106]

clrbit(TCCR0B, WGM02); // 0

setbit(TCCR0A, WGM01); // 1

clrbit(TCCR0A, WGM00); // 0


// 1 0 0 - Start timer at Fcpu/256 [p. 108]

setbit(TCCR0B, CS02); // 1

clrbit(TCCR0B, CS01); // 0

clrbit(TCCR0B, CS00); // 0

// Enable CTC interrupt

setbit(TIMSK0, OCIE0A);

}


void setTimer1()

{

// OCR1A =  OCR1AH + OCR1AL = (8 + 8 = 16 bit)

// Dec 15624 : repeat 0 ~ 15624 (15625 = 0)

OCR1A = 0x3D08; 


// Page 171-172

// Waveform Generation Mode : (TCCR1B:4,  TCCR1B:3, TCCR1A:1, TCCR1A:0)

// 0 1 0 0 - Set to TOP OCR1A

setbit(TCCR1B, WGM12); // Configure timer 1 for CTC mode


// Page 173

// Clock Select Bit ( CS12 CS11 CS10 )

// 0 1 0 - clock I/O / 1024 (From prescaler)

setbit(TCCR1B, CS12);

setbit(TCCR1B, CS10);


// Page 135

// TIMSK1 – Timer/Counter1 Interrupt Mask Register

setbit(TIMSK1, OCIE1A); // Enable CTC interrupt

}


void setTimer2()

{

// 1sec for 8-bit

// 1 / 16000000 = 0.0000000625 * 256 (prescale) = 0.000016 (usec)

// 0.000016 (usec) * 125 (multiplier) = 0.002 * 1000 = 2 (ms)

// 2 (ms) * 500 (count) = 1 (sec)


TCNT2   = 0;

// Set CTC compare value with a prescaler of 256 

OCR2A   = 124; // 125 -1 


// 0 1 0 - Configure timer 2 for CTC mode [p.155]

clrbit(TCCR2B, WGM22); // 0

setbit(TCCR2A, WGM21); // 1

clrbit(TCCR2A, WGM20); // 0


// 1 1 0 - Start timer at Fcpu/256 [p.156-157]

setbit(TCCR2B, CS22); // 1

setbit(TCCR2B, CS21); // 1

clrbit(TCCR2B, CS20); // 0


// Enable CTC interrupt

TIMSK2 |= (1 << OCIE2A);

}


// Main Function

int main(void)

{

setTimer0(); //  8 bit timer (TCNT0)

setTimer1(); // 16 bit timer (TCNT1)

setTimer2(); //  8 bit timer (TCNT2)


sei(); // Enable global interrupts


setbit(DDRD, DDD6); // Set output PD6

setbit(DDRD, DDD7); // Set output PD7

setbit(DDRB, DDB0); // Set output PB0


while (1)

{


}

}


// Timer0 interrupt

ISR (TIMER0_COMPA_vect)

{

tick0++;


// if (!(tick0 == 500)) // 1 sec

if (!(tick0 == 125)) // 1/4 sec

return;


tick0 = 0;


if (count0 == 0)

{

count0++;

clrbit(PORTB, PORTB0);

}

else

{

setbit(PORTB, PORTB0);

count0 = 0;

}

}


// Timer1 interrupt

ISR (TIMER1_COMPA_vect)

{

// already 1 sec


if (count1 == 0)

{

count1++;

clrbit(PORTD, PORTD6);

}

else

{

setbit(PORTD, PORTD6);

count1 = 0;

}

}


// Timer2 interrupt

ISR (TIMER2_COMPA_vect) 

{

tick2++;


// if (!(tick2 == 500)) // 1 sec

if (!(tick2 == 250)) // 1/2 sec

return;


tick2 = 0;


if (count2 == 0)

{

count2++;

clrbit(PORTD, PORTD7);

}

else

{

setbit(PORTD, PORTD7);

count2 = 0;

}

}




Posted by 해비
2016. 11. 15. 23:16

AVR 타이머 예제


앞에서 while 문에 타이밍 에 따라 분기 시켰는데...


이게 더 정답일듯 하다. 왜냐면.. 그 앞의 함수 실행 시간 동안 미세하게 나마 지연(?)같은 것이 발생할 테고... 그러면 미미하게나마 타이밍이 틀어질 수 있으니까...


타이머 사용하면 타이밍에 맞게 알아서 인트럽트 발생시켜 주니까 이쪽이 타이밍이 더 정확하게 동작할 것 같다.


스레드 대신 타이머... 로 생각하면 되려나...?

...일단은 어렵다!!


타이머가 몇개 인지는 모르겠는데... 타이머 갯수만큼 스레드 처럼 부려먹을수 있을지 조사해 봐야겠다 -_-;;


모드가 아래 2가지가 있는 듯 한데 

Normal - 정확하지 않은 타이밍

CTC - 정확한 타이밍


이것만 보면 당연히 CTC만 써야되고 Normal 은 못쓸것이 된다.

장/단점 이라던가... 더 찾아봐야 겠다.


음... 이 코드는 OCR1A가 있는 것으로 봐서 CTC 모드 인 것으로 보인다. (아직 코드 이해가 -_-)


일단 갖고 놀다보면 이해가 될거라 굳게 믿고 있음!! 꼭!!


#define F_CPU 16000000UL // Set CPU Frequency 16 MHz


#define setbit(PORTX, BitX) PORTX |= (1 << BitX) // set bit to 1

#define clrbit(PORTX, BitX) PORTX &= ~(1 << BitX) // clear bit


#include <avr/io.h>

#include <avr/interrupt.h>


int count = 0;


// Main Function

int main(void)

{

OCR1A = 0x3D08;


setbit(TCCR1B, WGM12);

setbit(TIMSK1, OCIE1A);

setbit(TCCR1B, CS12);

setbit(TCCR1B, CS10);


sei();


DDRD |= (1 << DDD6); // Set output PD6

DDRD |= (1 << DDD7); // Set output PD7


while (1)

{


}

}


ISR (TIMER1_COMPA_vect)

{

if (count == 0)

{

count++;

clrbit(PORTD, PORTD6);

}

else

{

setbit(PORTD, PORTD6);

count = 0;

}


일단... 돌아는 갑니다... 직접 칩에 넣고 돌려서 확인 했어용~ 




Posted by 해비