2014년 5월 29일 목요일

7. AVR(Atmega)와 Proteus의 만남 - LED를 숨쉬게 하자!! 터미네이터의 눈처럼.. PWM 제어하기!! 1부

오랫만에 글을 다시 올립니다. 이래 저래 정신이 없다보니... 변명 아닌 변명이네요.

오늘은 무엇을 할꼬 하니... 지난 시간에 했던 LED를 좀 더 고급스럽게 제어를 하려고 합니다. 고급스럽게 제어한다고 함은... LED를 좀 더 부드럽게 깜박인다고 할 수 있죠..

혹시, 영화 터미네이터의 빨간 눈알을 기억하시나요?
터미네이터의 전원이 꺼질무렵 빨간 눈빛이 서서히 어두워지고, 다시 살아날때는 다시 서서히 밝아지는 거요..



느낌이 잘 와닿지 않으시다면.. 음... 크리스마스 트리 장식용 LED를 생각하시면 금방 이해가 갈 겁니다. 서서히 환해졌다가 서서히 어두워지는 패턴을 반복하는 것 기억나시죠? 그런 것들이 모두 PWM 방식으로 LED(또는 모든 전구)를 제어 하는 것이죠.

그럼, PWM이란 무엇인가?? 한번 알아봅시다.

  • PWM = Pulse Width Modulation (펄스 폭 변조)
간단히 용어를 음미해 보면,, 펄스의 폭을 변화시킨다는 거죠? 맞습니다. 그게 끝입니다.
즉, 펄스의 ON되는 폭을 늘였다 줄였다 하는 것이죠. 역으로 생각을 해보면, OFF되는 시간이 줄었다 늘었다 하는 것으로도 생각할 수가 있죠. 아래 그림을 보시죠.



펄스(Pulse)는 주기가 있죠. 위의 예에서는 1/500sec이므로 2ms입니다. 
  • 주기(Period) = 2ms (1/500 sec)
  • Pulse ON Time
    • 1번 = 2ms x 5% = 0.1ms
    • 2번 = 2ms x 50% = 1.0ms
    • 3번 = 2ms x 90% = 1.8ms
이와 같이, 펄스의 폭을 변화시킴으로써 5V를 유지시키는 시간을 변화시킬 수 있는 것이죠. 만약, 위의 펄스가 LED를 구동시키는 입력이라면, 1번 보다는 2번이 더 밝을 것이며, 2번 보다는 3번이 더 밝겠죠. 

좀더, 재밌게 해보자면, 입력 순서를 아래와 같이 하면 어떨까요?

입력순서: [1번 --> 2번 --> 3번 --> 2번 -->1번 --> 2번 --> 3번 --> ....]

그러면, 예상하시듯이 LED 불빛이 점점 밝았지다가 다시 점점 어두워지고, 다시 점점 밝아지고.. 어두어지고... 이렇게 무한 반복을 하겠죠.^^ 이것이 곧, 숨쉬는 LED의 원리 입니다.



오우케이!! 여기까지는 다 이해하겠는데... 도대체 어떻게 저런 파형을 내 맘대로 제어할 수 있는거죠? 이런 의문이 당연히 들겠죠. 맞습니다. 당연히 이런 의문을 가져야죠. 그리고 그 방법을 찾아 보는 노력을 하는 것이 엔지니어(공학자)의 역할 아닌가요?

우리의 수고를 덜어주기 위해서, Atmel사에서는 친절하게도 아주 편리한 Atmega 칩을 만들어서 그 속에 아주 강력한 타이머(Timer) 기능을 구현해 놓았습니다. 우리는 그 사용법을 조금 공부하고 편하게 쓰면 그만인 거죠. 물론, 그 원리는 알고 써야곘죠.

그럼, 데이터시트를 한번 보시죠.


위 데이터시트를 보면, 타이머/카운터 제로(Timer/Counter 0)는 여러가지 기능이 있습니다. 우선 8비트 크기이며, Timer0에는 두개의 독립적인 출력비교 장치(Output Compare Unit)가 있습니다. 즉, Timer0 하나를 가지고 두개의 출력(독립적)을 낼 수 있죠. 좀 더 쉽게 얘기하자면, Timer 0를 써서 두개의 LED를 구동할 수 있습니다. 그것도 각각 따로따로. 어떤가요? 좋지 않나요. 타이머0의 블럭다이어그램은 아래와 같습니다.


