So lately I've been trying to control the speed of a 120mm 4-pin computer fan using PWM. This is what I've got so far, with the part above void loop() being from this thread
// Definition of Arduino type
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define IS_MEGA (1)
#define IS_UNO (0)
#else
#define IS_MEGA (0)
#define IS_UNO (1)
#endif
// Analog output (i.e PWM) pins. These must be chosen so that we can change the PWM frequency without affecting the millis()
// function or the MsTimer2 library. So don't use timer/counter 1 or 2. See comment in setup() function.
// THESE PIN NUMBERS MUST NOT BE CHANGED UNLESS THE CODE IN setup(), setTransistorFanSpeed() AND setDiodeFanSpeed() IS CHANGED TO MATCH!
#if IS_UNO
// On the Uno we can only use the OC1B pin, so these pin numbers are both 10
const int transistorFanPin = 10; // OC1B
const int diodeFanPin = 10; // OC1B
#else
// On the Mega we use OC1B and OC1C
const int transistorFanPin = 12; // OC1B
const int diodeFanPin = 13; // OC1C
#endif
// Definitions for PWM fan control
const unsigned char maxFanSpeed = 80; // this is calculated as 16MHz divided by 8 (prescaler), divided by 25KHz (target PWM frequency from Intel specification)
void setup()
{
// Set up the PWM pins for a PWM frequency close to the recommended 25KHz for the Intel fan spec.
// We can't get this frequency using the default TOP count of 255, so we have to use a custom TOP value.
#if IS_UNO
// Only timer/counter 1 is free because TC0 is used for system timekeeping (i.e. millis() function),
// and TC2 is used for our 1-millisecond tick. TC1 controls the PWM on Arduino pins 9 and 10.
// However, we can only get PWM on pin 10 (controlled by OCR1B) because we are using OCR1A to define the TOP value.
// Using a prescaler of 8 and a TOP value of 80 gives us a frequency of 16000/(8 * 80) = 25KHz exactly.
TCCR1A = (1 << COM1B1) | (1 << COM1B0) | (1 << WGM11) | (1 << WGM10); // OC1A (pin 9) disconnected, OC1B (pin 10) = inverted fast PWM
#ifdef FAN_AUDIO_TEST
// test code to get 440Hz output (= concert A) to test the logic
OCR1AH = 0;
OCR1BL = 71; // 50% duty cycle
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS12); // TOP = OCRA, prescaler = 256
OCR1AL = 141; // TOP = 141, 16000000 / (256 * 142) = 440.014
OCR1BH = 0;
#else
OCR1AH = 79;
OCR1AL = 0; // TOP = 79
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // TOP = OCR0A, prescaler = 8
OCR1BH = 0;
OCR1BL = maxFanSpeed; // max fan speed (i.e. pin 5 initially low all the time)
#endif
TCNT1H = 0;
TCNT1L = 0;
#else
// On the Mega we use TC1 and OCR1B, OCR1C
TCCR1A = (1 << COM1B1) | (1 << COM1B0) | (1 << COM1C1) | (1 << COM1C1) | (1 << WGM11) | (1 << WGM10); // OC1A disconnected, OC1B = OC1C inverted fast PWM
TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11); // TOP = OCR1A, prescaler = 8
TCCR1C = 0;
OCR1AH = 0;
OCR1AL = 79; // TOP = 79
OCR1BH = 0;
OCR1BL = maxFanSpeed;
OCR1CH = 0;
OCR1CL = maxFanSpeed;
TCNT1H = 0;
TCNT1L = 0;
#endif
// We have to enable the ports as outputs before PWM will work.
pinMode(transistorFanPin, OUTPUT);
pinMode(diodeFanPin, OUTPUT);
pinMode(A0, INPUT);
Serial.begin(9600);
}
// Set the transistor fan speed, where 0 <= fanSpeed <= maxFanSpeed
void setTransistorFanSpeed(unsigned char fanSpeed)
{
OCR1BH = 0;
OCR1BL = fanSpeed;
}
// Set the diode fan speed, where 0 <= fanSpeed <= maxFanSpeed
void setDiodeFanSpeed(unsigned char fanSpeed)
{
#if IS_UNO
OCR1BH = 0;
OCR1BL = fanSpeed;
#else
OCR1CH = 0;
OCR1CL = fanSpeed;
#endif
}
void loop() {
if (analogRead(A0) >= 800) {
setTransistorFanSpeed(80);
Serial.println("HIGH");
Serial.println(analogRead(A5));
} else {
setTransistorFanSpeed(0);
Serial.println("LOW");
Serial.println(analogRead(A5));
}
delay(100);
Serial.println("I'm running.");
}
In the serial monitor I'm getting the output I'm expecting ("HIGH" and 1023 when I've bridged the arduinos 5V pin to A0), but the signal doesn't seem to have any effect on the fan at all. In the thread linked above there's talk about this being an inverted fast PWM signal, and that it's assumed I'm using an opto isolator. Could this be why the signal doesn't have an effect on the fan? If anyone out there has any experience on this and are able to help me, that would be greatly appreciated.. :)