
알리에서 아두이노와 호환된다는 ATmega328P 보드를 하나 들였습니다. 아두이노 IDE는 처음이라 설치하고 나서 뭘 해야 할지 한참 헤맸는데, 결국 제일 먼저 손이 가는 건 역시 LED였어요. 그냥 깜빡이기만 하면 심심하니 밝기가 천천히 올라갔다 내려가는 디밍을 만들어 봤습니다.
보통 LED 밝기 조절은 analogWrite()로 하드웨어 PWM을 씁니다. 그런데 13번 핀은 우노 계열에서 PWM 핀이 아니라서 analogWrite()를 걸어도 켜짐/꺼짐 둘 중 하나로만 동작합니다. 그래서 여기서는 digitalWrite()만 가지고, 루프 안에서 직접 켜고 끄는 비율을 바꾸는 방식으로 밝기를 흉내 냈습니다. 제목에 "PWM 아님"이라고 붙인 이유가 이겁니다. 엄밀히 따지면 원리는 소프트웨어로 만든 PWM이지만, 타이머 하드웨어는 전혀 건드리지 않습니다.
// 아두이노 LED 예제 - LED dimming
// 필요한 라이브러리
#include <Arduino.h>
// LED 핀 설정
const int ledPin = 13;
int c = 0; // dim 기준값
int i = 0; // dim 값
int n = 1; // dim 값 업/다운
int x = 1; // dim 기준값 업/다운
int q = 0; // 루프를 도는데 너무 빠르므로 속도 조절을 위한 카운트
void setup() {
// LED 핀을 출력으로 설정
pinMode(ledPin, OUTPUT);
}
void loop() {
q ++;
if(q>5){
q =0;
i = i + n;
// 밝기가 0에서 255 범위를 벗어나면 방향 전환
if (i <= 0 || i >= 255) {
n = -n;
c = c + x;
if (c <= 0 || c >= 255) {
x = -x;
}
}
}
//digitalWrite(ledPin, !digitalRead(ledPin));
if (i < c) {
digitalWrite(ledPin, HIGH);
} else {
digitalWrite(ledPin, LOW);
}
}
변수 다섯 개가 하는 일
처음 보면 c, i, n, x, q가 뒤섞여서 헷갈리는데, 역할을 둘로 나눠 보면 단순합니다.
i는 0과 255 사이를 계속 오르내리는 톱니(정확히는 삼각파) 카운터입니다. n이 +1이면 올라가고, 끝에 닿으면 n = -n으로 방향을 뒤집어 내려옵니다. c는 밝기 기준값이에요. i가 한 번 끝에 닿을 때마다 c가 x만큼 한 칸씩 움직이고, c도 0이나 255에 닿으면 x를 뒤집어 반대로 갑니다.
출력은 맨 아래 비교 한 줄로 결정됩니다. i < c인 동안만 LED를 켜죠. i가 0~255를 고르게 훑고 지나가니까, 한 주기 중 LED가 켜져 있는 비율은 대략 c / 255가 됩니다. c가 작으면 잠깐만 켜져서 어둡게, c가 크면 거의 내내 켜져서 밝게 보이는 거고요. c가 천천히 올라갔다 내려가니 눈에는 숨 쉬듯 밝아졌다 어두워지는 LED로 보입니다.
q는 속도 조절용입니다. loop()가 워낙 빨리 돌아서 매번 i를 움직이면 변화가 너무 빠르기 때문에, 루프 여섯 번에 한 번만 i를 갱신하도록 걸러 줍니다. 주석 처리된 digitalWrite(ledPin, !digitalRead(ledPin));는 디밍 이전에 단순 토글로 테스트하던 흔적이라 지워도 됩니다.
돌려 볼 때 막히는 부분
깜빡임이 눈에 보인다면 한 주기가 너무 길다는 뜻입니다. q > 5의 5를 줄이면 i가 더 빨리 돌아 주기가 짧아지고 깜빡임이 줄어듭니다. 반대로 밝기 변화 자체가 너무 빠르게 느껴지면 c를 매번 1씩이 아니라 몇 주기에 한 번 움직이도록 카운터를 하나 더 두면 됩니다.
이 방식은 loop()가 쉬지 않고 돌아야 성립합니다. 루프 안에 delay()를 넣거나 시리얼 출력처럼 시간을 잡아먹는 코드가 끼면 켜짐/꺼짐 비율이 틀어져서 밝기가 들쭉날쭉해집니다. 다른 작업과 같이 돌려야 한다면 PWM 핀(우노 기준 3, 5, 6, 9, 10, 11번)으로 LED를 옮기고 analogWrite()를 쓰는 편이 낫습니다. 이 예제는 PWM 핀이 모자라거나, 소프트웨어 PWM 원리를 눈으로 확인하고 싶을 때 쓸 만합니다.
예전에 코드비전(CodeVisionAVR)이나 어셈블러로 AVR을 만질 때와 비교하면, pinMode()·digitalWrite() 몇 줄로 끝나는 아두이노 쪽이 확실히 점잖습니다. 레지스터를 직접 찍던 시절엔 이 정도 예제도 꽤 손이 갔거든요.
다음에는 13.56MHz RFID 모듈인 PN532를 ATmega328P에 붙여서 통신을 해 볼 생각입니다. 이 모듈은 SPI, I2C, UART 세 가지 인터페이스를 다 지원해서 어느 쪽으로 붙일지부터 골라야 합니다.
