반응형

사실 저번 외부인터럽트 한개 써놓고 소자 도착하면 그냥 하던거나 해야지!! 했는데 저번 글 호응이 이상하리만큼 높았습니다(......)

 

 

정말 저도 잘 못하는데!! 안쓸수도 없고...ㅇ<-<

 

 

이번엔 타이머/카운터 설명을 하겓습니다. 아씨 진짜 잘모르는데 ㅋㅋㅋㅋㅋㅋ

 

 

먼저! 타이머/카운터가 뭐냐하면,

기본적으로 ATmega128을 비롯한 MCU들이 시간을 세기 위해서 쓰는게 크리스탈입니다.

 

 

 

저놈인데요, 쟤가 16MHz의 펄스를 계속 내보내고, 그 펄스를 이용해서 MCU가 자체적으로 시간을 셉니다.

 

 

 

요런식의 펄스를 세는게 타이머/카운터이고,

 

MCU 내부 시스템 클럭을 세는게 타이머, 외부 핀에서 들어오는 펄스를 세는게 카운터입니다.(TOSC1,2, T1,2,3)

 

 

카운터는 패스하고, 타이머에 대해서 좀 알아보죠.

 

 

먼저, 프리스케일러라는게 있습니다. 분주비라고도 하는데요, 아까 얘기드렸던 것 같이 시스템 클럭은 16MHz, 한 펄스가 1/16us짜리입니다.

 

요걸로 1초마다 동작하는 타이머를 쓰려고 한다면.. 좀 많이 세어야겠죠? 그래서 분주비라는걸 씁니다.

예를 들어 16MHz를 2분주 시킨다면 8MHz, 1/8us가 되겠네요.

ATmega128은 총 네가지 타이머를 지원합니다. timer/counter 0,1,2,3 네가지로요.

이 중에서, timer/counter0,2 는 8비트 타이머, timer/counter1,3은 16비트 타이머입니다.

 

차이점이라면 8비트 카운터는 0x00~0xFF까지 세는거고요, 16비트 카운터는 0x0000~0xFFFF까지 셀 수 있습니다.

 

일단 8비트 타이머에 대해 설명하도록 할게요.

 

 

타이머의 동작 모드는 총 네개가 있습니다.

Normal 모드, CTC 모드, Fast PWM 모드, Phase Correct PWM 모드가 있는데요, 뒤에 두개는 PWM동작입니다. 다음에 설명하도록 하고 ^^;

 

 

Normal 모드와 CTC 모드는 인터럽트가 다릅니다.

 

 

Normal 모드는 timer/counterX overflow interrupt이고, CTC 모드는 timer/counterX compare match interrupt 입니다.

 

 

 

 

 

 

 

 

 

 

요게 Normal 모드입니다.

현재값은 TCNTn 레지스터에 저장되고, 말 그대로 TCNTn이 0xFF가 되면 오버플로우가 일어납니다.

 


 

 

이게 CTC모드인데요, TCNTn이 증가하는건 노멀모드와 동일하지만, 여기서는 OCRn이라는 레지스터를 하나 더 설정해줍니다.

TCNTn == OCRn이 되면 TCNTn은 다음 비트에서 0으로 클리어, 출력 비교 인터럽트가 일어나게 됩니다. 물론 OCRn을 0xFF로 설정할 수는 있습니다만.. 그럼 오버플로우랑 똑같죠뭐 ㅡ,.ㅡ

 

 

그럼 실제로 사용하기 위한 타이머 0의 레지스터들을 알아보죠. 사실 위에꺼 잘 몰라도 레지스터만 알면 써먹어 집니다(....)

 

 

1. TCNT0

..그림에서도 보셨겠지만 냅두면 알아서 0x00~0xFF까지 왔다갔다합니다.

 

 

2. OCR0

TCNT0와 계속 비교되는 값입니다. 같아지면 출력 비교 인터럽트가 일어납니다.

3. TIMSK

 

 

 

타이머/카운터0는 마지막 두 비트를 쓰는데, 비트 0이 타이머/카운터0 노멀모드, 비트 1이 타이머/카운터0 CTC모드 셋입니다.

