Datasheet
FAN Model & Specification
NOCTUA NF-P14s redux-1200 PWM
Wiring
Blue 라인을 통해 PWM으로 속도제어를 할 수 있고, Green 라인을 통해 타코미터 신호를 받아 팬 속도를 알 수 있다.
아두이노 9번 핀(PWM Out), 2번 핀(Tach In) 사용
PWM Speed Control
일단 PWM 신호 핀 없이 +12V랑 GND를 연결하면 팬이 최고속도로 도는데 아마도 PWM signal 입력을 받는쪽이 내부적으로 풀업이 되있나보다.
PWM 주파수는 25kHz이고 듀티를 0~100% 사이로 제어하면 된다.
처음엔 digitalWrite랑 delayMicroseconds를 써서 PWM을 만들까 했는데 25kHz는 40us정도니까 delayMicroseconds로 듀티를 세밀하게 제어하기 힘들 것 같아서 타이머와 PWM 출력핀을 쓰기로 했다.
팬의 속도는 PWM 신호의 듀티 사이클에 따라 거의 선형적으로 증가한다. 단 듀티가 20% 미만일 때는 출력 속도가 어떻게 된다고 딱 정의되지 않는다.
내가 구입한 팬은 최고 속도가 1200 RPM이고 미니멈이 350 RPM이니 듀티 30% 이상부터는 속도가 선형적으로 제어가될거다.
아두이노 GPIO 입출력 레벨은 5V라서 바로 연결하면 되겠다.
아두이노에 쓰이는 ATmega328P에는 8비트 타이머 2개와 16비트 타이머 1개가 있는데 그 중 타이머0은 delay() 등 시스템 함수에 사용되고 여기서는 PWM 신호 생성을 위해 16비트 타이머인 타이머0을 쓰기로한다.
타이머들은 16MHz 시스템클럭을 베이스로 사용하기 때문에 분주하지 않고 640까지 카운트하도록 설정하면 25kHz의 주기를 얻어낼 수 있다.
아래는 타이머1을 Fast PWM 모드로 설정하고 주파수를 25kHz로 설정한 뒤 비교 출력핀인 9번핀을 활성하하고 듀티는 0으로 초기화하는 코드이다.
참고로 타이머마다 비교 출력 핀이 2개씩 있고 16비트 타이머니까 ICRx와 OCRxA는 16비트 레지스터이다. 그리고 ATmega328P에도 쉐도우 레지스터가 있는지 OCR 값이 변경될 때 바로 PWM 출력에 반영되지 않고 다음 주기부터 변경된 값이 반영된다고 한다.
// 타이머1 설정
TCCR1A = 0; // 초기화
TCCR1B = 0; // 초기화
TCCR1A = (1 << WGM11); // Fast PWM 모드
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); // 프리스케일러 1
ICR1 = 640; // 16MHz / 640 = 25kHz
OCR1A = 320; // 초기 듀티 50%
TCCR1A |= (1 << COM1A1); // OC1A핀 (9번핀) 활성화
이후 Output Compare Register 값을 변경하여 듀티 사이클을 제어할 수 있다.
OCR1A = map(adcValue, 0, 1023, 0, 640);
Tachometer Signal Mornitoring
타코미터 시그널은 1회전당 두 번의 펄스가 출력되고 오픈 컬렉터 타입 출력이기 때문에 아두이노 5V 쪽에 2.7k옴 이상으로 풀업해주면 된다.
아두이노 외부 인터럽트는 2번핀 또는 3번핀이 사용 가능한데 여기선 2번핀을 사용했고 Rising edge에 인터럽트가 트리거되도록 설정한다.
// 외부 인터럽트 설정
pinMode(extiPin, INPUT);
attachInterrupt(digitalPinToInterrupt(extiPin), extiISR, RISING);
void extiISR() {
static uint32_t lastTime = 0;
uint32_t currentTime = micros();
if(currentTime - lastTime >= 20000) {
pulseCount++;
lastTime = currentTime;
}
}
프로그램이 시작한 이후 경과한 시간을 마이크로초로 반환하는 micros() 함수를 사용해 Rising edge 간의 간격을 측정하면 펄스의 주파수를 알 수 있고, 펄스가 1회전당 두번 튀니까 1초에 몇 회전하는지 알 수 있다. 다만 어차피 그렇게 세밀하게 보려는 건 아니어서 외부인터럽트 콜백함수 내에서는 pulseCount만 증가 시키고 loop문 내에서 1초에 한번 pulseCount 횟수를 확인해 속도를 계산한 뒤 LCD에 표시하는 방법을 사용했다.
그리고 고주파로 갈수록 생각보다 노이즈가 많이타는거 같아서 노이즈를 무시하기 위한 코드를 추가했다. 팬이 가장 빠르게 돌 때 펄스 간격이 25ms 정도여서 20ms 이내에 트리거된 경우 잡음으로 보고 무시하기로 한다.
Code
#include <LiquidCrystal_I2C.h> // I2C 인터페이스 1602 LCD를 제어하기 위해 라이브러리 포함
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C slave address는 0x3F 또는 0x27
const int pwmPin = 9; // 타이머1의 PWM 9번핀 사용
const int extiPin = 2; // interrupt 0이랑 연결된 2번핀 사용
volatile uint32_t pulseCount = 0;
void setup() {
// UART 설정
Serial.begin(115200);
// LCD 초기화
lcd.clear();
lcd.init();
lcd.backlight();
// PWM 설정 설정
pinMode(pwmPin, OUTPUT);
TCCR1A = 0; // 타이머1 레지스터 초기화
TCCR1B = 0; // 타이머1 레지스터 초기화
TCCR1A = (1 << WGM11); // Fast PWM 모드
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS10); // 프리스케일러 1
ICR1 = 640; // 16MHz / 640 = 25kHz
OCR1A = 0; // 초기 듀티 0%
TCCR1A |= (1 << COM1A1); // OC1A핀 (9번핀) 활성화
// 외부 인터럽트 설정
pinMode(extiPin, INPUT);
attachInterrupt(digitalPinToInterrupt(extiPin), extiISR, RISING);
}
void loop() {
static uint32_t lastTick = 0;
char buf[17];
uint32_t currentTick = millis();
if(currentTick - lastTick >= 1000) {
Serial.println(pulseCount);
sprintf(buf, "SPEED : %4d RPM", round(pulseCount * 60.0 / 2));
pulseCount = 0;
lcd.setCursor(0, 1);
lcd.print(buf);
lastTick = currentTick;
}
// 가변저항 읽기
int adcValue = analogRead(A3);
// LCD 출력
sprintf(buf, "PWM : %4d %%", round((float)adcValue / 1024.0 * 100.0));
lcd.setCursor(0, 0);
lcd.print(buf);
// PWM 듀티 사이클 제어
OCR1A = map(adcValue, 0, 1023, 0, 640);
}
// 외부 인터럽트 콜백함수
void extiISR() {
static uint32_t lastTime = 0;
uint32_t currentTime = micros();
if(currentTime - lastTime >= 20000) {
pulseCount++;
lastTime = currentTime;
}
}
'임베디드 개발 > 아두이노' 카테고리의 다른 글
아두이노 ] VIN으로 외부전원을 공급하는 동시에 USB 케이블을 연결해도 될까? (0) | 2024.07.26 |
---|---|
아두이노 ] 릴레이 모듈 + 흡입펌프 다루기 (0) | 2022.06.30 |
[ 아두이노 ] 풀업저항 , 풀다운저항 달아서 버튼 ( 스위치 )누르면 불켜지는 회로 구성하기 (0) | 2022.05.21 |
NodeMCU 를 Arduino IDE에서 사용하기 위한 환경 설정 방법 (0) | 2022.05.04 |
[ 아두이노 ] I2C LCD로 문자 출력 + 한 칸 씩 옆으로 이동 + 커스텀으로 한글 출력 + 타이머 출력 (1) | 2022.05.02 |