基于 PWM 的 Arduino 风扇控制器





5.00/5 (17投票s)
使用此库控制 3 或 4 针 PWM 风扇。
引言
我需要一些代码来控制一个 12 伏风扇,该风扇有一个 5 伏 PWM 输入和一个 5 伏测速输出。代码需要考虑环境条件和硬件差异,这些因素会影响风扇的响应曲线,即 PWM 占空比与 RPM 响应的关系。这是为了尽力实现目标 RPM。
理解这个项目
主要介绍具有测速功能的 4 针风扇。你可以购买具有此功能的广泛使用的 PC 机箱风扇,我也是用它们来测试此代码的 - 特别是 Noctua NF-A14。
硬件设备的表现不一定相同,有时设备特性会随时间而变化,此外还有环境因素造成的差异。如果你挡住气流,你会发现即使信号不变,风扇的 RPM 也会发生变化。
因此,为此,我们需要一些自适应的东西来达到目标 RPM。它还需要相当平滑,避免在无法精确达到目标值时在最近的较低值和最近的较高值之间循环的抖动。
代码的基本工作原理是持续采样 RPM,然后向上或向下调整占空比,直到达到目标 RPM,或者足够接近以至于再靠近会导致抖动。
请注意,此代码仍然可能遇到导致抖动的情况,尤其是在将 RPM 设置远低于最低工作 RPM 时。更准确地说,它可以大大减少抖动,在大多数情况下可以消除它。
它还需要对不同风扇进行一些调优。我已经为算法提供了一些默认值,这些值基本上应该可以工作。我们使用一种称为 PID 的方法来精确锁定目标 RPM。
我没有足够的数学背景来理解它,但我会提供一些我找到的材料 在这里,供那些可能有兴趣的人参考。
风扇的测速器每转报告一个刻度数 - 通常是两个。这意味着每转两次,它都会驱动测速线高电平。
理论上有两种方法可以从中读取 RPM。首选的方法是计算刻度之间的持续时间,然后从中推断。
另一种方法是计算在给定周期内实际经过的刻度数。
更新: 最初我使用后一种方法计算 RPM,因为前者方法遇到了问题。解决这个问题后,RPM 读数现在几乎是即时的,PID 不再需要等待重新计算。
连接这个混乱的线路
你需要一个 12 伏电源,能够安全地提供约 0.2 安培的电流。
要运行此项目,你需要一个 ESP32,但如果你修改代码,也可以使用 Teensy 或其他设备。
你需要一个电平转换器,因为风扇的 PWM 和测速工作在 5 伏,而 ESP32 工作在 3.3 伏。
这是关于 4 针风扇接线 的一些信息。请非常小心地根据引脚定义正确放置插头。
在通电之前,请完成所有接线并*仔细检查*。否则可能会损坏您的设备。
你将 PWM 和测速引脚连接到电平转换器的高侧。
将转换器的 5v VIN 连接到 ESP32 开发板的 5v。
将转换器的 3.3v VIN 连接到 ESP32 的 3.3v 输出。
将所有地线连接在一起。这意味着 ESP32 的地线、风扇的地线和电平转换器的地线。
将 ESP32 的 GPIO 22 连接到风扇的测速低电平转换侧。
将 ESP32 的 GPIO 23 连接到风扇的 PWM 低电平转换侧。
将风扇电源连接到您的 12 伏电源
将 ESP32 插入您的 PC。
编写这堆乱七八糟的代码
使用代码通常比创建它更简单。首先,在您的 Platform IO INI 文件中添加一个条目,或者下载代码并将其包含在项目的库中(适用于 Arduino IDE 等)。
lib_deps = codewitch-honey-crisis/htcw_fan_controller ; PIO ini entry for lib
接下来,在您的项目中 `include` 该文件,并可选地导入 `arduino` 命名空间
#include <fan_controller.hpp>
using namespace arduino;
现在您应该声明风扇。这取决于平台
对于 ESP32
有两个构造函数。其中一个用于带测速的风扇,另一个用于不带测速的风扇。
// four pin fan:
fan_controller fan(pwm_set,nullptr,TACH_PIN,MAX_RPM);
// three pin fan:
fan_controller fan(pwm_set,nullptr, MAX_RPM);
以上假设 `void pwm_set(uint8_t duty, void*)` 已声明并且将为您设置占空比。
使用内置的 8 位分辨率 PWM 通道,如下所示
static void pwm_set(uint16_t duty, void* state) {
// input is 16-bit
// write a 8-bit duty
ledcWrite(0,duty>>8);
}
对于其他平台
`fan_controller<>` 实际上是一个模板,您将测速引脚或 -1 以及每转刻度数(如果适用)作为模板参数传递。由于在核心 Arduino 框架下无法传递任何参数来处理中断的复杂原因,因此需要使用模板。
// four or three pin fan:
using fan_ctrl = fan_controller<TACH_PIN>;
static fan_ctrl fan(pwm_set,nullptr,MAX_RPM);
以上假设 `void pwm_set(uint8_t duty, void*)` 已声明并且将为您设置占空比。某些 MCU 支持本机 PWM 信号生成,而其他 MCU 需要您使用第三方设备来生成此类信号。在上面的 `pwm_set` 中,您将根据需要执行任何操作,以使您的硬件为您生成信号。
`duty` 参数是一个 0-65535 的值,表示 PWM 的占空比,以 16 位值空间表示,其中 0 表示无占空比,65535 表示 100% 占空比。
`state` 参数是可选的,是一个用户定义的值,如果构造函数中指定,可以在调用时传递。
在 `setup()` 中,您必须在开始使用风扇控制器之前调用 `initialize()`。在 `loop()` 中,您将需要调用 `update()`。
您只需使用 `rpm()` get/set 访问器来检索和设置目标 RPM。如果尚未提供读数 - 或永远无法提供 - 它将返回 `NAN`。
您可以使用 `pwm_duty()` 访问器以 16 位值空间设置或检索 PWM 占空比。这样做会放弃任何自适应 RPM 定向,而只是将设备设置为指定的占空比。
在初始化之前,可以使用 `find_min_rpm()` 和 `find_max_rpm()` 来检测最小和最大 RPM。如果您将 `NAN` 作为构造函数的 `max_rpm` 参数传递,则会在 `initialize()` 时检测最大 RPM。当然,这仅适用于 4 针构造函数。请注意,最小 RPM 是自适应定向的最小有效 RPM。通过手动设置 `pwm_duty()`,通常可以达到更低的 RPM。
祝您编码愉快!
历史
- 2023 年 1 月 1 日 - 初次提交
- 2023 年 1 月 3 日 - 更新以使 RPM 成为浮点数,并用更实时的机制取代基于周期的更新。
- 2023 年 1 月 4 日 - 添加了检测风扇最小/最大 RPM 的方法,更新了演示。
- 2023 年 1 月 6 日 - 提高了 RPM 和 PWM 的精度
- 2023 年 1 月 6 日 - 增加了跨平台兼容性