TIMSK = 0b00000001이면 노멀모드 인에이블, TIMSK = 0b00000010이면 CTC모드 인에이블이겠죠.

 

 

4. TCCR0

 

 

쬐끔 복잡합니다.

 

 

6번비트와 3번비트는

 

 

00일때 노멀, 01일때 CTC, 10일때 phase correct PWM, 11일때 fast PWM입니다.

 

 

2번에서 0번비트는 위에 잠깐 언급했던 분주비 설정입니다.

 

000일때 타이머/카운터 동작 중지

001일때 1분주(분주비 사용 X)

010일때 8분주

011일때 32분주

100일때 64분주

101일때 128분주

110일때 256분주

111일때 1024분주가 됩니다.

 

 

 

 

뭔가 설명은 길었는데, 실제로 쓰실때는 레지스터 세개정도? 에 저번에 했던 SREG레지스터 설정해서 쓰시면 됩니다.

예시 하나 보실까요?

 

 

 

 

 

 

타이머/카운터0을 이용해 LED를 0.5초마다 켰다껐다하는 동작입니다.

 

 

 

타이머0을 위해 들어간 코드는

9~13번줄입니다.

 

 

 

 

 

 

  1. TIMSK = 0b00000001; // TOIE0 = 1;
  2. TCCR0 = 0b00000111; // 일반모드, 프리스케일 = CK/1024
  3. TCNT0 = 0b00000000; // 타이머/카운터0 레지스터 초기값
  4. SREG = 0b10000000; // 전역 인터럽트 인에이블 비트 I 셋

 

TIMSK = 0b00000001; 타이머/카운터0 노멀모드 인에이블이고,

TCCR0의 6번, 3번비트는 0 : 노멀모드, 2~0번비트 111 : 1024분주비 사용입니다.

TCNT0값이 설정 가능하기는 한데, 여기서는 초기값이네요.

 

 

그리고 SREG = 0x80으로 인터럽트 사용 설정을 해줍니다.

 

요건 인터럽트 함수인데요,

 

  1. // 1/16us x 1024 x 256 = 16.384ms
  2. interrupt [TIM0_OVF] void timer_int0(void)
  3. {
  4. cnt++;
  5. if(cnt == 31) { // 16.384ms x 31 = 0.5sec
  6. led = led ^ 0xFF;
  7. PORTB = led;
  8. cnt = 0;
  9. }
  10. }

 

노멀모드의 경우

interrupt[TIMn_OVF] void timer_intn(void)

{

}

 

로 사용하고,

 

CTC모드의 경우

interrupt[TIMn_COMPA] void timer_compna(void)

{

}

로 씁니다.

 

 

 

한번 인터럽트 걸리는게 16.384ms이고, 그걸 31번 반복해서 0.5sec를 맞추는겁니다. 자기가 쓰고자 하는 시간에 맞춰서 분주비나 기타등등을 맞춰서 해 주시면 되는데...

 

 

귀찮으신분들을 위해 밑에 비기(!)를 몇개 소개해봅니다. 네이년 검색하다 찾았네요

 

 

 

 

 

//=====================================================

 

// 타이머0 오버플로우 인터럽트
TCCR0=3; TCNT0=156; TIMSK=1; //16000000/ 32/100=5000Hz=200us,(256-100)=156
TCCR0=4; TCNT0=6; TIMSK=1; //16000000/ 64/250=1000Hz=1ms, (256-250)=6
TCCR0=5; TCNT0=131; TIMSK=1; //16000000/ 128/125=1000Hz=1ms, (256-125)=131
TCCR0=5; TCNT0=6; TIMSK=1; //16000000/ 128/250= 500Hz=2ms, (256-250)=6
TCCR0=6; TCNT0=6; TIMSK=1; //16000000/ 256/250= 250Hz=4ms, (256-250)=6
TCCR0=7; TCNT0=100; TIMSK=1; //16000000/1024/156=100.16Hz=0.984ms, (256-156)=100
TCCR0=7; TCNT0=6; TIMSK=1; //16000000/1024/250=62.5Hz=16ms, (256-250)=6

