2014년 6월 1일 일요일

10. AVR(atmega)와 Preteus VSM의 만남 - 숨쉬는 LED를 위한 PWM 제어 (4부)

글이 점점 길어집니다. 머릿속에서는 간단했는데 활자로 표현하자니 점점 길어만 지는군요! 그래도 지치지 말고 글을 이어 나갑니다.

오늘은 Timer0의 설정을 좀 하려고 합니다. 지난번 올렸던 소스코드 중에서 Timer0의 초기화 함수를 먼저 보시죠.

// Timer0 Init (Atmega 168기준)
void init_TM0_FPWM_mode()
{
// TOP = 255(0xFF)
// WGM02:00 = 011 (FPWM mode)
// CS02:00 = 100 (Prescale = 256 ) --> 122Hz(about 8ms = 8000000/256/256 = 122)
// Pin mode = None inverting mode

TCCR0A = _BV(WGM01) | _BV(WGM00); // Mode : Fast PWM
TCCR0A |= _BV(COM0A1) | _BV(COM0B1); // PWM Out Pin Mode: None Inveting mode
TCCR0B = _BV(CS02); // Prescale = 1/256

TCNT0 = 0; // 초기값 넣기(안넣어도 되지만, 확실히 하기 위해서)
OCR0A = 1; // 안넣어도 됨. 하지만, None inverting mode에서 0값은 튀는 값이라서 1로 넣어줌.
OCR0B = 1; // 위와 마찬가지.(상동)

        // 인터럽트 마스크 설정 : Output Compare Interrupt Enable 0A, 0B
TIMSK0 = _BV(OCIE0A) | _BV(OCIE0B);
}

우선 TCCR0A, TCCR0B는 타이머0의 컨트롤 레지스터입니다. 여기에 어떤 비트(Bit)를 설정하는냐에 따라서 타이머/카운터0의 동작이 결정되지요.
아래 표를 다시 한번 보시죠. 첫번째 그림은 TCCR0A, TCCR0B입니다. TCCR이란 약자의 의미는 아시죠? Timer Counter Control Register (TCCR). Timer0는 두개의 컨트롤 레지스터가 필요하네요. 어떤 atmega칩에서는 TCCR이 한 개만 있는 것도 있거든요. 각 칩마다 다르니까 그건 그때 그때 데이터시트를 참조하시면 되겠지요.

그럼, 하나씩 설명을 하지요.
TCCR0A = _BV(WGM01) | _BV(WGM00); // Mode : Fast PWM 모드 설정
--> 아래에 있는 TCCR0A 레지스터에서 WGM01, WGM00 비트를 설정함. 물론, TCCR0B의 WGM02 비트는 0(Clear)로 설정하므로, 결국 WGM02:00 = 011로 설정하는 것임. 이것이 Fast PWM 모드로 설정하는 것이죠.  아래 Table15-8 참조하세요. 아주 잘 나와 있습니다.

TCCR0A |= _BV(COM0A1) | _BV(COM0B1);
-> 계속해서, OC0A, OC0B Pin의 출력모드를 설정합니다. Non-inverting mode입니다. 이 모드의 동작방식은 지난 글에 이미 설명했습니다. Table15-3 참조하세요. 즉, TCCR0A 레지스터에서 비트 4,5,6,7 을 해당 모드에 맞게 설정해야죠. 위 명령어는 COM0A1(비트7)과 COM0B1(비트5)를 Set(1) 합니다. COM0A0, COM0B0 비트는 clear(0)합니다. 클리어는 따로 셋팅할 필요가 없겠죠? initial value가 0라고 친절하게 나와 있으니까요.
TCCR0B = _BV(CS02); // Prescale = 1/256
--> 분주비(Prescale)을 설정하는 비트입니다. TCCR0B에서 비트0,1,2에 있습니다. 1/256분주비로 셋팅하기 위해서는 CS02, CS01, CS00 = 1, 0, 0 으로 하면 됩니다. 따라서 CS02 비트만 Set(1)하면 되며, 다른 비트는 clear(0)하면 됩니다. 디폴트(기본값)가 0 이므로  CS02만 set합니다.

  TCNT0 = 0; // 초기값 넣기(안넣어도 되지만, 확실히 하기 위해서)
OCR0A = 1; // 안넣어도 됨. 하지만, None inverting mode에서 0값은 튀는 값이라서 1로 넣어줌.
OCR0B = 1; // 위와 마찬가지.(상동)
--> TCNT0는 기본이 0(zero) 이므로 굳이 넣어줄 필요는 없는데, 명시적으로 보여주기 위해서 넣었구요. OCR0A, OCR0B는 1을 넣었는데, PWM 동작시 0값은 특이값이라서 그냥 1을 넣었습니다.(데이터시트 잘 읽어 보시면, 0을 넣을 경우 이상동작에 대해서 설명되어 있습니다.)
TIMSK0 = _BV(OCIE0A) | _BV(OCIE0B);
 --> TIMSK0는 Timer Interrupt Mask 라는 뜻입니다. 타이머0에는 3가지 인터럽트가 가능하죠. 첫번째는 OVF(Overflow), 두번째와 세번째는 OCIE(Output Compare Interrupt Enable)이며 A와 B 두가지가 있습니다. 아래 그림 참조하세요. TOIE0(Oveflow), OCIE0A(Output Compare A), OCIE0B(Output Compare B


마지막으로 잊지 말아야 할 것은, 모든 인터럽트를 활성화 시켜주세요.
sei(); // 모든 인터럽트 활성화
sei() --> Set Enable Interrupt 
이 함수를 실행시켜야만 인터럽트가 동작하기 시작하는 거죠.^^

오늘은 여기까지 하고, 다음에 또 글을 씁니다.

끝.












댓글 1개:

  1. 궁금한게 있는데요 혹시 이런 레지스터를 쓰다보면 입출력 포트를 쓰는데에 제한이 되는 경우가 있나요??

    답글삭제