Go Down

Topic: Controlling PC cooling fan that has a PWM input (Read 15848 times) previous topic - next topic

Daka101

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

dc42

#1
Jun 10, 2012, 06:50 pm Last Edit: Jun 10, 2012, 06:51 pm by dc42 Reason: 1
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. 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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Daka101

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

dc42

#3
Jun 10, 2012, 09:23 pm Last Edit: Jun 10, 2012, 09:26 pm by dc42 Reason: 1
See http://arduino.cc/en/Tutorial/SecretsOfArduinoPWM and 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;
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Daka101

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.

dc42

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
}
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Daka101

DC,
Thank you very much, I will set up eveything tonight and give it a try... Will report back Ya or Na.
Thanks
Daka

dc42

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".
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Daka101

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.

dc42

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.
Formal verification of safety-critical software, software development, and electronic design and prototyping. See http://www.eschertech.com. Please do not ask for unpaid help via PM, use the forum.

Daka101

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

forthings

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

Go Up