// 타이머0 매치 인터럽트
TCCR0=0x0B; OCR0= 28; TIMSK=2; //16000000/ 32/(1+ 28)=17241.37931Hz=58us
TCCR0=0x0B; OCR0= 49; TIMSK=2; //16000000/ 32/(1+ 49)=10000Hz=100us
TCCR0=0x0B; OCR0= 99; TIMSK=2; //16000000/ 32/(1+ 99)= 5000Hz=200us
TCCR0=0x0C; OCR0= 74; TIMSK=2; //16000000/ 64/(1+ 74)= 3333.3Hz=300us
TCCR0=0x0C; OCR0=124; TIMSK=2; //16000000/ 64/(1+124)= 2000Hz=500us
TCCR0=0x0C; OCR0=249; TIMSK=2; //16000000/ 64/(1+249)= 1000Hz=1ms
TCCR0=0x0D; OCR0=124; TIMSK=2; //16000000/ 128/(1+124)= 1000Hz=1ms
TCCR0=0x0D; OCR0=249; TIMSK=2; //16000000/ 128/(1+249)= 500Hz=2ms
TCCR0=0x0E; OCR0=249; TIMSK=2; //16000000/ 256/(1+249)= 250Hz=4ms
TCCR0=0x0F; OCR0= 71; TIMSK=2; //14745600/1024/(1+ 71)= 200Hz=5ms
TCCR0=0x0F; OCR0=155; TIMSK=2; //16000000/1024/(1+155)= 100.16..Hz=9.984ms
TCCR0=0x0F; OCR0=249; TIMSK=2; //16000000/1024/(1+249)= 62.5Hz=16ms

// 타이머1 오버플로우 인터럽트
TCCR1B=1; TCNT1=49536; TIMSK=4; //16000000/ 1/16000=1000Hz=1ms, (65536-16000)=49536
TCCR1B=1; TCNT1=33536; TIMSK=4; //16000000/ 1/32000= 500Hz=2ms, (65536-32000)=33536
TCCR1B=2; TCNT1=63536; TIMSK=4; //16000000/ 8/ 2000=1000Hz=1ms, (65536- 2000)=63536
TCCR1B=2; TCNT1=61536; TIMSK=4; //16000000/ 8/ 4000= 500Hz=2ms, (65536- 4000)=61536
TCCR1B=2; TCNT1=45536; TIMSK=4; //16000000/ 8/10000= 200Hz=5ms, (65536-10000)=45536
TCCR1B=2; TCNT1=35536; TIMSK=4; //16000000/ 8/20000= 100Hz=10ms, (65536-20000)=35536
TCCR1B=3; TCNT1=65286; TIMSK=4; //16000000/ 64/ 250=1000Hz=1ms, (65536- 250)=65286
TCCR1B=4; TCNT1=59286; TIMSK=4; //16000000/256/ 6250= 10Hz=100ms,(65536- 6250)=59286
TCCR1B=4; TCNT1=34286; TIMSK=4; //16000000/256/31250= 2Hz=500ms,(65536-31250)=34286
TCCR1B=4; TCNT1= 3036; TIMSK=4; //16000000/256/62500= 1Hz=1sec, (65536-62500)= 3036

