Arduino Forum

Using Arduino => Motors, Mechanics, and Power => Topic started by: Daka101 on Jun 10, 2012, 05:49 pm

Title: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 10, 2012, 05:49 pm
Hey Everyone,
I am trying to control an arctic F12 Pro PWM with my Mega 2560. It is a four wire fan, Vc, grd, sensor/tach and PWM.
When I plugged it in it runs at high. I Tried to run a fading led sketch, but it had no affect on it. I am using this to cool a LED light over a Reef tank. It will be contolled by the out put of a DS18b20. Right now would love to just get it to  change speed with different numbers in the Sketch. WARNING...I am aNoobie...lol
Any help would be great. Also if you do know of a sketch that interfaces with a DS18B20 please add that too.... Basic  Temp a =25% b= 50%, c= 75% and d =100%.
Thanks in advance
Daka
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: dc42 on Jun 10, 2012, 06:50 pm
I've successfully PWM'd a fan, AFAIR it was an Arctic F8 PWM. The PWM input is designed to be driven from an open collector output, and the Intel specification calls for the PWM frequency to be between 21kHz and 28kHz, see http://www.formfactors.org/developer%5Cspecs%5CREV1_2_Public.pdf (http://www.formfactors.org/developer%5Cspecs%5CREV1_2_Public.pdf). This is much faster than the usual Arduino PWM frequency of around 600Hz. I reprogrammed one of the counter/timer registers to generate a 25kHz PWM signal.

To drive the PWM pin, I used an opto isolator because I needed isolation. However, you should be able to drive it directly because the maximum open circuit voltage on the PWM pin is 5.25V and the maximum current sourced is 5mA. Or you could use a small signal NPN transistor to drive it, or a 2N7000 mosfet.
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 10, 2012, 08:26 pm
Thanks, Now how do I program the clock/counter to 25kHz and what is the value I use to vary the speed...0-255?
What pin will this effect?
I have a mega 2560
Thanks
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: dc42 on Jun 10, 2012, 09:23 pm
See http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM (http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM) and http://arduino.cc/playground/Code/PwmFrequency (http://arduino.cc/playground/Code/PwmFrequency) for the basics - although I don't think they tell you how to get a PWM frequency quite as high as you need.

Here is the code I used:

Code: [Select]

// 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() AND setFanSpeed() IS CHANGED TO MATCH!
// On the Uno we can only use the OC1B pin

const int fanPin = 10;     // OC1B
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.
 // 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 = 0;
 OCR1AL = 79;  // TOP = 79
 TCCR1B = (1 << WGM13) | (1 << WGM12) | (1 << CS11);  // TOP = OCR0A, prescaler = 8

 OCR1BH = 0;
 OCR1BL = maxFanSpeed;  // max fan speed 
#endif

 TCNT1H = 0;
 TCNT1L = 0;

 // We have to enable the ports as outputs before PWM will work.
 pinMode(fanPin, OUTPUT);
}

// Set the fan speed, where 0 <= fanSpeed <= maxFanSpeed
void setTransistorFanSpeed(unsigned char fanSpeed)
{
 OCR1BH = 0;
 OCR1BL = fanSpeed;
}
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 10, 2012, 11:09 pm
Thanks for the in formation.
As I am new at this, which varible changes the speed, what are the 0% and 100% numbers, and will this work on a mega 2560 R3
Thanks for you help.
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: dc42 on Jun 10, 2012, 11:20 pm
OK here is the version for Uno or Mega. For the Mega it can control 2 fans. To set the speeds, call setTransistorFanSpeed or setDiodeFanSpeed. The range is 0 to maxFanSpeed, however in practice the minimum fan speed is about 30%.

Code: [Select]

// 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 = 0;
  OCR1AL = 79;  // 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);
}

// 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
}
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 10, 2012, 11:35 pm
DC,
Thank you very much, I will set up eveything tonight and give it a try... Will report back Ya or Na.
Thanks
Daka
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: dc42 on Jun 11, 2012, 09:30 am
btw the code as written assumes that the Arduino is connected to the pwm fan input via an opto isolator or npn transistor that pulls the pwm input low when the Arduino pin is high. If you connect it directly, then the speed control will work in reverse, unless you change the counter timer mode from "inverted fast pwm" to "fast pwm".
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 11, 2012, 04:26 pm
The code would not compile, so I removed the parts that refered to the Uno. Had a Compile error about a loop being called in the main.ccp, Found an answer to that, Just added a void loop.
I will change the fast PWM. Sorry for the lame questions :~ ,but just started working with  the arduino last week. REally enjoy it.
But which varible do you change for the speed, I believe the params  are 0 -80.
Thanks again for your help.
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: dc42 on Jun 11, 2012, 05:18 pm
Yes, the speed control is from 0 to 80 (more practically, from about 28 to 80 with most fans). I don't change a variable to set the speed, I make a call to setTransistorFanSpeed or setDiodeFanSpeed. The system remembers the speed you asked for until the next time you call the function. Example:

Code: [Select]

void loop()
{
  setTransistorFanSpeed(80);
  delay(30000);   // run for 30 seconds at maximum fan speed
  setTransistorFanSpeed(28);
  delay(30000);   // run for 30 seconds at low fan speed
}


btw I haven't tested the code on a Mega, only on a Uno, so it could be incorrect.
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: Daka101 on Jun 12, 2012, 04:33 pm
Hey,

Still not working, But I think it maybe the inverted fast PWM vs Fast PWM. How do you change it to just Fast PWM?
Thanks
Title: Re: Controlling PC cooling fan that has a PWM input
Post by: forthings on Nov 22, 2016, 11:21 am
I'm using this code
Code: [Select]


// 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
#define UPDATE_ZYKLUS 1000
// 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)
const int ANZAHL_INTERRUPTS = 1; // Anzahl der Interrupts pro Umdrehung (1 oder 2)
int counter_rpm = 0;
int rpm = 0;
unsigned long letzte_ausgabe = 0;
int baseTime = 10;
int eingabe = 0;
void setup()
{
    Serial.begin(9600);
  digitalWrite(3, HIGH);
  attachInterrupt(3, rpm_fan, FALLING);
  // 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 = 0;
  OCR1AL = 79;  // 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);
}

// 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()
{
 
 // setTransistorFanSpeed(28);
  if (Serial.available()){
    eingabe = Serial.parseInt() ;
      setTransistorFanSpeed(eingabe);
       Serial.print("Set RPM: ");
        Serial.print(eingabe);
  }
    if (millis() - letzte_ausgabe >= UPDATE_ZYKLUS){
    // Interrupt deaktivieren um das rechnen nicht zu unterbrechen.
    detachInterrupt(0);
 
    // RPM errechnen und ausgeben:
    rpm = counter_rpm * (60 / ANZAHL_INTERRUPTS);
    Serial.print("RPM: ");
    Serial.println(rpm);
 
    // Counter zuruecksetzen
    counter_rpm = 0;
 
    // Zeitpunkt setzen
    letzte_ausgabe = millis();
 
    // Interrupt wieder aktivieren
    attachInterrupt(0, rpm_fan, FALLING);
  }
}

void rpm_fan(){
  counter_rpm++;
}



Could anybody tell me why my RPM isn't calculated correctly?
At 80 I got 3700
At 10 I got sometimes negative values

I'm using an Arduino Lenonardo
Is it better to use an mini pro?

Best Regards forthings