각 레지스터(Register)의 기능을 간단히 설명할께요.

  • TCNTn 
    • Timer CouNTer 이것은 0부터 255까지 1씩 증가. 다시 0 --> 255 증가.
    • 물론, 증가를 하기 위해서는 Tn(Clock)이 들어와야 하죠.
    • Tn 앞단에는 Prescaler(분주기)가 있죠? 이것을 통해서 클럭 속도를 줄일 수 있어요.
    • 예를 들면, Tn = 8Mhz, Prescale = 1/8 이면 clkTn = 1Mhz가 되죠. 이것에 맞춰서 Edge Triggering에 맞춰서 TCNT 레지스터의 값이 1씩 증가하는 거겠죠..
  • OCRnA
    • Timer0의 경우에는 OCR0A라고 써야겠죠. 
    • 의미는, Output Compare Register 우리말로 하면, 출력 비교 레지스터(저장소)
    • 예를들어, OCR0A = 100 이라고 하면 TCNT0의 값이 0부터 시작해서 쭉 증가하다가 100에 도달하면 뭔가 내부적으로 비교일치(Compare Match)신호가 발생해요. 비교일치신호는 s/w내부적으로는 인터럽트(Interrupt)로 발생하고 외부적으로는 OC0A Pin에 신호가 나오죠.(즉, 예를들면,0V에 있다가 비교일치가 발생하면 5V로 바뀌는 것. 그 반대의 경우로도 설정하여 동작할 수도 있음. 설정하기 나름이죠.) 이후 TCNT0는 100을 넘어서 255까지 쭉 증가하다가 다시 0부터 시작하죠. 그리고 또 100에 도달하면 또 비교일치신호 발생. 이렇게 무한번 반복하는 것입니다. 그것도 아주 빠르게...
    • 위의 블록다이어그램에서 보면 OCR0A와 TCNT0가 화살표로 등호(=)표시가 있죠? 이 의미가 서로 비교해서 같으면(equal = ), 오른쪽 화살표가 쭉~~ 가서 하나는 OC0A(Int. req= Interrupt request)에서 s/w인터럽트로 발생하고, 또 하나는 waveform generator를 거쳐서 OC0A pin을 통해서 전기적 신호(0V 또는 5V)로 나오는 것입니다. 설명이 기네요....^^
  • OCRnB
    • 바로 위의 OCRnA와 동일합니다. 이렇게 A, B 두 가지가 있으므로, 독립적으로 두개의 신호(OCnA, OCnB)가 발생하는 것이죠. 물론, OC0B 핀에서는 전기적 신호가 발생하구요.
    • 나머지는 위의 OCRnA와 동일합니다.
  • TOVn(Int. req.)
    • 이것도 뭔가 신호가 발생한 것이죠. 근데 이름이 Timer OVerflow 입니다. 즉, TCNT의 값이 넘치면(Overflow)되면 발생하는데... 넘친다는 의미가 뭘까요?
    • 그건, TCNT의 사이즈가 8비트 이므로 0부터 255까지 증가하다가 그 다음이 0으돌아가죠. 이때, 255 --> 0으로 바뀔때가 Overflow입니다. 이때 s/w 인터럽트가 발생하고 s/w 내부적으로 이 순간에 뭔가 원하는 동작을 할 수 있습니다.
  • TCCRnA, TCCRnB
    • 이건, 제어를 위한 레지스터 입니다. 두 개가 있느네요.
    • Timer Counter Control Register 란 뜻입니다.
    • 제어 레지스터에 몇가지 설정을 하는데.. 별 것 없어요.
    • 세가지만 확실히 설정하면 끝!!
      • 1. Timer0의 동작 모드 결정하기 --> WGM00, WGM01, WGM02
      • 2. Prescale(분주비) 정해주기 --> CS00, CS01, CS02
      • 3. 출력핀 사용 여부 결정하기 --> COM0A1, COM0A0 또는 COM0B1, COM0B0

그럼, 차례대로 아래 그림을 보죠.

첫번째, Timer0의 동작모드 결정(WGM00, WGM01, WGM02)

여러가지 모드가 있죠? 근데, 보통 자신이 자주 사용하는 모드가 있더라구요. 
제가 자주 쓰는 모드는 아래 붉은색 부분입니다. Fast PWM.


2. Prescale(분주비) 정해주기 --> CS00, CS01, CS02

TCCR0B의 오른쪽 3비트가 분주비(Prescale)을 결정합니다. Maser Clock이 주파수가 너무 높으면, Timer를 동작시키기 위한 원하는 주파수로 조금 낮출 수 있어요.
예를들어, Master Clock 주파수가 8Mhz라고 하고, Prescale을 1/64로 하고 싶다면 아래 표15-9에서 처럼 선택하면 됩니다. 그러면, 실제 Timer0로 입력되는 주파수는 8M/64 = 125khz입니다. 이런 식으로 주파수를 낮출 수 있어요. 만약 그냥 8Mhz 넣고 싶다면 CS00=1로만 하면 되죠. 즉, no prescaling.




3. 출력핀 사용 여부 결정하기 --> COM0A1, COM0A0 또는 COM0B1, COM0B0

만약, Timer0의 비교일치(Compare Match) 신호를 외부 핀으로 전기적 출력(간단히 말하면, 0V, 5V의 전기적 출력)으로 하고 싶을 경우 설정하죠. 
간단히 예를들어, Timer0가 fast PWM으로 설정되었을 경우, COM0A1=1, COM0A0=0으로 설정하면,, TCNT0 = OCR0A 와 값이 비교일치(Compare Match)할 때, OC0A 핀으로 출력이 나옵니다. 어떻식으로 나오느냐? 아래 설명을 읽어보세요.

(원문)
"Clear OC0A on compare match, set OC0A at BOTTOM, (non-inverting mode)"

(해석)
"OC0A 핀(12번 핀: PD6 )을 클리어(논리적으로는 0, 전압으로는 0V)하고, TCNT0 = BOTTOM(0 즉 zero)일 때, 셋(set)한다.(논리적으로 1, 전압으로는 5V)"

이해 되시나요?


(atmega 168)

아무튼,, 오늘은 여기까지 해야겠네요.
글을 쓰다보니, 잔소리가 늘어서 길어지네요.
조만간 다음 글을 쓰겠습니다.

이만 총총.


댓글 없음:

댓글 쓰기