// 타이머1 A매치 인터럽트
TCCR1B=0x09; OCR1A= 1474; TIMSK=0x10; //14745600/ 1/(1+ 1474)=9997.01695Hz=100.02984us
TCCR1B=0x09; OCR1A= 1599; TIMSK=0x10; //16000000/ 1/(1+ 1599)=10KHz=100us
TCCR1B=0x09; OCR1A= 3999; TIMSK=0x10; //16000000/ 1/(1+ 3999)= 4KHz=250us
TCCR1B=0x09; OCR1A=15999; TIMSK=0x10; //16000000/ 1/(1+15999)=1000Hz=1ms
TCCR1B=0x09; OCR1A=31999; TIMSK=0x10; //16000000/ 1/(1+31999)= 500Hz=2ms
TCCR1B=0x0A; OCR1A= 1999; TIMSK=0x10; //16000000/ 8/(1+ 1999)=1000Hz=1ms
TCCR1B=0x0A; OCR1A= 3999; TIMSK=0x10; //16000000/ 8/(1+ 3999)= 500Hz=2ms
TCCR1B=0x0A; OCR1A= 4999; TIMSK=0x10; //16000000/ 8/(1+ 4999)= 400Hz=2.5ms
TCCR1B=0x0A; OCR1A= 9999; TIMSK=0x10; //16000000/ 8/(1+ 9999)= 200Hz=5ms
TCCR1B=0x0A; OCR1A=19999; TIMSK=0x10; //16000000/ 8/(1+19999)= 100Hz=10ms
TCCR1B=0x0A; OCR1A=18431; TIMSK=0x10; //14745600/ 8/(1+18431)= 100Hz=10ms
TCCR1B=0x0A; OCR1A=39999; TIMSK=0x10; //16000000/ 8/(1+39999)= 50Hz=20ms
TCCR1B=0x0A; OCR1A=59999; TIMSK=0x10; //16000000/ 8/(1+59999)= 33.33Hz=30ms
TCCR1B=0x0B; OCR1A= 249; TIMSK=0x10; //16000000/ 64/(1+ 249)=1000Hz=1ms
TCCR1B=0x0C; OCR1A= 3124; TIMSK=0x10; //16000000/256/(1+ 3124)= 20Hz=50ms
TCCR1B=0x0C; OCR1A= 6249; TIMSK=0x10; //16000000/256/(1+ 6249)= 10Hz=100ms
TCCR1B=0x0C; OCR1A=12499; TIMSK=0x10; //16000000/256/(1+12499)= 5Hz=200ms
TCCR1B=0x0C; OCR1A=31249; TIMSK=0x10; //16000000/256/(1+31249)= 2Hz=500ms
TCCR1B=0x0C; OCR1A=62499; TIMSK=0x10; //16000000/256/(1+62499)=1Hz=1sec
TCCR1B=0x0D; OCR1A=62499; TIMSK=0x10; //16000000/1024/(1+62499)=0.25Hz=4sec

// 타이머3 A매치 인터럽트
TCCR3B=0x09; OCR3AH= 1474>>8; OCR3AL= 1474&0xFF; ETIMSK=0x10; //14745600/ 1/(1+ 1474)=9997.01695Hz=100.02984us
TCCR3B=0x09; OCR3AH= 1599>>8; OCR3AL= 1599&0xFF; ETIMSK=0x10; //16000000/ 1/(1+ 1999)=10KHz=100us
TCCR3B=0x09; OCR3AH=15999>>8; OCR3AL=15999&0xFF; ETIMSK=0x10; //16000000/ 1/(1+15999)=1000Hz=1ms
TCCR3B=0x09; OCR3AH=31999>>8; OCR3AL=31999&0xFF; ETIMSK=0x10; //16000000/ 1/(1+31999)= 500Hz=2ms
TCCR3B=0x0A; OCR3AH= 1999>>8; OCR3AL= 1999&0xFF; ETIMSK=0x10; //16000000/ 8/(1+ 1999)=1000Hz=1ms
TCCR3B=0x0A; OCR3AH= 3999>>8; OCR3AL= 3999&0xFF; ETIMSK=0x10; //16000000/ 8/(1+ 3999)= 500Hz=2ms
TCCR3B=0x0A; OCR3AH= 9999>>8; OCR3AL= 9999&0xFF; ETIMSK=0x10; //16000000/ 8/(1+ 9999)= 200Hz=5ms
TCCR3B=0x0A; OCR3AH=19999>>8; OCR3AL=19999&0xFF; ETIMSK=0x10; //16000000/ 8/(1+19999)= 100Hz=10ms
TCCR3B=0x0C; OCR3AH= 6249>>8; OCR3AL= 6249&0xFF; ETIMSK=0x10; //16000000/256/(1+ 6249)= 10Hz=100ms
TCCR3B=0x0C; OCR3AH=31249>>8; OCR3AL=31249&0xFF; ETIMSK=0x10; //16000000/256/(1+31249)= 2Hz=500ms
TCCR3B=0x0C; OCR3AH=62499>>8; OCR3AL=62499&0xFF; ETIMSK=0x10; //16000000/256/(1+62499)=1Hz=1sec
TCCR3B=0x0C; OCR3AH=28799>>8; OCR3AL=28799&0xFF; ETIMSK=0x10; //14745600/1024/(1+28799)=0.5Hz=2sec

