PC Fans: Difference between revisions

No edit summary
 
(21 intermediate revisions by the same user not shown)
Line 6: Line 6:
* Noctua's [https://noctua.at/pub/media/wysiwyg/Noctua_PWM_specifications_white_paper.pdf PWM Specification White Paper] (unknown date, but it references 1.3 <i>ibid</i>)
* Noctua's [https://noctua.at/pub/media/wysiwyg/Noctua_PWM_specifications_white_paper.pdf PWM Specification White Paper] (unknown date, but it references 1.3 <i>ibid</i>)
Almost all available fans are either 5V, 12V, or 24V, with 12V dominating personal computers:
Almost all available fans are either 5V, 12V, or 24V, with 12V dominating personal computers:
* there is no readily-available 24V source from an [[ATX]] power supply, but you'll see 24V in e.g. [[3D printers]]
* there is no readily available 24V source from an [[ATX]] power supply, but you'll see 24V in e.g. [[3D printers]]
* 5V fans are seen in conjunction with MCUs such as [[Raspberry Pi]]
* 5V fans are seen in conjunction with MCUs such as [[Raspberry Pi]]
Fans operating at higher voltages will require less current for the same work. Two-pin fans offer neither feedback nor dedicated PWM control. Three-pin fans typically offer a tachometer signal on the third wire. Four-pin fans add a PWM control on the fourth wire. PWM is a <b>5V logical control</b> that does not modify supplied power. In the absence of PWM support, some control can be effected by varying voltage (voltage ought be held constant for a PWM fan), but since a minimum voltage is necessary to avoid stalling, only the higher-speed range is available. "Low-frequency PWM" applies PWM to the power wire itself, but suffers from audible commutation noise, and must apply pulse stretching to the tachometer.
Fans operating at higher voltages will require less current for the same work. Two-pin fans offer neither feedback nor dedicated PWM control. Three-pin fans typically offer a tachometer signal on the third wire. Four-pin fans add a PWM control on the fourth wire. PWM is a <b>5V logical control</b> that does not modify supplied power. In the absence of PWM support, some control can be effected by varying voltage (voltage ought be held constant for a PWM fan), but since a minimum voltage is necessary to avoid stalling, only the higher-speed range is available. "Low-frequency PWM" applies PWM to the power wire itself, but suffers from audible commutation noise, and must apply pulse stretching to the tachometer.
Line 31: Line 31:
Pulse width modulation controls the power delivered by dividing time up into quanta, and supplying current only during part of each quantum. The signal is characterized by the quantum length (∝ 1/frequency); the level can then be varied from none to the entirety of the quantum. 100% PWM is equivalent to constant current supply, while 0% PWM is equivalent to no supply.
Pulse width modulation controls the power delivered by dividing time up into quanta, and supplying current only during part of each quantum. The signal is characterized by the quantum length (∝ 1/frequency); the level can then be varied from none to the entirety of the quantum. 100% PWM is equivalent to constant current supply, while 0% PWM is equivalent to no supply.


