4핀 PWM 팬을 제어하는 방법에 대해 고민하다가, 마침 5개의 팬이 있어 직접 만들어보기로 했습니다. 주파수는 125Hz ~ 8MHz 사이에서 조정 가능하며 듀티 사이클도 가변적입니다. 이 회로는 타이머 1의 OCR1A(핀 9)와 ICR1(핀 10)을 사용하는 모드 10 PWM 위상 보정 방식을 활용합니다. 디버깅 과정에서 핸드헬드 오실로스코프를 사용했는데, 주파수와 듀티 사이클을 시각적으로 확인할 수 있어 추측 없이 정확한 튜닝이 가능했습니다.
중요: 사용 중인 팬의 데이터시트를 반드시 확인하세요. 제 경우 제어 핀이 노란색이었습니다(일반적으로 타코미터 선으로 알려져 있음). 또한 팬의 허용 주파수 범위(또는 '중심 주파수')를 확인하고, 필요시 코드의 주파수 값을 조정해야 합니다. 안정적인 전원 공급 장치도 필수입니다(저는 오래된 PC의 ATX 파워를 개조해 사용했습니다).
참고: 다른 타이머를 사용해 2개의 팬을 독립적으로 제어해보려 했으나 작동하지 않았습니다. 두 번째 타이머의 해상도가 낮기 때문입니다(16비트 vs 8비트). 이로 인해 주파수와 듀티 조정 단계가 노이즈가 많고 파형이 점점 왜곡됩니다. 또한 값 입력도 직관적이지 않습니다.
소스 코드
const byte OC1A_PIN = 9;
const byte OC1B_PIN = 10;
const word PWM_FREQ_HZ = 25000; // 이 값을 조정하여 주파수 변경 (Hz 단위!) (현재 25kHz로 설정)
const word TCNT1_TOP = 16000000/(2*PWM_FREQ_HZ);
void setup() {
pinMode(OC1A_PIN, OUTPUT);
// 타이머 1 제어 및 카운트 레지스터 초기화
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
// 타이머 1 설정
// COM1A(1:0) = 0b10 (출력 A 상승 에지 클리어/하강 에지 셋)
// COM1B(1:0) = 0b00 (출력 B 일반 동작)
// WGM(13:10) = 0b1010 (위상 보정 PWM)
// ICNC1 = 0b0 (입력 캡처 노이즈 캔슬러 비활성화)
// ICES1 = 0b0 (입력 캡처 에지 선택 비활성화)
// CS(12:10) = 0b001 (입력 클럭 선택 = 클럭/1)
TCCR1A |= (1 << COM1A1) | (1 << WGM11);
TCCR1B |= (1 << WGM13) | (1 << CS10);
ICR1 = TCNT1_TOP;
}
void loop() {
setPwmDuty(0);
delay(5000);
setPwmDuty(25); // 0-100 사이 값으로 듀티 사이클 조정
delay(5000);
// setPwmDuty(50);
// delay(20000);
// setPwmDuty(75);
// delay(20000);
// setPwmDuty(100);
// delay(20000);
}
void setPwmDuty(byte duty) {
OCR1A = (word) (duty*TCNT1_TOP)/100;
}