PWM(Pulse Width Modulation)란?
1. 개요
PWM(Pulse Width Modulation)은 디지털 신호를 이용하여 아날로그 신호를 구현하는 기술 중 하나이다. 즉 디지털 클럭 신호를 모터의 속도, LED의 밝기 등 아날로그 신호로 바꿀 수 있다는 말이다. 주파수는 일정하게 유지하면서, 펄스의 폭(Width)를 조절하여 폭이 넓을수록 신호의 크기가 크고, 폭이 좁을수록 신호의 크기가 작아지게 된다.
2. 동작 방식
PWM을 이용하여 디지털 신호를 발생시키면, 신호의 High 레벨과 Low 레벨이 일정한 주기로 반복되는 것을 볼 수 있다. PWM의 동작방식은 주파수와 듀티비(Duty Cycle)로 이루어져 있다. 주파수는 신호가 반복되는 주기를 나타내며, 듀티비는 High 레벨의 지속시간을 주기에 대한 비율로 나타낸 것이다. 예를 들어, 주파수가 1kHz인 클럭이 있다고 가정하자. 그렇다면 클럭이 한번 움직이는 시간(주기 T)는 0.001초일 것이다. 이 클럭의 듀티비가 50%인 PWM 신호를 발생시키면, 신호의 High 레벨은 500us 동안 유지된다.
또한 ATmega128에서의 PWM은 클럭을 사용해야하므로 타이머/카운터를 사용해야한다. 이전에 배웠던 타이머/카운터를 다시 생각해보면 8비트와 16비트 타이머/카운터가 있었다. 8비트의 경우 2의 8승 즉 256번을 카운트할 수 있다고 했었고, 16비트의 경우 2의 16승 즉 65536번 카운트를 할 수 있었다고 했다. 그리고 이 카운트 횟수는 TCNTn레지스터에 저장되어 MAX 카운트 값에 도달할 경우 TCNT는 0으로 초기화된다는 것도 배웠었다.
비트수가 높을수록 PWM을 사용할 때 카운팅 값(해상도)가 높기 때문에 듀티비의 세부 조정이 훨씬 수월하다.
3. ATmega128A에서의 PWM 사용 방법
간단하게 PWM 대해 소개했다. ATmega128A에서의 PWM을 사용하기 위한 방법을 알아보자. PWM을 사용하기 위해서 먼저 고려해야할 사항과 순서는 다음과 같다.
제어하고자 하는 듀티비 선택 - 반전/비반전 모드 선택 - PWM모드 선택 - 클럭을 사용해야하므로 몇 비트의 타이머/카운터를 사용할지 선택 -선택사항에 맞춰 레지스터를 설정
위와 같은 순으로 선택사항을 고려하여 PWM 제어를 사용할 수 있다. 선택 사항을 고려하기 위해 반전/비반전 모드, PWM모드와 레지스터가 무엇이 있는지 알아보자.
(1) ATmega128A 반전/비반전 모드
비반전 모드 : 상승 에지에서 5V의 전압이 출력되고, 하강 에지에서 0V 전압이 출력된다. 이로 인해 출력되는 전압이 PWM 신호의 주기에 따라 달라지게 되며, 출력 전압의 크기는 PWM 신호의 duty cycle에 비례한다. 예를 들어, PWM 신호의 주기가 10ms이고 duty cycle이 50%인 경우, 5ms 동안은 상승 에지에서 5V의 전압이 출력되고, 다른 5ms 동안은 하강 에지에서 0V의 전압이 출력된다.
반전 모드 : 상승 에지에서 0V의 전압이 출력되고, 하강 에지에서 5V 전압이 출력된다. 이로 인해 출력되는 전압은 PWM 신호의 주기에 따라 달라지지만, 출력 전압의 크기는 duty cycle에 반비례한다. 즉, duty cycle이 증가할수록 출력 전압이 감소하게 된다. 예를 들어, PWM 신호의 주기가 10ms이고 duty cycle이 50%인 경우, 5ms 동안은 상승 에지에서 0V의 전압이 출력되고, 다른 5ms 동안은 하강 에지에서 5V의 전압이 출력된다.
이 두 모드는 PWM 신호를 이용하여 출력 전압을 조절하는 방법이 다르므로, 사용하는 회로의 목적과 요구사항에 따라 적절한 모드를 선택하여 사용해야 한다.
(2) ATmega128A PWM 모드 4가지
일반모드 (Normal Mode):
이 모드에서는 OCRnA 레지스터 값이 TOP 값으로 설정되어, TCNTn 레지스터 값이 TOP에 도달하면 카운터가 0으로 초기화됩니다. 그리고 OCnA 비교매치 이벤트가 발생할 때 PWM 신호가 생성된다.
위상정정모드 (Phase Correct Mode):
이 모드에서는 일반모드와 같이 OCRnA 레지스터 값이 TOP 값으로 설정된다. 하지만 카운터가 TOP에 도달할 때마다 방향을 바꿔 카운트를 다시 시작한다. 이 때, TOP 값의 1/2 크기에서 방향을 반대로 바꿔서 카운트를 진행한다. 이러한 과정에서 PWM 신호가 생성된다.
CTC 모드 (Clear Timer on Compare Match Mode):
이 모드에서는 OCRnA 레지스터 값이 MAX 값으로 설정된다. TCNTn 레지스터 값이 OCRnA 값과 같아지면 카운터가 0으로 초기화된다. 이 때, OCnA 비교매치 이벤트가 발생할 때 PWM 신호가 생성된다.
고속 PWM 모드 (Fast PWM Mode):
이 모드에서는 OCRnA 레지스터 값이 TOP 값으로 설정된다. TCNTn 레지스터 값이 TOP에 도달하면 바로 0으로 초기화된다. 그리고 OCnA 비교매치 이벤트가 발생할 때 PWM 신호가 생성된다. 일반모드와 다르게, TOP 값이 MAX가 아닌 ICRn 레지스터 값으로도 설정할 수 있다.
(3) ATmega128A PWM 레지스터
제어 레지스터
TCCRnA/B: Timer/Counter Control Register A/B. PWM 모드, 타이머 모드, 카운터 모드를 선택하고, 출력 비교 모드 및 클럭 분주 비율을 설정한다.
상태 레지스터
TCNTn: Timer/Counter Register. 카운터의 현재 값이 저장된다.
출력 비교 레지스터
OCRnA/B/C: Output Compare Register A/B/C :출력 신호의 duty cycle을 제어하는 값이다. 설정에 따라 OCR레지스터의 크기를 이용하여 디지털 출력을 제어할 수 있다.
인터럽트 관련 레지스터
TIMSKn: Timer/Counter Interrupt Mask Register. 타이머/카운터 인터럽트를 설정하는 레지스터이다.
TIFRn: Timer/Counter Interrupt Flag Register. 타이머/카운터 인터럽트 플래그가 저장되는 레지스터이다.
기타 레지스터
ICRn: Input Capture Register. 고속 PWM 모드에서 TOP 값을 설정하는 레지스터이다.
TCCRnC: Timer/Counter Control Register C. PWM 모드에서 하드웨어 출력 반전을 설정하는 레지스터이다.
4. ATmega128A에서의 PWM 코드 예시
다음은 PWM제어를 이용하여 LED의 밝기를 제어하는 코드이다. OCR 레지스터(듀티비를 결정하는 레지스터)를 이용하여 OCR 값이 0(듀티비 0%)일 때부터, 255(듀티비 100%)일 때 밝기를 볼 수 있다. 먼저 PWM을 사용하기 전에 조건을 설정하자.
- 타이머/카운터 0번 (OCR0/ PB4에 LED 연결)
- 고속 PWM모드
- 비반전 모드
- 분주비 : 1024
- 타이머 오버플로우 인터럽트 벡터 사용
위와 같이 설정하도록 하자.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdio.h> // printf, scanf등이 정의 되어 있다.
#include <string.h> // strcpy, strncmp, strcat 등이 들어 있다.
// OC0 : PWM 파형 출력 신호 PB.4 으로 출력을 설정
init_timer0_pwm()
{
// 파형 출력 핀 PB.4(OC0)을 출력으로 설정
DDRB |= (1 << 4);
// 타이머/카운터 0번을 고속 PWM 모드로 설정
TCCR0 |= (1 << WGM01) | (1 << WGM00); // P344 표 15-3
// 비반전 모드 OCR --> LOW
TCCR0 |= (1 << COM01); // P344 표 15-4
// 분주비를 1024 16000000/1024(분주비) : 15625HZ
// 15625HZ 에서 1펄스 의주기 : t = 1/f 1/15625 ==> 0.000064sec (64us)
// 0.000064sec (64us) x 256 = 16.4ms 16.4ms마다 TImer0 Overflow INT 발생
TCCR0 |= (1 << CS02) | (1 << CS01) | (1 << CS00);
TIMSK |= (1 << TOIE0); // 타이머 0 overflow INT 설정
TCNT0 =0;
}
// 16.4ms 마다 이곳으로 진입
volatile int tim_16ms =0;
ISR(TIMER0_OVF_vect)
{
TCNT0 = 0; // 주의 CTC아닌 경우는 반드시 초기화 해 줘야 한다.
tim_16ms++;
}
int main(void)
{
int dim=0;
int direction=1;
int i;
init_timer0_pwm();
sei();
OCR0 = dim;
while (1)
{
// duty cycle 설정: 1클럭 64us
// 초기 OCR0:1(64us) 부터 OCR0:255(16.4 ms)까지 //////////////////////////////////////////
// 16.4ms단위로 서서히 밝아 지도록 한다 OCR0: 0==>255
if (tim_16ms >= 1)
{
tim_16ms =0;
OCR0 = dim;
dim += direction; // 밝기 변화
if(dim == 0)
{
direction = 1;
}
else if(dim ==255)
{
direction = -1; // 밝기 변화
}
}
}
}
init_timer0_pwm()의 함수를 통해 위에서 언급한 설정 값 대로 레지스터를 초기화하였다. 타이머 오버플로우 인터럽트를 사용한다. 1024 분주비로 계산할 때, 16.4ms당 한 번씩 오버플로우 인터럽트가 발생할 것이다. 인터럽트가 발생할 때, 인터럽트 내 동작은 밝기의 변수인 tim_16ms를 1씩 증가시키도록 한다.
메인함수 내에서는 while문 무한루프를 돌면서, tim_16ms가 1씩 바뀔 때마다, OCR 값을 증가시켜 PORT B의 4번에 연결되어있는 LED의 밝기가 서서히 커질 것이다. 8비트 타이머/카운터이므로 OCR의 크기는 최대 255를 가질 것이며, MAX값인 255에 도달하면 다시 밝기가 서서히 줄어들 것이다.
'AVR' 카테고리의 다른 글
[AVR] 5. ATmega128A의 인터럽트 (0) | 2023.04.09 |
---|---|
[AVR] 4. ATmega128A의 타이머/카운터 (0) | 2023.04.05 |
[AVR] 3. ATmega128A UART 통신과 I2C 통신 (0) | 2023.04.05 |
[AVR] Pull-Up/Pull-Down 저항이란? (0) | 2023.03.31 |
[AVR] 2. ATmega128A 버튼 제어 (0) | 2023.03.30 |