타이머 카운터
타이머는 8비트 타이머와 16비트 타이머가 있다.
8비트 타이머에 대해 알아보자.
16Mhz = 16000000hz <- Atmega328p의 클락이다.
동작을 맞춰주기 위해서 클락이 필요하다.
16Mhz / 1024 = 15625 <- 1초당 발생 펄스 수이다. <- 초당 발생되는 클럭이라고 정의할 수 있다
1024는 분주율이다.
분주비는 8, 64, 256, 1024를 선택할 수 있다. 그중에 1024를 많이 쓴다.
분주율을 사용하는 이유는 1초 / 16Mhz를 하면
하나의 펄스가 만들어지는데 걸리는 시간이 나노 세컨드 단위로 나온다.
즉, 값이 너무 작아서 분주율을 적용한 것이다.
그리고 8비트가 timer 발생 비트의 수라고 한다면, 8비트로 표현할 수 있는 가지 수는 2^8 = 256이다.
16Mhz / 1024 / 256 = 약 61 <- 1초에 펄스를 61번 발생한다는 의미.
256을 61번 돌아야 15625hz가 된다.
61은 그 단위가 hz이다. ( 펄스가 초당 61번 발생. )
2로 나누는 이유는 1초가 아닌 0.5초를 표현하기 위함.
이에 대한 근사치로 61은 64로 둬서 이를 2로 나누면 32가 된다.
그리고 1 / 61을 해서 하나의 펄스를 만드는데 걸리는 시간이다. ( 주기 )
1 / 61 = 0.016초이다.
소스 내용
/*
타이머 카운터를 이용한 LED의 ON/OFF
*/
#define F_CPU 16000000UL // 16MHz
#include <avr/io.h>
#include <avr/interrupt.h>
int count = 0; // 오버플로우 개수 카운터
int state = 0; // LED 상태
/* Interrupt Service Routine */
ISR(TIMER0_OVF_vect) // 인터럽트는 오버플로우가 발생했을 때, 호출된다.
{
/* 0.5초마다 LED의 ON/OFF를 제어 */
count += 1;
// 8bit timer에 1024 분주율을 적용하면, 약 61hz가 된다.
// 대략 0.5초는 32hz가 발생
if ( count == 32 ) // 0.5초가 지나면
{
count = 0;
state = !state;
if ( state ) PORTB = 0xff;
else PORTB = 0x00;
}
}
int main(void)
{
// 1. 디지털 12번핀을 출력으로 사용하고 타이머 카운터를 설정
// D12번핀과 LED와 220옴 저항을 연결.
DDRB = 0x10;
PORTB = 0x00;
// 2. TCCR설정(분주비 1024), TIMSK(인터럽트 허용)
// TCCR0B |= ( 1 << CS02 ) | ( 1 << CS00 );
// TOIE0는 레지스터(0-base 기준)의 1번째 비트를 의미
TCCR0B |= ( 1 << CS02 );
TCCR0B |= ( 1 << CS00 );
TIMSK0 |= ( 1 << TOIE0 ); // 해당 비트를 1로 설정해서 인터럽트 허용
sei(); // 전역 인터럽트 허용
while (1)
{
}
}
16비트 타이머를 사용해서 0.5초마다 LED를 On/Off해보자.
16비트에서는 8192가 16비트로 표현할 수 있는 가지 수이다.
8비트 카운터를 사용하냐 16비트 카운터를 사용하냐에 따라 OCR값이 달라진다.
16비트를 사용할 때는 최대 가지수 2^16 = 65536
15625hz / 65536 = 0.249hz
소스 내용
/*
값이 같아질 때, 인터럽트가 발생한다.
인터럽트의 발생에 따라 5V가 인가되거나 0V가 인가된다.
아두이노의 9번핀에 LED를 연결한다.
*/
#define F_CPU 16000000UL // 16MHz
#include <avr/io.h>
#include <avr/interrupt.h>
/* Interrupt Service Routine */
ISR(TIMER1_COMPA_vect)
{
TCNT1 = 0;
}
int main(void)
{
// 타이머1을 이용하여 비교하는 내용
// 1. 타이머1의 컨트롤 레지스터를 분주율 1024로 설정(101로 설정)
TCCR1B |= ( 1 << CS10 );
TCCR1B |= ( 1 << CS12 );
// 2. 카운터랑 설정값을 비교하는 설정
OCR1A = 0x2000; // 0x2000 = 0010 0000 0000 0000 = 1 * 2^13 = 8192
// 0x2000는 십진수로 8192이며, 이는 0.5초를 의미한다.
TCCR1A |= ( 1 << COM1A0 );
// 3. 출력핀을 설정
DDRB |= ( 1 << PORTB1 ); // OCA1핀을 의미(아두이노의 D9번핀)
// 4. 타이머 마스크 설정
TIMSK1 |= ( 1 << OCIE1A ); // 비교하여 인터럽트 발생
sei(); // 전역 인터럽트 허용
while (1)
{
}
}
서보 모터를 제어하는 내용을 알아보자.
소스 내용
/*
서보모터를 아두이노의 9번핀에 연결한다.
*/
#define F_CPU 16000000UL // 16MHz
#include <avr/io.h>
#include <util/delay.h>
#define PULSE_MIN 1000 // 최소 펄스 지점
#define PULSE_MAX 5000 // 최대 펄스 지점
void InitTimer(void)
{
TCCR1A |= ( 1 << WGM11 );
TCCR1B |= ( 1 << WGM12 ) | ( 1 << WGM13 );
TCCR1B |= ( 1 << CS11 ); // 분주율 8, 2Mhz
ICR1 = 40000; // 20ms 주기
TCCR1A |= ( 1 << COM1A1 ); // 비 반전 모드
DDRB |= ( 1 << PB1 ); // 디지털 9번핀
}
int main(void)
{
InitTimer();
int i, j;
while (1)
{
// 서보 모터를 조금씩 움직이는 내용
for ( i = PULSE_MIN; i <= PULSE_MAX; i += 20 )
{
OCR1A = i;
_delay_ms(50);
}
for ( j = PULSE_MAX; j >= PULSE_MIN; j -= 20 )
{
OCR1A = j;
_delay_ms(50);
}
}
}