타이머/카운터란?
1. 개요
타이머 카운터(Timer/Counter)는 마이크로컨트롤러에서 주어진 시간 동안의 카운트를 수행하는 하드웨어 모듈이다. 이 모듈은 내부적으로 클럭 신호를 카운트하고, 사용자가 설정한 카운터 값에 도달하면 인터럽트를 발생시키거나 외부 장치를 제어하는 등의 기능을 수행할 수 있다.
쉽게 말해서, 일정한 시간 동안 발생되는 클럭을 세서 시계로 쓸수도 있고, 클럭이 일정 횟수가 카운팅되면 LED를 켤 때, 카운팅을 위해 쓸 수도 있는 것이 타이머/카운터이다.
2. 타이머/카운터를 쓰는 이유
간단한 예로 시계를 구현한다고 하자. _delay() 함수를 통해서 시계를 구현할 수 있지만, 미세한 차이가 누적된다면 그다지 정밀하지 않을 것이다. 하지만 내부의 클락(타이머/카운터)을 사용할 경우 더욱 정밀한 시간 계산이 가능하므로 클럭을 사용한 타이머/카운터 사용한다.
3. 8비트 타이머/카운터? 16비트 타이머/카운터?
타이머/카운터면 타이머/카운터인데 왜 앞에다가 비트 수를 붙여놔서 헷갈리게 할까? 다 이유가 있다.
8비트는 8자리로 이루어진 비트이다. 8비트가 셀 수 있는 경우의 수는 2의 8승, 즉 256번이다.
만약 16비트의 경우 2의 16승 65536번이다.
즉, 비트 수가 많은 타이머/카운터를 사용하면 좋은 점은 작은 비트 수의 타이머 카운터보다 더 긴 시간 간격을 측정하거나 더 많은 이벤트를 세는 등의 작업을 수행할 수 있다.
4. 그러면 분주(Prescaler)란 무엇인가?
ATMEGA128은 클럭이 한 번 움직일 때마다 한 동작씩 수행한다. 그런데 ATMEGA128은 16MHz의 클럭을 갖고 있는데, 이는 1초동안 1600만번의 동작이 가능하다는 뜻이다. 다르게 말하면 1초를 측정하는 시스템 입장에서 1초 한 번 세려고 1600만번을 카운팅한다고 생각해보면 카운터 값 저장 레지스터, 동작 모든 것이 낭비이다. 이런 낭비를 줄이기 위한 개념이 바로 분주(Prescaler)이다.
먼저 이 클럭을 쪼개기 위해서는 '분주비(prescaler)' 라는 개념이 나온다. 간단하게 말하면, 1600만번을 다 세기에는 카운트를 많이 해야하므로 큰 뭉치로 묶어 계산을 간단하게 만들 수 있다. 아래의 예시를 보자.
만약 카운팅 1600만번을 다 센다면 1초이고, 1600만번 중 1번을 세는 시간(주기 T)을 구해보자. 그렇다면 다음과 같다.
주기 T = 1 / 16000000 = 0.0000000625초
1초를 세기 위한 카운팅 횟수 : 16000000회
이 주기를 몇 번 세면 1초가 될까? 계산이 끔찍해질 수 밖에 없다.
따라서 간단하게 위에서 말한 '분주비' 개념을 적절하게 사용할 것이다. 64분주를 예로들면 다음과 같다.
주기 T = 64 / 16000000 = 0.000004초
1초를 세기 위한 카운팅 횟수 : 250000회
만약 1초를 센다면, 분주를 쓰지 않을 때보다 카운트 하는 횟수가 25만번으로 현저하게 줄어들었다. 이 64분주를 이용한 주기를 이용하여 코딩으로 카운트 변수를 적절하게 쓴다면 _delay함수보다 훨씬 더 정확하고, 많은 카운팅 없이 쉽게 1초를 셀 수 있고, 정확한 시간에 따른 기능을 구현할 수 있다는 것이다.
위에서 본 타이머/카운터를 사용한 분주의 개념을 예시로 다시 정리해보면,
8비트 타이머/카운터는 최대 256번까지 카운팅할 수 있다고 했고, 64분주의 주기 T는 4us이다.
그렇다면 이 8비트 타이머 카운터가 64분주 주기 T를 256번을 센다면 1.024ms가 걸리게 됨을 알 수 있다.
만약 이를 코딩으로 250번만 세도록 설정한다면 비교적 정확한 1ms를 만들 수 있음을 알 수 있다.
이것을 초 단위 변수를 추가하여 1000번 카운트하면 1초로 만들고, 초 단위 변수를 60번 카운트하면, 1분이 된다. 이러한 방식으로 시계를 만들 수 있다는 것이다. 간단한 기능 구현은 아래 7번으로 가면 코드를 확인할 수 있다.
5. ATmega128A 타이머/카운터의 레지스터
레지스터들을 간단히 소개하겠다. 레지스터 별 디테일한 비트는 검색으로 찾아봤으면 좋겠다..!
TCNTn (n이 0과 2는 8비트, 1과 3은 16비트) : 타이머/카운터의 카운트 값을 저장하는 레지스터 앞서 말한 카운터에서 8비트는 256번, 16비트는 65536번 까지 카운트할 수 있다고 언급했는데, 이 카운트 값을 저장하는 레지스터이다.
OCR0,2(8비트) / OCR1A,3A,OCR1B,3B (16비트): 출력 비교 레지스터. 타이머/카운터의 카운트 값과 비교하여 출력 신호를 제어한다.
TCCR0,2(8비트) / TCCR1A,3A, TCCR1B,3B(16비트): 타이머/카운터 제어 레지스터. 타이머/카운터의 모드, 클럭 소스, 프리스케일러 등을 설정한다.
TIMSK0(8비트)/TIMSK1, TIMSK2(16비트): 타이머/카운터 인터럽트 마스크 레지스터. 오버플로우 인터럽트와 출력 비교 인터럽트를 사용할지 여부를 설정한다.
TIFR0(8비트)/TIFR1, TIFR2(16비트): 타이머/카운터 인터럽트 플래그 레지스터. 인터럽트 발생 시 해당 비트가 설정된다.
6. ATmega128A 타이머/카운터의 인터럽트 벡터
타이머/카운터의 TIMSK0, TIMSK1, TIMSK2레지스터를 적절하게 사용하면 몇 가지의 인터럽트 벡터를 호출하여 사용할 수 있다.
참고 : n은 0과 2는 8비트 타이머/카운터, n이 1과 3일 때는 16비트 타이머/카운터이다.
Timer/Counter n Overflow Interrupt(TIMERn_OVF_vect):
Timer/Counter0이 TOP 값(256 / 65536)에 도달하여 오버플로우가 발생했을 때 실행되는 인터럽트
Timer/Counter n Output Compare Match Interrupt(TIMER0_COMP_vect):
Timer/Counter0의 출력 비교 레지스터와 TCNT0이 일치하여 출력 비교 매치가 발생했을 때 실행되는 인터럽트
7. 타이머/카운터를 이용한 소스코드
다음 코드는 8비트 타이머/카운터를 사용하고 16MHz의 클럭을 64분주로 설정하여 PORTA에 연결된 LED를 0.5ms마다 ALL ON/ALL OFF 시키는 코드이다.
#include <avr/io.h>
#include <avr/interrupt.h>
#define F_CPU 16000000UL
volatile uint8_t ms_count = 0;
ISR(TIMER0_OVF_vect) { //(4)의 타이머 오버플로우 인터럽트 벡터
// 타이머 0이므로 8비트 타이머/카운터이다.
//8비트 타이머/카운터는 256번을 카운팅하면 이 오버플로우가 발생
TCNT0 = 6; //주기 T 4us를 256번 카운트하면 1.024ms
//따라서 250번을 카운트하면 1ms 마다 인터럽트 발생
if(++ms_count == 1000) //1000번 카운팅되면
{
ms_count = 0; //ms카운트 초기화
}
}
int main() {
DDRA = 0xFF; // PORTA를 출력으로 설정
TCNT0 = 0; // 카운터 초기화
TCCR0A = 0; // 타이머/카운터 모드 설정
TCCR0B = (1 << CS01) | (1 << CS00); // 분주비 64로 설정
TIMSK0 = (1 << TOIE0); // 오버플로우 인터럽트 활성화
sei(); // 전역적으로 인터럽트 활성화
while(1) {
if(ms_count == 500)
{
PORTA = 0xFF; // LED ALL ON
}
else if(ms_count == 1000)
{
PORTA = 0x00; //LED ALL OFF
}
return 0;
}
먼저 메인함수에서 8비트 타이머/카운터, 64분주를 설정하고 오버플로우 인터럽트를 사용할 수 있도록 레지스터를 초기화한다. 그 다음 주기 T를 250번 카운트 하면 1ms마다 오버플로우 인터럽트를 발생시킨다. ms카운터를 1ms마다 증가시켜서 1초가 되면 초기화해주고, 메인함수에서 ms카운트를 이용해 0.5초마다 LED를 껐다, 켰다를 반복한다.
'AVR' 카테고리의 다른 글
[AVR] 6. ATmega128A의 PWM (0) | 2023.04.18 |
---|---|
[AVR] 5. ATmega128A의 인터럽트 (0) | 2023.04.09 |
[AVR] 3. ATmega128A UART 통신과 I2C 통신 (0) | 2023.04.05 |
[AVR] Pull-Up/Pull-Down 저항이란? (0) | 2023.03.31 |
[AVR] 2. ATmega128A 버튼 제어 (0) | 2023.03.30 |