PC fans are ideally controlled via 25KHz PWM, though the range 21KHz to 28KHz is deemed acceptable (why 21KHz? so that you can't hear it (more precisely, so that you can't hear the commutation noise from switching the drive coils on and off): children hear up to about 20KHz, adults 17). The PWM high signal ought be 5V with an absolute maximum of 5.25V. The absolute max current drawn is 5mA. The maximum value of logic low is 0.8V. In the absence of a valid signal, the fan ought run at maximum speed. Intel's document puts fairly strict requirements on the PWM-RPM relationship: the fan speed shall be a "continuous and monotonic function of the duty cycle" (a reasonable if almost redundant requirement, see below), and furthermore that the effected speed, as a percentage of maximum speed, should match the PWM duty cycle to within ±10%. If the PWM duty cycle is e.g. 50%, and the fan's max speed is 1900 RPM, conformance requires fan speed of 760–1140 RPM. The response curve must thus be linear in the large.
PC fans are ideally controlled via 25KHz PWM, though the range 21KHz to 28KHz is deemed acceptable (why 21KHz? so that you can't hear the commutation noise from switching the drive coils on and off: children hear up to about 20KHz, adults 17). The PWM high signal ought be 5V with an absolute maximum of 5.25V. The absolute max current drawn is 5mA (as an example, the ESP32 specs 40mA on each GPIO pin). The maximum value of logic low is 0.8V. In the absence of a valid signal, the fan ought run at maximum speed. Intel's document puts fairly strict requirements on the PWM-RPM relationship: the fan speed shall be a "continuous and monotonic function of the duty cycle" (a reasonable if almost redundant requirement, see below), and furthermore that the effected speed, as a percentage of maximum speed, should match the PWM duty cycle to within ±10%. If the PWM duty cycle is e.g. 50%, and the fan's max speed is 1900 RPM, conformance requires fan speed of 760–1140 RPM. The response curve must thus be linear in the large.


Fans have a minimum rotation speed, corresponding to some minimum PWM duty cycle; duty cycles below this minimum result in the the fan running at less than or equal to the minimum speed (perhaps not running at all). The fan will never *start* spinning at less than the minimum rotation speed. The minimum rotation speed must not be more than 30% of the maximum speed.
Fans have a minimum rotation speed, corresponding to some minimum PWM duty cycle; duty cycles below this minimum result in the the fan running at less than or equal to the minimum speed (perhaps not running at all). The fan will never *start* spinning at less than the minimum rotation speed. The minimum rotation speed must not be more than 30% of the maximum speed.
Line 44: Line 44:
Note that 25KHz is beyond the capability of many general-purpose PWM ICs (e.g. TLC5940, PCA9685), though I list several suitable special-purpose ICs [[#Accessories|below]].
Note that 25KHz is beyond the capability of many general-purpose PWM ICs (e.g. TLC5940, PCA9685), though I list several suitable special-purpose ICs [[#Accessories|below]].


====On Arduino====
====On ATmega-based Arduino====
Generating a 25KHz PWM signal on [[Arduino]] Unos and MEGAs is not possible via the standard libraries, and the hardware must be programmed directly. Here's a sample implementation for the MEGA, providing eight-bit resolution (PWM values [0..255]), and using timer 5 for a PWM signal:
Generating a 25KHz PWM signal on [[Arduino]] Unos and MEGAs is not possible via the standard libraries, and the hardware must be programmed directly. Here's a sample implementation for the MEGA, providing eight-bit resolution (PWM values [0..255]), and using timer 5 for a PWM signal:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
Line 64: Line 64:
</syntaxhighlight>
</syntaxhighlight>
To use a different timer, replace all '5's above with the desired timer number. The relevant pin ought be placed into <tt>OUTPUT</tt> mode, and [https://www.mathworks.com/help/supportpkg/arduino/ug/pin-mapping-for-arduino-timer-dependent-blocks.html must be associated] with the configured timer (say, Pin 44 for Timer 5). No direct manipulation of the pins is necessary, and the PWM is handled in hardware.
To use a different timer, replace all '5's above with the desired timer number. The relevant pin ought be placed into <tt>OUTPUT</tt> mode, and [https://www.mathworks.com/help/supportpkg/arduino/ug/pin-mapping-for-arduino-timer-dependent-blocks.html must be associated] with the configured timer (say, Pin 44 for Timer 5). No direct manipulation of the pins is necessary, and the PWM is handled in hardware.
====On RA4M1-based Arduino====
Things are simpler on the Renesas RA4M1 driving the UNO R4. Use <tt>pwm.h</tt>'s <tt>PwmOut</tt> class on one of the numerous pins capable of hardware PWM, initializing it to 25Khz:
<syntaxhighlight lang="c">
static const int PWM_PIN = D3;  // we'll use D3
PwmOut pwmd3(PWM_PIN);
pinMode(PWM_PIN, OUTPUT);
pwmd3.begin(25000.0f, 0.0f); // start with a PWM of 0
pwmd3.pulse_perc(pwm);
</syntaxhighlight>
====On ESP32====
====On ESP32====
The [[ESP32]]'s [https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html <tt>ledc</tt>] library can be harnessed to do this at a high level, while still using hardware PWM. You might need step up the voltage to 5V via one mechanism or another (failure to do can, in my experience, result in devices running at lower speeds than expected). A level shifter will do nicely. Supply a chosen channel (of 8) and an <tt>OUTPUT</tt> pin, together with a <tt>freq</tt> of 25000, to e.g.:
The [[ESP32]]'s [https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html <tt>ledc</tt>] library can be harnessed to do this at a high level, while still using hardware PWM. You might need step up the voltage to 5V via one mechanism or another (failure to do can, in my experience, result in devices running at lower speeds than expected). A level shifter will do nicely. Supply a chosen channel (of 8) and an <tt>OUTPUT</tt> pin, together with a <tt>freq</tt> of 25000, to e.g.:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
static int initialize_pwm(ledc_channel_t channel, int pin, int freq){                                                                     
static const ledc_mode_t LEDCMODE = LEDC_LOW_SPEED_MODE; // no high-speed on ESP32-S3
  ledc_channel_config_t conf;                                                                                                     
 
  memset(&conf, 0, sizeof(conf));                                                                                                 
static int initialize_pwm(ledc_channel_t channel, int pin, int freq, ledc_timer_t timer){
  conf.gpio_num = pin;                                                                                                             
  conf.speed_mode = LEDC_HIGH_SPEED_MODE;                                                                                         
  conf.intr_type = LEDC_INTR_DISABLE;                                                                                             
  conf.timer_sel = LEDC_TIMER_1;
  conf.duty = LEDC_TIMER_8_BIT;
  conf.channel = channel;
  if(ledc_channel_config(&conf) != ESP_OK){
    return -1;
  }
   ledc_timer_config_t ledc_timer;
   ledc_timer_config_t ledc_timer;
   memset(&ledc_timer, 0, sizeof(ledc_timer));
   memset(&ledc_timer, 0, sizeof(ledc_timer));
   ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;
   ledc_timer.speed_mode = LEDCMODE;
   ledc_timer.bit_num = LEDC_TIMER_8_BIT;
   ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; // used to be "bit_num"
   ledc_timer.timer_num = LEDC_TIMER_1;
   ledc_timer.timer_num = timer;
   ledc_timer.freq_hz = freq;
   ledc_timer.freq_hz = freq;
   if(ledc_timer_config(&ledc_timer) != ESP_OK){
   if(ledc_timer_config(&ledc_timer) != ESP_OK){
    return -1;
  }
  ledc_channel_config_t conf;
  memset(&conf, 0, sizeof(conf));
  conf.gpio_num = pin;
  conf.speed_mode = LEDCMODE;
  conf.intr_type = LEDC_INTR_DISABLE;
  conf.timer_sel = timer;
  conf.duty = LEDC_TIMER_8_BIT;
  conf.channel = channel;
  if(ledc_channel_config(&conf) != ESP_OK){
     return -1;
     return -1;
   }
   }
Line 96: Line 108:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
static int set_pwm(ledc_channel_t channel, unsigned p){                                                                                                     
static int set_pwm(ledc_channel_t channel, unsigned p){                                                                                                     
   if(ledc_set_duty(LEDC_HIGH_SPEED_MODE, channel, p) != ESP_OK){                                                                 
   if(ledc_set_duty(LEDCMODE, channel, p) != ESP_OK){                                                                 
     return -1;                                                                                                                       
     return -1;                                                                                                                       
   }
   }
   if(ledc_update_duty(LEDC_HIGH_SPEED_MODE, channel) != ESP_OK){                                                           
   if(ledc_update_duty(LEDCMODE, channel) != ESP_OK){                                                           
     return -1;                                                                                                                       
     return -1;                                                                                                                       
   }                                                                                                                                 
   }                                                                                                                                 
Line 107: Line 119:


====On Linux====
====On Linux====
PWM fans are controlled through the [https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface <tt>hwmon</tt>] [[sysfs]] interface, using the "PWM" type (''not'' the "fan" type). This is dependent on your hardware controller (usually some SMBus-accessed [https://en.wikipedia.org/wiki/Super_I/O SuperIO] shadynasty on the motherboard) being supported and detected. You'll need set <tt>pwmX_enable</tt> to 1 for manual control, at which point you ought be able to write a value [0..255] to <tt>pwmX</tt>. When the PWM is under automatic control, you ought be able to read the current level by reading this latter file.
PWM fans are controlled through the [https://www.kernel.org/doc/Documentation/hwmon/sysfs-interface <tt>hwmon</tt>] [[sysfs]] interface, using the "PWM" type (''not'' the "fan" type) via <tt>/sys/class/hwmon/*/pwm*</tt>. This is dependent on your hardware controller (usually some SMBus-accessed [https://en.wikipedia.org/wiki/Super_I/O SuperIO] shadynasty on the motherboard) being supported and detected. You'll need set <tt>pwmX_enable</tt> to 1 for manual control, at which point you ought be able to write a value [0..255] to <tt>pwmX</tt>. When the PWM is under automatic control, you ought be able to read the current level by reading this latter file.


Some devices don't have a kernel driver, but are supported by userspace tools like [https://github.com/liquidctl/liquidctl liquidctl].
Some devices don't have a kernel driver, but are supported by userspace tools like [https://github.com/liquidctl/liquidctl liquidctl].


==Tachometer==
==Tachometer==
Two pulses (momentarily closed circuit) per revolution on an [https://en.wikipedia.org/wiki/Open_collector open collector]. Maximum current is 5mA for 5V and 12V fans, and 2mA for 24V fans. V<sub>CC</sub> ought be 13V for 12V fans (so claims Noctua. Other places, and my experience, suggest that 5V is sufficient), and 6V for 5V and 24V fans, necessitating at least the following pullup resistors:
Two pulses (momentarily closed circuit) per revolution on an [https://en.wikipedia.org/wiki/Open_collector open collector]. Maximum current is 5mA for 5V and 12V fans, and 2mA for 24V fans. V<sub>CC</sub> ought be 13V for 12V fans (so claims Noctua. Other places, and my experience, suggest that 5V and even 3.3V are sufficient), and 6V for 5V and 24V fans, necessitating at least the following pullup resistors:
* 5V: 1.2KΩ (6V, 5mA)
* 5V: 1.2KΩ (6V, 5mA)
* 12V: 2.6KΩ (13V, 5mA) (or 1KΩ assuming 5V, see above)
* 12V: 2.6KΩ (13V, 5mA)
** or, fed 5V: 1KΩ (5mA)
** or, fed 3.3V: 680Ω (4.9mA)
* 24V: 3KΩ (6V, 2mA)
* 24V: 3KΩ (6V, 2mA)
Arduino 5V internal pullup resistors are several tens of thousands of ohms, so they can be used to simplify the circuit (on a 3.3V MCU, you'll need hook up the tachometers to at least a 5V V<sub>CC</sub>, so this won't help you there). You'll then want an RC circuit going to ground, something like 0.1nF and 3.3kΩ for a 12V fan. The tach will pull the signal low. It's best to detect this via hardware interrupts:
Arduino 5V internal pullup resistors are several tens of thousands of ohms, so they can be used to simplify the circuit (on a 3.3V MCU, you'll need hook up the tachometers to at least a 5V V<sub>CC</sub>, so this won't help you there). The tach will pull the signal low. It's best to detect this via hardware interrupts:
<syntaxhighlight lang="c">
<syntaxhighlight lang="c">
static volatile unsigned Pulses;
static _Atomic(uint32_t) LowerFanPulses, UpperFanPulses;
 
static void                                                                                                                       
pulse_isr(void* pulsecount){                                                                                                       
  _Atomic(uint32_t)* pc = pulsecount;                                                                                                       
  ++*pc;                                                                                                                           
}   


static void IRAM_ATTR rpm(void){
static int
   if(Pulses < UINT_MAX){ // saturate
setup_intr(gpio_num_t pin, uint32_t* arg){
     ++Pulses;
  if(gpio_set_input(pin)){
    return -1;
  }
  esp_err_t e = gpio_set_intr_type(pin, GPIO_INTR_NEGEDGE);
   if(e != ESP_OK){
     fprintf(stderr, "failure (%s) installing %d interrupt\n", esp_err_to_name(e), pin);
    return -1;
  }
  if((e = gpio_isr_handler_add(pin, pulse_isr, arg)) != ESP_OK){
    fprintf(stderr, "failure (%s) setting %d isr\n", esp_err_to_name(e), pin);
    return -1;
  }
  if((e = gpio_intr_enable(pin)) != ESP_OK){                                                                                       
    fprintf(stderr, "failure (%s) enabling %d interrupt\n", esp_err_to_name(e), pin);
    return -1;
   }
   }
  return 0;
}
}


static void setup_interrupt(int pin){
...
  pinMode(pin, INPUT_PULLUP);
if(setup_intr(lowertpin, &LowerFanPulses)){
  digitalWrite(pin, HIGH);
   return -1;
   attachInterrupt(digitalPinToInterrupt(pin), rpm, FALLING);
}
}
if(setup_intr(uppertpin, &UpperFanPulses)){
  return -1;
}
...


...
uint32_t up = UpperFanPulses;
unsigned p;
UpperFanPulses = 0;
noInterrupts();
uint32_t lp = LowerFanPulses;
p = Pulses;
LowerFanPulses = 0;
Pulses = 0;
interrupts();
...
...
</syntaxhighlight>
</syntaxhighlight>
<tt>p</tt> can then be used to calculate RPM. Remember to divide by 2 due to two pulses per revolution. The pin must be associated with some hardware interrupt.
<tt>up</tt> and <tt>lp</tt> can then be used to calculate RPM. Remember to divide by 2 due to two pulses per revolution. The pin must be associated with some hardware interrupt.
 
If there's noise contaminating the tachometer circuit, you might need to establish an RC circuit going to ground, yielding a [https://en.wikipedia.org/wiki/Low-pass_filter#RC_filter low-pass filter]. Given a time constant RC, a low-pass filter blocks signals over 1/2πRC. Pick something a fair amount above your expected frequency. Let's say your fan maxes out at 3000 RPM. That's 3000 / 60 = 50 RPS. The Hall sensor triggers twice per revolution so you're expecting 100 transitions per second. If you're using a 680Ω resistor on input, and want to pass signals below 500 Hz, a 470 nF capacitor will do the trick: 1/2πRC == 1/2π(.0003196) == 497.98 Hz. Note that this will make your transitions less sharp on the rising side, and only serves to reduce the voltage drastically when the frequency increases substantially. It's best to avoid their use.


A junction must propagate only one tachometer signal, i.e. if three fans are connected to a splitter, only one tach value is reported. Whether this is a maximum, or an average, or something else is undefined, but every junction I've ever seen, from passive splitters to active controllers, simply connects only one tachometer to the wire (controllers using a side channel in addition to the fan connector might of course report multiple tach signals, as does the [https://shop.aquacomputer.de/product_info.php?products_id=3832&language=en Aquacomputer OCTO] via USB).
A junction must propagate only one tachometer signal, i.e. if three fans are connected to a splitter, only one tach value is reported. Whether this is a maximum, or an average, or something else is undefined, but every junction I've ever seen, from passive splitters to active controllers, simply connects only one tachometer to the wire (controllers using a side channel in addition to the fan connector might of course report multiple tach signals, as does the [https://shop.aquacomputer.de/product_info.php?products_id=3832&language=en Aquacomputer OCTO] via USB).
Line 171: Line 209:


===Connector and wiring===
===Connector and wiring===
[[File:0470533000.jpeg|right|thumb|A Molex 47053-1000 4-pin header]]
Intel specifies UL1430 wire with a minimum AWG26, 300V capacity, with rating of at least 105℃.
Intel specifies UL1430 wire with a minimum AWG26, 300V capacity, with rating of at least 105℃.


Line 211: Line 250:
* Devices, often in 5.25" bay form factor, with knobs for manual control of connected fans. [[Counterforce|Manual control is lame]] so fuck that.
* Devices, often in 5.25" bay form factor, with knobs for manual control of connected fans. [[Counterforce|Manual control is lame]] so fuck that.
* Specialized ICs exist to drive fan PWM and read tach:
* Specialized ICs exist to drive fan PWM and read tach:
** Analog Devices [https://www.digikey.com/en/products/detail/analog-devices-inc/ADT7470ARQZ-REEL7/995575 ADT7470ARQZ-REEL7]
** Analog Devices [https://www.digikey.com/en/products/detail/analog-devices-inc/ADT7470ARQZ-REEL7/995575 ADT7470ARQZ-REEL7] (4 fans, SMT, [[SMBus]], temp sensor)
** Maxim Integrated [https://www.maximintegrated.com/en/products/interface/signal-integrity/MAX31790.html MAX31790]
** OnSemi [https://www.onsemi.com/pdf/datasheet/adm1031-d.pdf ADM-1301] (2 fans, SMT, [[SMBus]])
** Texas Instruments [https://www.ti.com/product/AMC6821 AMC6821]
** OnSemi [https://www.onsemi.com/download/data-sheet/pdf/adt7460-d.pdf ADT7460] (2 fans, SMT, [[SMBus]])
** Maxim Integrated [https://www.maximintegrated.com/en/products/interface/signal-integrity/MAX31790.html MAX31790] (6 fans, SMT, I<sup>2</sup>C)
** Texas Instruments [https://www.ti.com/product/AMC6821 AMC6821] (1 fan, SMT, [[SMBus]], temp sensor)
** Microchip [https://ww1.microchip.com/downloads/aemDocuments/documents/MSLD/ProductDocuments/DataSheets/EMC2301-2-3-5-Data-Sheet-DS20006532A.pdf EMC230x] (1, 2, 3, or 5 fans depending on model, SMT, [[SMBus]])
===LEDs===
===LEDs===
Fans are well-represented in modern workstation lightshows. Look for daisy-chaining capabilities in an LED-enabled fan, as there will usually be twice the total cabling. 5V can't be daisy-chained as far as 12V due to voltage drop.
Fans are well-represented in modern workstation lightshows. Look for daisy-chaining capabilities in an LED-enabled fan, as there will usually be twice the total cabling. 5V can't be daisy-chained as far as 12V due to voltage drop.