반응형
출처 : 나 (이전 정리했던 자료...)
기초지식.
ATmega128은 4개의 범용 타이머/카운터가 있다.
- Timer/Counter0(8비트),
- Timer/Counter1(16비트),
- Timer/Counter2(8비트),
- Timer/Counter3(16비트)
타이머/카운터의 제어에 필요한 레지스터는...
- 타이머/카운터 제어 레지스터(TCCRn)
- 타이머/카운터 레지스터(TCNTn)
- 출력 비교 레지스터(OCRn)
타이머/카운터 인터럽트 관련한 레지스터는...
- 타이머/카운터 인터럽트 플래그 레지스터(TIFR)
- 타이머/카운터 인터럽트 마스크 레지스터(TIMSK)
타이머를 사용하기 위해서는 타이머에서 사용하는 클럭에 대해서 설정을 해야 하는데 이는 프리스케일러(Prescaler) 값으로 조절할 수 있다. 프리스케일러 값은 각 타이머의 컨트롤 레지스터(TCCRn)에서 설정할 수가 있다. 타이머 인터럽트는 각 타이머 관련 컨트롤 레지스터에서 적절한 프리스케일러 값을 설정한 후 , 각 타이머 레지스터(TCNTn)에 얼마마다 한번씩 인터럽트를 걸게 할 것인지와 관련된 값을 써주면 된다.
그리고 인터럽트를 사용해야 하므로 타이머 인터럽트 관련 레지스터들을 설정해야 한다. 타이머 인터럽트에서는 TIMSK 레지스터만 설정하면 된다.
실제 간단 예제...
#include <iom128.h>
#include <ina90.h>
#include <sig-avr.h>
#include <interrupt.h>
#define SYSTEM_CLOCK 16000000 // CLOCK (X-tal frequency)
volatile int Count = 0;
SIGNAL(SIG_OVERFLOW0)
{
outp(0x83, TCNT0);
outp(Count, PORTA);
Count++;
if (Count > 255)Count= 0;
}
void Port_Init(void)
{
//Port_A setting up output
outp(0xFF, DDRA);
}
void Timer_Init(void)
{
// Timer0 setting, 1ms
// system_clk/Presclae = divide_clk
// 1/divide_clk = divide_time
// 1ms/divide_time = n
// 256 - n = TCNT0
outp(0x05, TCCR0); // Clock Source = System clock/128
outp(0x83, TCNT0);
outp(0x01, TIMSK); // Timer 0 overflow enable
}
int main(void)
{
// External Memory Disable
outp(0x00, MCUCR);
Port_Init();
Timer_Init();
sei();
while(1){ }
return 0;
}
위의 소스는 인터넷에서 돌아당기던 실제예제소스.이다.
- 1msec마다 발생하는 타이머 오버플로 인터럽트를 이용하여, 변수값이 1씩 증가하도록 설정하여 포트 A로 카운트된 값을 출력하는 프로그램
타이머 ISR 관리하기
이제 실제 프로젝트에서의 실제 사용 예제를 살펴보자...
void timerDetach(u08 interruptNum)
{
// make sure the interrupt number is within bounds
if(interruptNum < TIMER_NUM_INTERRUPTS)
{
// set the interrupt function to run nothing
TimerIntFunc[interruptNum] = 0;
}
}
역시나 각각의 배열에 함수 포인터로 연결해서 나중에 isr연결할때 쓴다.
각각 타이머 prescaler 관리
void timer0SetPrescaler(u08 prescale)
{
// set prescaler on timer 0
outb(TCCR0, (inb(TCCR0) & ~TIMER_PRESCALE_MASK) | prescale);
}
void timer1SetPrescaler(u08 prescale)
{
// set prescaler on timer 1
outb(TCCR1B, (inb(TCCR1B) & ~TIMER_PRESCALE_MASK) | prescale);
}
이런식으로 각각 세팅한다.
- 각 타이머 마다.. 해당레지스터 중에 프리스케일러에 해당하는 비트만 세팅한다. 각각의 프리스케일러 값을...프로그램 메모리 영역에 저장을 한다. 그래서 리턴하는 방식으로 프리스케일러 값을 얻어온다.
u16 timer0GetPrescaler(void)
{
// get the current prescaler setting
return (pgm_read_word(TimerPrescaleFactor+(inb(TCCR0) & TIMER_PRESCALE_MASK)));
}
u16 timer1GetPrescaler(void)
{
// get the current prescaler setting
return (pgm_read_word(TimerPrescaleFactor+(inb(TCCR1B) & TIMER_PRESCALE_MASK)));
}
프리스케일러 값을 설정하는것은 reference book을 살펴보길 바란다.
타이머초기화부분...
void timer0Init()
{
// initialize timer 0
timer0SetPrescaler( TIMER0PRESCALE ); // set prescaler
outb(TCNT0, 0); // reset TCNT0
sbi(TIMSK, TOIE0); // enable TCNT0 overflow interrupt
timer0ClearOverflowCount(); // initialize time registers
}
void timer1Init(void)
{
// initialize timer 1
timer1SetPrescaler( TIMER1PRESCALE ); // set prescaler
outb(TCNT1H, 0); // reset TCNT1
outb(TCNT1L, 0);
sbi(TIMSK, TOIE1); // enable TCNT1 overflow
}
void timer2Init(void)
{
// initialize timer 2
timer2SetPrescaler( TIMER2PRESCALE ); // set prescaler
outb(TCNT2, 0); // reset TCNT2
sbi(TIMSK, TOIE2); // enable TCNT2 overflow
timer2ClearOverflowCount(); // initialize time registers
}
void timer3Init(void)
{
// initialize timer 3
timer3SetPrescaler( TIMER3PRESCALE ); // set prescaler
outb(TCNT3H, 0); // reset TCNT3
outb(TCNT3L, 0);
sbi(ETIMSK, TOIE3); // enable TCNT3 overflow
}
- 각각의 프리스케일러와 오버플로우 레지스터 세팅같은걸 한다.
그럼 인터럽트 처리 루틴을 한번 보자..
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW0)
{
Timer0Reg0++; // increment low-order counter
if(!Timer0Reg0) // if low-order counter rollover
Timer0Reg1++; // increment high-order counter
// if a user function is defined, execute it too
if(TimerIntFunc[TIMER0OVERFLOW_INT])
TimerIntFunc[TIMER0OVERFLOW_INT]();
}
//! Interrupt handler for Timer1 overflow interrupt
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW1)
{
// if a user function is defined, execute it
if(TimerIntFunc[TIMER1OVERFLOW_INT])
TimerIntFunc[TIMER1OVERFLOW_INT]();
}
//! Interrupt handler for Timer2 overflow interrupt
TIMER_INTERRUPT_HANDLER(SIG_OVERFLOW2)
{
Timer2Reg0++; // increment low-order counter
if(!Timer2Reg0) // if low-order counter rollover
Timer2Reg1++; // increment high-order counter
// increment pause counter
TimerPauseReg++;
// if a user function is defined, execute it
if(TimerIntFunc[TIMER2OVERFLOW_INT])
TimerIntFunc[TIMER2OVERFLOW_INT]();
}
- 아마도 avr_studio 에서는 위와 같은 TIMER_INTERRUPT_HANDLER () 에서 isr을 관리하는듯 하다. 이때 관리시에.. 위에서 등록한 함수포인터들을 연결시켜놓는다.
- 하나의 타이머 인터럽트로 각 프로그래밍의 상황에따라... 여러 isr을 처리할수도 있을듯 하다.
- 이렇게 관리를 하면 각 인터럽트 루틴을 따로 안고치더라도 쉽게 isr을 변경할수 있을듯 하다.
실제적인 각 타이머의 동작 관리
void timer3PWMAOn(void)
{
// turn on channel A (OC3A) PWM output
// set OC3A as non-inverted PWM
sbi(TCCR3A,COMA1);
cbi(TCCR3A,COMA0);
}
void timer3PWMBOn(void)
{
// turn on channel B (OC3B) PWM output
// set OC3B as non-inverted PWM
sbi(TCCR3A,COMB1);
cbi(TCCR3A,COMB0);
}
void timer3PWMCOn(void)
{
// turn on channel C (OC3C) PWM output
// set OC3C as non-inverted PWM
sbi(TCCR3A,COMC1);
cbi(TCCR3A,COMC0);
}
void timer3PWMAOff(void)
{
// turn off channel A (OC3A) PWM output
// set OC3A (OutputCompare action) to none
cbi(TCCR3A,COMA1);
cbi(TCCR3A,COMA0);
}
void timer3PWMBOff(void)
{
// turn off channel B (OC3B) PWM output
// set OC3B (OutputCompare action) to none
cbi(TCCR3A,COMB1);
cbi(TCCR3A,COMB0);
}
void timer3PWMCOff(void)
{
// turn off channel C (OC3C) PWM output
// set OC3C (OutputCompare action) to none
cbi(TCCR3A,COMC1);
cbi(TCCR3A,COMC0);
}
void timer3PWMASet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel A
// this PWM output is generated on OC3A pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
outb(OCR3AH, (pwmDuty>>8)); // set the high 8bits of OCR3A
outb(OCR3AL, (pwmDuty&0x00FF)); // set the low 8bits of OCR3A
}
void timer3PWMBSet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel B
// this PWM output is generated on OC3B pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
outb(OCR3BH, (pwmDuty>>8)); // set the high 8bits of OCR3B
outb(OCR3BL, (pwmDuty&0x00FF)); // set the low 8bits of OCR3B
}
void timer3PWMCSet(u16 pwmDuty)
{
// set PWM (output compare) duty for channel B
// this PWM output is generated on OC3C pin
// NOTE: pwmDuty should be in the range 0-255 for 8bit PWM
// pwmDuty should be in the range 0-511 for 9bit PWM
// pwmDuty should be in the range 0-1023 for 10bit PWM
outb(OCR3CH, (pwmDuty>>8)); // set the high 8bits of OCR3C
outb(OCR3CL, (pwmDuty&0x00FF)); // set the low 8bits of OCR3C
}
- 아마도 timer / pwm의 관리는 해당 레지스터를 세팅하면 해당 isr루틴으로 갈것 같으니 별로 어려울것 없을듯하다.
- 데이터 시트만 잘 보면 그리 어려울것 같지는 않다.ㅋㅋ
- 뭐 카운터 관련 레지스터들을 초기화 시켜줘야 할 경우도 있을듯 하다.
반응형