// 타이머2 매치 인터럽트
TCCR2=0x0B; OCR2= 24; TIMSK=0x80; //16000000/64/(1+ 24)=10000Hz=100us
TCCR2=0x0B; OCR2=249; TIMSK=0x80; //16000000/64/(1+249)=1000Hz=1ms

//=====================================================

 

http://cafe.naver.com/circuitsmanual/116901 에 보시면 계산기도 있더라구요? 허허

반응형
반응형
#include "timer.h"


static volatile unsigned int uiTic; // Timer_Handler 함수 호출을 카운트, 1m초


 

void Timer0_Init(void)

{

PMC_PCER = 1 << TC0; // PMC_PCER(Peripheral Clock Enable Register)에 타이머카운터 장치 활성화, P204, P33

// TC0(타이머카운터 0)에 클럭이 공급된다.


// 1. 시작 : 타이머 클럭 비활성화 ------------------

TC0_CCR = 1 << CLKDIS; //카운터 클럭 비활성화 명령(CLKDIS)을 Enable


// 2. 시작 : 타이머 인터럽트 비활성화 -------------

TC0_IDR = (1 << COVFS)|(1 << LOVRS)|(1 << CPAS)|(1 << CPBS)

|(1 << CPCS)|(1 << LDRAS)|(1 << LDRBS)|(1 << ETRGS); // 타이머 인터럽트 비활성화(TC_IDR 설정)

TC0_SR;// 타이머카운터 상태 레지스터 초기화(TC_SR 읽기)


TC0_CMR = (TIMER_CLOCK4 << TCCLKS) | (1 << CPCTRG); /*TC0_CMR(채널 모드 레지스터)에서

TCCLKS(Clock Selection)에 3을 넣어 분주비 128(TIMER_CLOCK4) 설정 | RC 비교방식 트리거 활성화*/


TC0_RC = 375; // MCK divided by 128 => 375000Hz, 1CK = (about)2.67us [TC_RC(Register C)설정]

// 한마디로 1ms에 인터럽터를 발생시키기 위해 375로 설정


// 3. 시작 : 타이머 카운터 0 인터럽트 비활성화 ------

AIC_IDCR = 1 << TC0; // 인터럽트 비활성화(AIC_IDCR) = Enable(1) << 타이머 카운터 0(TC0)


AIC_SVR[TC0] = (volatile unsigned int)Timer_Handler;

/*AIC_Source Vector Register = 타이머 카운터 0 인터럽트 핸들러 등록

여기서 Vector 레지스터는 인터럽트가 발생하면 점프하게 될 함수(ISR:인터럽트 서비스 루틴) 주소만을 저장할 수 있다.

함수 전체를 저장할 수 있으면 좋겠지만, 그러기엔 ARM 레지스터의 용량이 너무 작다.


Vector 가 뭔지 궁금해 하시는 분들을 위해

- 우리의 ARM은 폴링형 인터럽트와 vector형 인터럽트 등 인터럽트 처리방법 중에 vector형 인터럽트 처리 방식을 사용한다.

1. Polling 형 인터럽트

- 폴링은 투표, 개표 라는 뜻이다. 즉 CPU가 소프트웨어적으로 보드에 인터럽트를 낼수있는 하드웨어들을 차례대로

검사하는 방식이다. 하드웨어가 간단하고 저렴하지만, 인터럽트를 낼수있는 하드웨어 갯수 증가에 따라 속도가 느려진다.

이후 인터럽트가 검출된 장치를 처리한다. 여기서 "처리한다" ISR(Interrupt Service Rutine)이라고 한다.


2. vector 형 인터럽트

- cpu가 소프트웨어적으로 하드웨어들을 검사하는것이 아니라,

인터럽트를 발생시킨 장치가 cpu에 ISR(우리가 만든 핸들러 함수)의

시작 번지를 제공하면 cpu가 이것을 먼저 처리하는 방식.

이 ISR들은 기본적으로 interrupt vector에 저장되어 있고 더 나아가

사용자가 ISR을 설정해주고 싶으면 Interrupt Handler를 작성/등록(AIC_SVR[TC0])해주면 된다.

하드웨어가 복잡하고 비싸지만, 빠르고, 장치 갯수에 상관없이 언제든지! 서비스를 제공한다.

AT91SAM7S256, atmega2560 등 에서는 이방식을 사용한다.


AIC_SMR[TC0] = (0 << PRIOR)|(3 << SRCTYPE);

/* TC0 인터럽트 소스 모드 레지 = 우선순위레벨(Priority Level)을 0 |

여기서 만약, 인터럽트 두개가 동시에 발생하면 ?

polling 방식은 순차적으로 장치를 검사하기 때문에 그 순서대로 우선순위를 정하지만

vector 방식은 인터럽트 우선순위를 제어할 수 있는 레지스터(AIC_SMR)가 존재하여,

레지스터에 값을 집어넣어 우선순위를 정한다.

[출처] [AVR] Interrupt|작성자 태희로그

[출처] [AVR] Interrupt|작성자 태희로그


Interrupt Source Type(SRCTYPE) High level Sensitive로 설정*/



AIC_ICCR = 1 << TC0; // 타이머 카운터 0 인터럽트 클리어(TC0)


TC0_IER = 1 << CPCS;

/* 타이머 카운터 인터럽트 활성화 레지스터 = RC 비교 인터럽트만 활성화*/


// 2. 끝 : 타이머 인터럽트 비활성화 ---------------


AIC_IECR = 1 << TC0; // 타이머 카운터 0 인터럽트 활성화(AIC_IECR, TC0)


// 3. 끝 : 타이머 카운터 0 인터럽트 비활성화 -------


TC0_CCR = 1 << CLKEN; // 타이머 클럭 활성화(TC_CCR, CLKEN)


// 1. 끝 : 타이머 클럭 비활성화 ------------------


TC0_CCR = 1 << SWTRG; // 타이머 시작(TC_CCR, SWTRG)


}


