[임베디드/C] 16비트 타이머/카운터
16 비트 타이머/카운터
16비트 타이머/카운터는 $2^{16}$개의 펄스를 셀 수 있다. 비교 일치 인터럽트를 3개까지 사용할 수 있다.
16비트 타이머/카운터 1,3번에 대한 인터럽트
각각 5개씩 모두 10개가 존재한다.
벡터 번호 | 인터럽트 | 벡터 이름 | 인터럽트 허용 비트 |
---|---|---|---|
12 | Timer/Counter 1 Input Capture Interrupt Enable | TIMER1_CAPT_vect | TICIE1 |
13 | Timer/Counter 1 Output Compare Match A Interrupt Enable | TIMER1_COMPA_vect | OCIE1A |
14 | Timer/Counter 1 Output Compare Match B Interrupt Enable | TIMER1_COMPB_vect | OCIE1B |
15 | Timer/Counter 1 Overflow Interrupt Enable | TIMER1_OVF_vect | TOIE1 |
25 | Timer/Counter 1 Output Compare Match C Interrupt Enable | TIMER1_COMPC_vect | OCIE1C |
26 | Timer/Counter 3 Input Capture Interrupt Enable | TIMER1_CAPT_vect | TICIE3 |
27 | Timer/Counter 3 Output Compare Match A Interrupt Enable | TIMER1_COMPA_vect | OCIE3A |
28 | Timer/Counter 3 Output Compare Match B Interrupt Enable | TIMER1_COMPB_vect | OCIE3B |
29 | Timer/Counter 3 Output Compare Match C Interrupt Enable | TIMER1_COMPC_vect | OCIE3C |
30 | Timer/Counter 3 Overflow Interrupt Enable | TIMER1_OVF_vect | TOIE3 |
오버플로 인터럽트
${2^{16} * prescale \over 16M}$의 시간간격이며 분주하지 않는 경우(prescale = 1) 4.096ms, 분주비가 256인 경우(prescale = 256) 약 1초가 나온다.
- LED 점멸 예제
#include <avr/io.h>
#include <avr/interrupt.h>
int state = 0;
ISR(TIMER_OVF_vect)
{
state = !state;
if(state) PORTB = 0x01;
else PORTB = 0x00;
}
int main(void)
{
DDRB = 0x01;
PORTB= 0x00;
TCCR1B |= (1 << CS12);
TIMSK |= (1 << TOIE1);
sei();
while(1){}
return 0;
}
펄스의 개수가 기록되는 TCNTn레지스터는 16bit를 기록하지만 ATmega128이 8bit CPU를 가지고 있으므로 8비트짜리 레지스터를 두 개 묶어서 사용한다. TCCR1B의 CS12의비트를 세트했는데 저건 타이머를 활성상태로 변화시킴과 동시에 분주비를 256으로 설정하기 위함이고 그. 아래의 TIMSK의 TOIE1의비트를 세트한 것은 오버플로 인터럽트를 허용시키기 위함이다. sei()까지 해주면 전역 인터럽트가 활성화된다. 오버플로우 인터럽트 발생 시 TIFR레지스터의 2번 비트가 세트되는데 이때 TIMSK레지스터의 TOIE1비트가 세트된 것을 확인하고 오버플로 인터럽트가 실제로 발생하여 ISR로 넘어간다.
비교 일치 인터럽트
- 비교 일치 인터럽트 LED점멸 예제
#include <avr/io.h>
#include <avr/interrupt.h>
int state = 0;
ISR(TIMER1_COMPA_vect)
{
state = !state;
if(state) PORTB = 0x01;
else PORTB = 0x00;
TCNT1 = 0;
}
int main()
{
DDRB = 0x01;
PORTB = 0x00;
OCR1A = 0x7FFF;
TCCR1B |= (1 << CS12);
TIMSK |= (1 << OCIE1A);
sei();
while(1) {}
return 0;
}
OCR1A레지스터는 비교값을 기록하는 것이고 0x7FFF값을 넣었으니 0.5초에 인터럽트가 발생할 것이다. TIMSK레지스터에서 OCIE1A비트를 세트하는 것, TCNT1값이 자동으로 초기화되지 않는다는 점 이외에는 오버플로우 인터럽트의 경우와 같다. 비교일치 인터럽트의 경우 비교일치값을 저장할 수 있는 레지스터가 3개 있어서 여러 개의 주기를 설정할 수 있다.
- 비교 일치 인터럽트 여러 개
#include <avr/io.h>
#include <avr/interrupt.h>
int state0 = 0, state1 = 0, state2 = 0;
ISR(TIMER_COMPA_vect)
{
state2 = !state2;
if(state2) PORTB |=0x04;
else PORTB &= ~0x04;
}
ISR(TIMER1_COMPB_vect)
{
state2 = !state2;
if(state2) PORTB |= 0x04;
else PORTB &= ~0x04;
state1 = !state1;
if(state1) PORTB |= 0x02;
else PORTB &= ~0x02;
}
ISR(TIMER_COMPC_vect)
{
state2 = !state2;
if(state2) PORTB |= 0x04;
else PORTB &=~0x04;
}
ISR(TIMER_OVF_vect)
{
state2= !state2;
if(state2) PORTB |= 0x04;
else PORTB &= ~0x04;
state1= !state1;
if(state2) PORTB |= 0x02;
else PORTB &= ~0x02;
state0= !state0;
if(state0) PORTB |= 0x01;
else PORTB &= ~0x01;
}
int main(void)
{
DDRB = 0x07;
PORTB = 0x00;
OCR1A = 0x3FFF;
OCR1B = 0x7FFF;
OCR1C = 0xBFFF;
TCCR1B |= (1 << CS12);
TIMSK |= (1 << OCIE1A) | (1 << OCIE1B) | (1 << TOIE1);
ETIMSK |= (1 << OCIE1C);
sei();
while(1){}
return 0;
}
- 파형 출력
비교일치가 발생할 경우 지정된 핀을 통해 신호를 출력할 수 있다.
#include <avr/io.h>
#include <avr/interrupt.h>
int state = 0;
ISR(TIMER1_COMPA_vect)
{
TCNT1 = 0;
}
int main()
{
DDRB = 0x20;
PORTB = 0x00;
OCR1A = 0x7FFF;
TCCR1B |= (1 << CS12);
TCCR1A |= (1 << COM1A0);
TIMSK |= (1 << OCIE1A);
sei();
while(1){}
return 0;
}
LED의 상태를 바꾸어주는 코드가 사라지고 TCCR1A 레지스터에 관한 코드가 생겼다. 점멸에 해당하는 역할을 저 레지스터에 비트를 세트하는 것으로 수행했다고 보면 되는데 파형생성기를 통해 하드웨어적으로 제어한 것이다. COM1A0비트가 1, COM1A1비트가 0(디폴트)일 때, 비교일치가 발생하면 파형생성기에 연결된 핀의 출력이 반전된다. 타이머/카운터의 종류에 따라 파형생성기에 연결된 핀이 다르다.
입력 캡처
ICP의 상태 변화를 통해 이벤트를 감지하여 그 시간을 기록하기 위해 사용한다. 상태 변화는 상승 에지 또는 하강 에지이고 TCCRnB 레지스터의 ICESn bit가 사용된다.
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include "UART1.h"
FILE OUTPUT = FDEV_SETUP_STREAM(UART1_transmit, NULL, _FDEV_SETUP_WRITE);
FILE INPUT = FDEV_SETUP_STREAM(NULL, UART1_receive, _FDEV_SETUP_READ);
ISR(TIMER1_CAPT_vect)
{
int temp1 = TCNT1;
int temp2 = ICR1;
printf("Input Capture : %u\n", temp2);
printf("Timer/Counter : %u\n\n", temp1);
}
int main()
{
stdout = &OUTPUT;
stdin = &INPUT;
UART1_init();
TCCR1B |= (1 << CS12) | (1 << CS10);
TIMSK |= (1 << TICIE1);
sei();
while(1) {}
return 0;
}
끝
댓글남기기