MCU 제어에서
I/O포트를 이용하여 HIGH(5v), LOW(0v) 두 상태만 출력할 수 있는
디지털 출력도 있지만,
그 중간 상태인 1.1v ~ 4.9v등의 값을 출력하는
아날로그 출력도 있습니다.
하지만 우리가 사용하는 Atmega328에는 그런 기능이 없습니다.
대신 PWM이라는 방식으로 아날로그 출력을 비슷하게 구현할 수 있습니다.
PWM이란
PWM은 Pulse Width Modulation의 약자로
펄스(신호)의 On/Off 시간을 조절하는 제어를 의미합니다.
PWM은 주기적으로 반복되는 On/Off 시간을 조절하여
아날로그 비슷한 신호를 만들어냅니다.
예를 들어 LED의 밝기를 최대 밝기의 50%만으로 밝히고 싶다면
On시간을 50%, Off시간을 50%로 아주아주 빠르게 깜박이면 사람의 눈에는 50%의 밝기로 보일 것입니다.
혹은 75%의 밝기로 하고싶다면
On시간을 75%(전체 시간의) Off시간을 25%로 설정한 다음 아주아주 빠르게 깜박이면, 75%의 밝기로 보일 것입니다.
조금 더 구체적으로
On시간을 10ns, Off시간을 10ns로 하면, 총 On/Off시간은 20ns가 될 거고,
출력은 50%가 될 것입니다.
또는
On시간을 15ns, Off시간을 5ns로 하면 총 On/Off시간은 20ns가 될 거고,
출력은 75%가 될 것입니다.
위의 구체적인 예시에서 총 On/Off시간이 20ns라고 했으니, On/Off가 20ns마다 반복될 것입니다.
다른 말로 20ns는 50MHz이니 50MHz의 주기로 On/Off가 반복될 것이라는 것을 알 수 있습니다.
MCU에서 PWM 제어
MCU는 내부적으로 자동으로 일정 주기마다 1씩 증가되는 레지스터(TCNT0)가 있습니다.
이 레지스터는 설정된 주기마다 0에서부터 255까지 1씩 증가되며
255를 넘어가면 0으로 돌아오는 성질을 갖고 있습니다.
예를 들어 주기를 1ns로 설정했다면, 1ns마다 0, 1, 2 .....255, 0, 1, 2.....이렇게 값이 올라갔다 떨어집니다.(계속 반복)
즉 1ns * 256( 0 ~ 255까지 총 256) = 256ns마다 값이 0으로 변하는 것입니다.
MCU는 위의 TCNT0라는 레지스터와 기준 값을 자동으로 비교하여 PWM을 구현합니다.
기준 값이 127이면, TCNT0값과 127값을 비교하여
TCNT0가 0 ~ 126사이, 즉 127보다 작으면 On
TCNT0가 127 ~ 255사이, 즉 127이상이면 Off하는 방식으로 PWM을 구현합니다.(50% 출력)
같은 방식으로
기준 값이 100이라면 TCNT0값과 100값을 비교하여
TCNT0가 0 ~ 99사이에 있으면 On
100이상이면 Off하는 방식으로 PWM을 구현합니다.(대략 39.2% 출력)
필요한 레지스터
그럼 PWM을 설정하기 위해서 우리가 설정해야하는 값은
기준값, TCNT0레지스터 주기( = On/Off주기 / 256)입니다.
또한 PWM활성화 등의 추가적인 작업도 해야합니다.
이들은 TCCR0A, TCCR0B 레지스터 값을 변경하여 설정할 수 있습니다.(Timer Counter Control Register A, B)
우선 TCCR0A의 부조는 다음과 같습니다.
비트 번호 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
비트 이름 | COM0A1 | COM0A0 | COM0B1 | COM0B0 | 사용 못 함 | 사용 못 함 | WGM01 | WGM00 |
초기 값 | 0 | 0 | 0 | 0 | 사용 못 함 | 사용 못 함 | 0 | 0 |
일단 위에 보이는 COM0A1, 0와 COM0B1, 0가 있는데요, 여기서 0A와 0B는 핀 이름이라고 생각하시면 됩니다.
즉 COM0A1과 COM0A0를 설정하면 0A(OC0A)번 핀에 PWM관현 설정한 것이고,
COM0B1과 COM0B0를 설정하면 0B(OC0B)번 핀에 대해 PWM관련 설정한 것입니다.
여기서는 0A번(OC0A)번 핀을 기준으로 설정할 것입니다.
COM0A0, COM0A1 (PWM 활성화 비트)
COM0A0와 COM0A1비트는 PWM 기능을 활성화할 때 사용하는 비트라고 생각하면 됩니다.(평소에는 비활성화 상태)
COM0A1 | COM0A0 | 설명 |
0 | 0 | OC0A핀의 PWM 기능 비활성화 |
0 | 1 | 토글 모드 |
1 | 0 | 비반전 모드(일반적으로 사용된다.) |
1 | 1 | 반전 모드 |
보통은 비반전 모드를 사용하기 때문에
저희는 무조건 비반전 모드를 사용할 것입니다.(반전/비반전 모드는 다음 포스트에서 설명)
7번 6번 비트를 각각 1, 0으로 설정해주면 OC0A핀의 PWM기능을 사용할 수 있게 됩니다.
(OC0B핀은 PWM으로 안 사용할 것이기 때문에 COM0B1(5번 비트), COM0B0(4번 비트)는 0으로 설정!)
WGM02, WGM01, WGM00비트
WGM02비트는 TCCR0B레지스터에 있고,
WGM01, WGM00비트는 TCCR0A레지스터에 있습니다.
WGM은 Wave Generation Mode의 약자로,
일정 주기마다 값이 증가하고 떨어지는 TCNT0에 관한 것입니다.
이는 다음 포스트에서 설명할 것이며,
이번 포스트에서는 "fast PWM을 사용하기 위해 WGM02 ~ 00비트를 0,1,1로 설정한다."라고만 알아 두세요.
따라서 우리는 TCCR0A값을 10000011로 할 것입니다.
(COM0A1, COM0A0를 1 0
COM0B1, COM0B0를 0 0
3, 2번 비트는 무조건 0 0으로
WGM01 00은 fast PWM을 위해 1 1로)
TCCR0B 레지스터의 구조는 다음과 같습니다.
비트 번호 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
비트 이름 | FOC0A | FOC0B | 사용 못 함 | 사용 못 함 | WGM02 | CS2 | CS1 | CS0 |
초기 값 | 0 | 0 | 사용 못 함 | 사용 못 함 | 0 | 0 | 0 | 0 |
여기서 FOC0A, FOC0B는 PWM과 상관이 없는 비트이기 때문에 0, 0으로 만들어 줍니다.
WGM02
WGM02비트는 TCCR0A에 있는 WGM01, WGM00비트와 같이 사용되며,
이 또한 다음 포스트에서 설명할 것이기 때문에 WGM02비트는 0으로 설정합니다.
CS02 CS01 CS00 (주기 설정 비트)
PWM에서 중요한 On/Off 주기와 관련된(정확하게는 TCNT0의 주기) 비트입니다.
Atmega328에서는 가장 빠른 16MHz부터
2MHz, 500KHz, 250KHz, 125KHz, 62.5KHz, 15.625KHz로 설정할 수 있는데,
이에 관한 것이 CS[02:00]비트 입니다.
16MHz로 주기를 맞추거나
1/8배인 2MHz
1/32배인 500MHz등으로 설정할 수 있습니다.
이 때 1/8, 1/32를 분주비라고 부릅니다.
CS02 ~ 00은 이러한 분주비를 설정하는 비트로, 다음과 같습니다.
CS02 | CS01 | CS00 | 설명 |
0 | 0 | 0 | PWM 비활성화 |
0 | 0 | 1 | 분주비 1/1 (= 16MHz) |
0 | 1 | 0 | 분주비 1/8 (= 2MHz) |
0 | 1 | 1 | 분주비 1/32 (=500KHz) |
1 | 0 | 0 | 분주비 1/64 (=250KHz) |
1 | 0 | 1 | 분주비 1/128 (=125KHz) |
1 | 1 | 0 | 분주비 1/256 (=62.5KHz) |
1 | 1 | 1 | 분주비 1/1024 (=15.625KHz) |
만약 CS02~00을 0, 0, 1로 설정하면 TCNT0는 16MHz주기로 1씩 증가할 것입니다.(즉 On/Off 주기는 1/(16MHz) * 256)
또는 0 1 1로 설정한다면 TCNT0는 500KHz를 주기로 1씩 증가할 것입니다.(즉 On/Off 주기는 1/(500MHz) * 256)
이 값은 상황에 따라 적절하게 설정하면 되며, 저희는 분주비를 1/32로 하겠습니다.
따라서 TCCR0B의 값은 00000011입니다.
(FOC0A, FOC0B는 PWM과 관련 없기 때문에 0 0
5, 4비트는 무조건 0 0
WGM02비트는 fast PWM을 위해 0
CS02 ~ 00은 분주비를 1/32로 하기 위해 0 1 1로)
OCR0A 레지스터 (기준 값)
저희는 OC0A핀을 사용할 것이기 때문에 OC0A Register를 사용해야합니다.
(OC0B를 사용한다면, OCR0B)
OCR0A 레지스터는 위에서 설명한 기준값에 관한 레지스터로,
MCU는 TCNT0가 OCR0A값보다 작으면 On
크거나 같으면 Off로 자동으로 제어해줍니다.
따라서 저희는 OCR0A값을 변경하여 On, Off %를 변경할 수 있습니다.
OCR0A의 크기는 8bit이기 때문에 0 ~ 255의 값으로 설정할 수 있으며,
이는 0% ~ 100%의 출력을 설정할 수 있다는 의미입니다.
예를들어 OCR0A값을 127로 하면 50%의 출력이,
100으로 하면 39.2%의 출력이 나옵니다.(위에서 한 번 언급함)
참고 영상
예제 코드
다음 코드는 분주비가 1/32이고,
1초 동안 100%
1초 동안 50%
1초 동안 0%를 출력하는 코드입니다.
void setup() {
TCCR0A = 0b10000011;
TCCR0B = 0b00000011;
OCR0A = 255; //100%
delay(1000); //1초 기다리기
OCR0A = 127; //50%
delay(1000); //1초 기다리기
OCR0A = 0; //0%
delay(1000); //1초 기다리기
}
void loop() {
}
'atmega328(아두이노 미니 이용)' 카테고리의 다른 글
스터디 상생플러스 5 - 1 (USRAT 통신) (0) | 2020.05.01 |
---|---|
스터디 상생플러스 4 - 1 (ADC 이론) (0) | 2020.04.22 |
스터디 상생플러스 2 - 3 (Atmega328 I/O제어 실습) (1) | 2020.04.13 |
스터디 상생플러스 2 - 2 (Atmega328 I/O제어 이론) (0) | 2020.04.13 |
스터디 상생플러스 2 - 1 (Atmega328 I/O 방향 설정 이론) (0) | 2020.04.13 |