void Timer_Handler(void)

{

TC0_SR; //핸들러가 호출되면 현재 인터럽트가 발생된 상태이기 때문에

//다음 인터럽트가 발생할 수 있도록 상태 레지스터 초기화

++uiTic;

return;

}


void ms_Delay(unsigned int uims)

{

uiTic = 0;

while(uims > uiTic); //이 함수를 종료하려면 uims를 기다려야 한다

// uiTic은 1ms마다 증가하므로 uims에 1000을 넣으면

//약 1초를 기다려야 반복문이 종료된다.

}


 

반응형
반응형

/* Atmega8a

tool : avr studio4

win avr gcc

*/

// √ 한건 코드를 짜야함 ㅠㅠ

//현재 timer/counter에서 고전하고 있다... resister와 타이밍도 그리고 분주기설정하는 것을 이해를 못하겠다.....

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
unsigned int pwck = 0;
unsigned int cnt=0;

//timer 사용 , 부저사용 , sleep모드
//스위치 누를 때마다 부저 울림√
ISR(INT0_vect){ // 전원스위치 인터럽트 1. led켜기 2. led끄기 3. sleep모드 √
PORTD = 0x20;
pwck++;
if(pwck ==1){
if((PIND &0x20)==0xff){
PORTC = 0xff;
TIMSK = 0x00;
}
else if((PIND & 0x60) ==0xff){
PORTC = 0xff;
}
if((PIND & 0xd0) == 0xff){
PORTC = 0xff;
}
}
if(pwck == 2)
PORTC = 0x00;
pwck=0;
// 2~3초간 누를시 sleep모드 timer사용√
}
int main(){
PORTD = 0x00;
PORTC = 0x00;
DDRB = 0x02; // 부저
DDRC = 0x01; // 후면 led
DDRD = 0x47; // 전면 led 출력 & 스위치 입력 & 인터럽트 INT0

if((PIND & 0x10) == 0xff){
cnt++;
switch(cnt){
case 0:
PORTD=0x20;
break;
case 1:
PORTD=0x60;
break;
case 2:
PORTD=0xd0;
break;
default:
PORTD=0x20;
cnt=0;
break;
}
}
while(1){
// 슬립모드 √
}
return 0;
}

반응형

+ Recent posts