9강. PWM (8bit-timer)
본 글은 Atmega의 PWM에 대해서 알아본다.
- PWM?
Pulse Width Modulation의 약자로, 파형의 Duty Cycle을 바꾸는 기능이다.
Duty Cycle은 Square wave 신호의 0과 1의 비율을 의미한다.
여기서 핵심적으로 보아야할 부분은, 파형의 Duty Cycle을 바꾼다는 점이다.
Duty Cycle=0 이면 0신호가 나타날테고, Duty Cycle=100%이면 1신호가 나타날 것이다. 그래서, 파형의 주파수가 지나치게 낮지 않은 이상, 즉 10Hz 이하가 아니라면, 파형의 주파수는 큰 의미가 없다.
Duty Cycle을 바꾸는 이유는 0과 1신호의 비율을 조절하기 위해서이지, 주파수를 바꾸어서 무언가를 하는건 아니다.
PWM이 사용되는 곳은 대부분 전력을 조절하기 위해서 사용된다. 가령, 선풍기를 만든다고 가정해보자. Duty Cycle=25%이면 약풍을, Duty Cycle=75%면 강풍을 만들수 있게 된다. 이런식으로, PWM은 대부분 전력을 조절하기 위해 사용된다.
- atemga의 PWM 원리
atmega의 PWM은 TIMER에 기반한다.
타이머의 클럭이 1 올라갈 때 마다, TCNT 값이 1 올라가고, overflow가 발생하면 TCNT 값이 0이 되면서 인터럽트가 발생하는 원리로 사용했었다.
PWM에서도 타이머의 원리를 이용한다. 다만 PWM에서는 한 가지가 추가됬는데,
1. 먼저, TCNT 값이 0에서부터 올라가는 동안은 1의 신호를 출력한다.
2. TCNT 값이 OCR 값과 일치하면, 그 이후는 0의 신호를 출력한다.
3. TCNT가 overflow가 발생하면 다시 1번으로 돌아간다.
단, PWM에서는 TCNT 값을 조작할 수 없다.
atmega의 PWM은 2가지 모드가 있다.
1. Fast PWM
앞에서 설명한 것이 이 그림에 그대로 나타나있다.
참고로, 8bit timer의 PWM의 주파수는 다음과 같다.
fclk_I/O는 16000000Hz (외부에 달린 클럭마다 다르다),
N은 Prescaler,
256은 TCNT 값의 순환을 의미한다.
그런데.. 앞서 예기했듯이, PWM의 주파수는 큰 의미가 없다. 밑의 그림을 보자.
이거는 10Hz의 50%, 5Hz의 50% 파형 그림이다. 그림을 보면 알겠지만, 1의 신호의 양은 결과적으로 둘 다 동일하다.
즉, PWM를 사용하는 목적은 주파수를 바꾸는데 있지 않고, Duty Cycle을 바꾸어서 0과 1의 신호의 비율을 바꾼 후, 파워의 출력을 바꾸는데 의의가 있다. 따라서... PWM의 주파수에 너무 큰 의의를 두지 않아도 된다..!
PWM의 주파수는 높은가 낮은가만 따지면 되는게, 모터의 경우에는 고주파수를 견디지 못해 저주파수를 사용해야 한다. 그런것만 고려하면 되지, 주파수를 정확히 1000Hz로 맞추어야 하고 하는건 타이머에서 해야할 일이다.
2. Phase Correct PWM
Fast PWM과 똑같은데 딱 한가지 다른 점은, TCNT가 0 -> 255 -> 0으로 순환한다는 점이다.
따라서 OCR이 인식하는 방법도 다른데, 별로 사용하진 않으므로 생략한다.
- PWM 핀
PWM은 atmega의 TIMER에서 만들어 낸다.
TIMER0에서 나오는 PWM은 OC0이고,
TIMER1에서 나오는 PWM은 OC1A, OC1B, OC1C, ...
이런식으로, PWM의 레지스터를 활성화하면 OC(Output Compare)에서 나온다.
Output Compare에서 나오는 이유는, PWM이 OC를 기준으로 파형을 출력하기 때문이다. 자세한건 뒤에서 다룬다.
- PWM 레지스터
atmega의 PWM은 대부분 다음과 같은 순서로 사용한다.
1. TCCRn 에서 Fast PWM, Phase correct PWM 둘 중 하나를 선택하고, 반전/비반전을 선택한다.
2. OCRn 값을 조정하여, Duty Cycle을 정한다.
타이머보다 더 간단하다. 바로 알아보도록 하자!
> 1. TCCR0 (Timer/Counter Control Register 0) : 타이머0 설정
타이머0에서 사용했던 그 레지스터이다.
타이머를 다루었다면, 이 레지스터는 쉽게 볼 수 있을거라 생각한다.
WGM은 PWM의 모드를,
COM은 PWM의 반전/비반전을,
CS는 Prescaler를 선택할 수 있다.
> 2. OCRn (Output Compare Register) : PWM Duty Cycle 값
PWM에서는 TCNT 값이 한 사이클 돌 때마다, OCR 값을 만나면 신호가 반전된다.
즉, 1을 출력하고 있었다면 0을, 0을 출력하고 있었다면 1을 출력하게 된다.
참고로, 8-bit timer에서는 OCR값의 종류는 256가지이다.
따라서, OCR 값이 1 올라갈 때 마다, PWM의 Duty Cycle은 1/256씩 올라가게 된다!
- 예제 : PWM으로 LED 밝기 조절하기
#define F_CPU 10000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
int main(void)
{
unsigned char i;
DDRB = 0xff;
PORTB = 0xff;
TCCR0 = 0b01101010; // Fast PWM, Non-inverting, 8 prescaler
while(1)
{
for(i=0; i<256; i++)
{
OCR0 = i; // 이 값으로 PWM의 Duty Cycle을 설정할 수 있다.
_delay_ms(10);
}
i=0;
}
}
- 예제 : PWM으로 1초 단위로 LED 밝기 조절하기
// timer0으로 1/32초 신호를 만들고,
// timer2에서 PWM을 사용하는데,
// OCR2가 256까지 한 사이클을 돌아야 하므로, 8씩 더했다.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
volatile int count = 0;
ISR(TIMER0_OVF_vect)
{
TCNT0 = -125;
count++;
if(count>125-1) // 1/32초
{
count=0;
OCR2 += 8;
}
}
int main(void)
{
DDRB = 0xff;
PORTB = 0xff;
TCCR2 = 0b01101011; // Fast PWM
TCCR0 = 0b00000011; // Timer0
TCNT0 = -125;
TIMSK=(1<<TOIE0); // PWM은 TIMSK를 쓰지 않는것에 주의!!
sei();
while(1)
{
}
}
참고로 PWM 파형은 이런식으로 나온다.