1487浏览
查看: 1487|回复: 6

4Pin 风扇转速控制器

[复制链接]
对于风扇来说,只要供电就能工作。最开始的电脑使用的都是这种风扇。但是随着技术的发展,人们发现需要对这个风扇进行控制,因为风扇转的快噪音和功耗都会随之增加。于是,加上一根反馈线,让用户能够得知当前的转速。但是这样会遇到另外的两个问题:第一个问题是风扇电压变化,反馈线上的电压也会随之变化,范围大了读取这个反馈会很麻烦。第二个问题是:电压和风扇转速转速关系并不是线性的。比如,一个 12V 的风扇,12V时转速是 2000CPM10V供电时转速时1000CPM,但是如果11V供电时,转速很可能是 1100CPM。风扇的转速和风力噪音直接相关,用户想要得到一个大概的转速非常困难。最终 Intel 推出了一个方案:通过 PWM 来控制风扇转速,然后使用5V信号作为当前转速反馈引脚。
我们最常见到的CPU的风扇通常都是 4 Pin的。供电要求12V,控制的PWM 5V 25KHz,转速反馈引脚为 5V  输出。
4Pin 风扇转速控制器图1
4Pin 风扇转速控制器图2
有些风扇是存在最低转速的,意思是哪怕有PWM已经为0仍然能够旋转,有些不存在最低转速,PWM比较低的时候就会停止转动。
4Pin 风扇转速控制器图3
了解了上述知识就可以得知我们为了实现控制 CPU 风扇,需要提供12V电源,提供25Khz PWM 信号,以及读取 5V 的转速输出。
电路设计如下,我们使用Arduino Uno作为主控。外部12V供电进入(DC1)之后,经过一个DC-DC降压模块(来自DFROBOTDFR0831DC-DC降压模块7~24V5V/4A),降压为5V提供给 Arduino 作为风扇控制,然后风扇的转速反馈信号经过U3上拉后进入Arduino即可读取。最终转速和当前的PWM设定显示在一个 1602LCD上。
4Pin 风扇转速控制器图4
PCB 设计如下,可以看到板子上带有4个按钮用于控制 PWM ,分别是-1-10+1+10这样用户可以快速的改变 PWM 设置。
4Pin 风扇转速控制器图5
代码设计如下:
  1. #include <LiquidCrystal_I2C.h>
  2. // 保存当前设定的 PWM 值
  3. #include<EEPROM.h>
  4. LiquidCrystal_I2C lcd(0x3F, 16, 2);
  5. // PWM 发生器相关定义
  6. const byte OC1A_PIN = 9; //PWM输出引脚为 D9
  7. // 定义 PWM 频率
  8. const word PWM_FREQ_HZ = 25000; //Adjust this value to adjust the frequency
  9. const word TCNT1_TOP = 16000000 / (2 * PWM_FREQ_HZ);
  10. const int BTN1 = 13;
  11. const int BTN2 = 12;
  12. const int BTN3 = 11;
  13. const int BTN4 = 10;
  14. // 计算转速相关定义
  15. const int TOTAL = 10; // 计算10次输出一次,这是一个简单的滤波
  16. int InterruptPin = 2;// 接收风扇中断 D2
  17. volatile long int counter = 0;
  18. volatile long int t[TOTAL];
  19. byte p = 0;
  20. byte SavedPWM = 0;
  21. byte lastPWM = -1;
  22. byte pwm;
  23. long int Elsp=0;
  24. void setup() {
  25.   pinMode(BTN1, INPUT_PULLUP);
  26.   pinMode(BTN2, INPUT_PULLUP);
  27.   pinMode(BTN3, INPUT_PULLUP);
  28.   pinMode(BTN4, INPUT_PULLUP);
  29.   pinMode(OC1A_PIN, OUTPUT);
  30.   // Clear Timer1 control and count registers
  31.   TCCR1A = 0;
  32.   TCCR1B = 0;
  33.   TCNT1  = 0;
  34.   // Set Timer1 configuration
  35.   // COM1A(1:0) = 0b10   (Output A clear rising/set falling)
  36.   // COM1B(1:0) = 0b00   (Output B normal operation)
  37.   // WGM(13:10) = 0b1010 (Phase correct PWM)
  38.   // ICNC1      = 0b0    (Input capture noise canceler disabled)
  39.   // ICES1      = 0b0    (Input capture edge select disabled)
  40.   // CS(12:10)  = 0b001  (Input clock select = clock/1)
  41.   TCCR1A |= (1 << COM1A1) | (1 << WGM11);
  42.   TCCR1B |= (1 << WGM13) | (1 << CS10);
  43.   ICR1 = TCNT1_TOP;
  44.   Serial.begin(115200);
  45.   Serial.setTimeout(300);
  46.   pinMode(InterruptPin, INPUT);
  47.   attachInterrupt(0, speedX, FALLING );
  48.   lcd.init();
  49.   lcd.backlight();
  50.   lcd.setCursor(0, 0);
  51.   lcd.print("PWM Fan CNT");
  52.   // PWM 存在地址0 上
  53.   SavedPWM = EEPROM.read(0);
  54.   if (SavedPWM>100) {
  55.       SavedPWM=0;
  56.     }
  57.   // 将上一次保存的 PWM 设定进去
  58.   setPwmDuty(SavedPWM);
  59.   pwm = SavedPWM;
  60.   lastPWM = pwm + 1;
  61. }
  62. void loop() {
  63.   if (pwm != lastPWM) {
  64.     Serial.println(pwm);
  65.     setPwmDuty(pwm);
  66.     lastPWM = pwm;
  67.     Elsp=millis();
  68.   }
  69.   // 计算当前转速
  70.   long int avg = 0;
  71.   counter = 0;
  72.   delay(1000);
  73.   p = (p + 1) % TOTAL;
  74.   t[p] = (long int)(counter * 60 / 2);
  75.   for (byte i = 0; i < TOTAL; i++) {
  76.     avg = avg + t[i];
  77.   }
  78.   // 输出转速
  79.   Serial.print("Current speed: ");
  80.   Serial.println(avg / TOTAL);
  81.   lcd.setCursor(0, 1);
  82.   lcd.print("Fan Speed:");
  83.   lcd.print(avg / TOTAL);
  84.   lcd.print("      ");
  85.   counter = 0;
  86.   pinMode(BTN1, INPUT_PULLUP);
  87.   if (digitalRead(BTN1) == LOW) {
  88.     delay(5);
  89.     if ((digitalRead(BTN1) == LOW) && (pwm > 9)) {
  90.       pwm = pwm - 10;
  91.     }
  92.   }
  93.   if (digitalRead(BTN2) == LOW) {
  94.     delay(5);
  95.     if ((digitalRead(BTN2) == LOW) && (pwm > 0)) {
  96.       pwm = pwm - 1;
  97.     }
  98.   }  
  99.   if (digitalRead(BTN3) == LOW) {
  100.     delay(5);
  101.     if ((digitalRead(BTN3) == LOW) && (pwm <=100-10)) {
  102.       pwm = pwm + 10;
  103.     }
  104.   }
  105.   if (digitalRead(BTN4) == LOW) {
  106.     delay(5);
  107.     if ((digitalRead(BTN4) == LOW) && (pwm <100)) {
  108.       pwm = pwm + 1;
  109.     }
  110.   }
  111.   // 每隔5秒检查 pwm 是否已经改变,如果改变则保存
  112.   if ((SavedPWM!=pwm)&&(millis()-Elsp>5000)) {
  113.       Serial.println("Save current pwm");
  114.       Elsp=millis();
  115.       SavedPWM=pwm;
  116.       EEPROM.write(0,pwm);
  117.     }
  118. }
  119. void setPwmDuty(byte duty) {
  120.   Serial.print("Set pwm "); Serial.println(duty);
  121.   lcd.setCursor(0, 0);
  122.   lcd.print("Current pwm:");
  123.   lcd.print(duty);
  124.   lcd.print("  ");
  125.   OCR1A = (word) (duty * TCNT1_TOP)/ 100;
  126. }
  127. void speedX()//中断函数
  128. {
  129.   counter++;
  130. }
复制代码


zoologist  高级技匠
 楼主|

发表于 2023-7-14 09:43:14

本帖最后由 zoologist 于 2023-7-14 14:34 编辑

工作测试的视频:



本文提到的电路图和PCB(立创 EDA)
下载附件4PinFan.zip
回复

使用道具 举报

木子呢  管理员

发表于 2023-7-14 14:31:49

666,蹲一个后续完整步骤过程~
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-7-17 21:52:46

不错不错
回复

使用道具 举报

三春牛-创客  初级技神

发表于 2023-7-17 21:56:03

赞赞赞赞
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-7-18 21:31:29

厉害厉害
回复

使用道具 举报

花生编程  中级技匠

发表于 2023-7-18 21:34:46

赞赞赞赞赞
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4

© 2013-2024 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail