2014년 5월 29일 목요일

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

지난 글에 이어,, 숨쉬는 LED를 제어하기 위한 PWM제어입니다.

이제는 PWM이 무엇인지 좀 감이 잡히시는지요?
'백문이불여일견' 이라서,, PWM동영상 하나 보여드리죠. 제가 간단히 만든겁니다.

[PWM 제어 동영상]

인간의 눈으로 보는것과 동영상으로 촬영한 것은 좀 느낌이 다르긴 하지만,, 그래도 LED의 밝기가 자연스럽게 변화하는 것이 보이시죠? 바로 그것입니다.

[숨쉬는 LED 회로도(Proteus VSM에서)]


위의 회로도를 간단히 설명하면,

  • PD0 : LED Green (지난번 강좌때 동작시켰던 LED. 10ms 주기로 On/Off)
  • PD5 : LED Yellow (Timer0의 OC0B Pin으로 동작. PWM 출력 나옴)
  • PD6 : LED Red (Timer0의 OC0A Pin으로 동작. PWM 출력이 나옴)
나머지는 지난 번과 동일합니다.

그럼, Proteus로 동작을 시켜보겠습니다.

[Proteus VSM 시뮬레이션 동영상]

어떤가요? Duty Rate(On/Off 시간)가 변하는 것이 보이나요?
보이면, PWM 신호를 구분할 수 있는 능력이 생긴겁니다.
좀더 확실한 이해를 위해서 아래에 그림을 하나 캡쳐했습니다.

오실로스코프에서...
  • 분홍색 - PD5(OC0B)
  • 파랑색 - PD6(OC0A)
  • 노란색 - PD0
아래 스코프 그림을 보면, 분홍색과 파란색의 Duty Rate가 다릅니다. 제가 아래 소스코드에서 OC0A와 OC0B의 Duty Rate를 반대로 움직이도록 코딩했거든요. 그래서, 한쪽의 듀티레이트가 서서히 증가할 때(256단계로 증가함) 다른 한쪽은 서서히 감소합니다.

PWM 주파수는 대략 122Hz 정도로 셋팅했죠.(아래 소스코드를 보면 다~~ 나옴)
노파심에 간단히 계산해 봅시다.

Master Clock Freq. = 8 Mhz = 8000000 Hz
Prescale = 1/256
Timer0 TOP value = 256 (Timer0 의 Fast PWM mode with fixed TOP value)

PWM 주파수 = Master Freq / Prescale / Timer TOP value 
                   = 8000000 / 256 /256 = 122.07 Hz

그래서, 아래 그림에서 스코프의 X축 Time Scale은 2ms/Div입니다. 즉 한 칸당 2ms 입니다.
그러므로, 주기는 노락색이 5칸(10ms), 파란색이 약4칸(8ms), 분홍색도 약4칸(8ms)



참고로, 아래에 소스 코드를 첨부합니다.
소스코드 설명은 다음에 시간날때 한번 할 예정입니다.

이만 총총.

------------------------------------------------------------------------
[Source code at Atmel Studio6.0]


#define F_CPU 8000000L   
#include <avr/io.h>
#include <avr/sfr_defs.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define BYTE unsigned char
// -------------------------------------------------------
// Timer 0 ISR Routine
// -------------------------------------------------------
BYTE t0cnt = 0;
BYTE UDFlag = 0;

ISR(TIMER0_COMPA_vect)  // PD6: CH4 PWM out
{
// nothing
if(UDFlag == 0)
{
t0cnt++;
OCR0A = t0cnt;
if(t0cnt == 255) UDFlag = 1;
}
else
{
t0cnt++;
OCR0A = 256 - t0cnt;
if(t0cnt == 255) UDFlag = 0;
}
}

ISR(TIMER0_COMPB_vect)  // PD5: CH5 PWM out
{
// nothing
OCR0B = 256 - OCR0A;

// Timer0 Init
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; // 위와 마찬가지.(상동)
TIMSK0 = _BV(OCIE0A) | _BV(OCIE0B); // Set interrupt mask : Output Compare Interrupt Enable 0A, 0B
}

int main(void)
{
cli(); // 모든 인터럽트 정지.
DDRD = 0xFF; // 모두 출력으로 설정.
PORTD = 0x00; // 출력 초기값은 Off(0V)로 설정.
init_TM0_FPWM_mode(); // Timer0의 초기화 함수. (위에 정의 한 것들)
sei(); // 모든 인터럽트 활성화
    while(1)
    {
        //TODO:: Please write your application code 
_delay_ms(10); // Delay 100ms
PORTD ^= _BV(PD0); // PD0 비트를 XOR 함. 즉, 토글되도록 함. 그래야 LED 깜박임.
    }
}


댓글 없음:

댓글 쓰기