香橙派5智能温控风扇开发实战:从PWM基础到闭环控制
香橙派5作为一款高性能单板计算机,在持续高负载运行时会产生大量热量。传统散热方案要么噪音过大,要么效率低下。本文将带你深入PWM风扇控制技术,实现从10%到100%占空比的精确调速,并最终构建一个完整的温度联动智能控制系统。
1. 硬件准备与PWM基础配置
在开始编写脚本前,我们需要确保硬件连接正确并完成基础PWM配置。香橙派5的26针GPIO接口中,有多个引脚支持PWM输出功能,但默认处于关闭状态。
首先确认你的风扇类型:
- 4线PWM风扇 :直接使用PWM信号线控制转速
- 2线电压调速风扇 :需要通过MOSFET或三极管进行信号转换
对于最常见的4线PWM风扇,接线方式如下:
香橙派5 GPIO引脚 → 风扇对应接口
PWM输出引脚 → 风扇PWM线(通常为蓝色)
5V → 风扇红色电源线
GND → 风扇黑色地线
关键硬件检查清单 :
- 确认风扇工作电压(常见5V/12V)
- 检查PWM频率兼容性(通常20-25kHz为最佳)
- 确保接线牢固,避免接触不良
接下来启用PWM功能。编辑/boot/orangepiEnv.txt文件:
sudo nano /boot/orangepiEnv.txt
在overlays行添加对应的PWM配置,例如使用pwm13-m2:
overlays=pwm13-m2
保存后重启系统使配置生效。
验证PWM通道是否成功启用:
ls /sys/class/pwm/
应该能看到新增的pwmchipX目录(X为数字编号)。
2. PWM手动调速脚本开发
我们先实现基础的手动调速功能,为后续的温控系统打下基础。以下是一个健壮的Bash脚本,支持10-100%的占空比调节:
#!/bin/bash
# pwm_fan_control.sh
PWM_CHIP="pwmchip2" # 根据实际pwmchip编号修改
PWM_NUM="0" # PWM通道号
PWM_PATH="/sys/class/pwm/${PWM_CHIP}/${PWM_NUM}"
# 初始化PWM通道
init_pwm() {
if [ ! -d "$PWM_PATH" ]; then
echo "Exporting PWM channel..."
echo $PWM_NUM > /sys/class/pwm/${PWM_CHIP}/export
sleep 1 # 等待设备初始化
echo "normal" > ${PWM_PATH}/polarity
fi
# 设置PWM频率为25kHz(周期40000ns)
echo 40000 > ${PWM_PATH}/period
}
# 设置风扇转速(百分比)
set_fan_speed() {
local speed=$1
# 输入验证
if [[ ! $speed =~ ^[0-9]+$ ]] || [ $speed -lt 10 ] || [ $speed -gt 100 ]; then
echo "Error: Speed must be between 10 and 100"
return 1
fi
# 计算实际占空比时间(单位:ns)
local duty_cycle=$((40000 * speed / 100))
echo $duty_cycle > ${PWM_PATH}/duty_cycle
echo 1 > ${PWM_PATH}/enable
echo "Fan speed set to ${speed}% (Duty cycle: ${duty_cycle}ns)"
}
# 主程序
init_pwm
if [ $# -eq 1 ]; then
set_fan_speed $1
else
echo "Usage: $0 <speed_percentage (10-100)>"
fi
脚本功能说明 :
- 自动检测并初始化PWM通道
- 设置25kHz的PWM频率(人耳不可闻范围)
- 提供输入参数验证,防止错误设置
- 支持10-100%的精确调速(低于10%可能导致风扇停转)
使用示例:
# 设置风扇转速为50%
sudo ./pwm_fan_control.sh 50
# 设置风扇全速运转
sudo ./pwm_fan_control.sh 100
3. 温度监测与动态调速
实现温度联动的关键在于实时获取CPU温度并据此调整PWM占空比。香橙派5的温度传感器数据通常位于:
/sys/class/thermal/thermal_zone0/temp
以下是温度读取函数(返回摄氏度):
get_cpu_temp() {
local temp_raw=$(cat /sys/class/thermal/thermal_zone0/temp)
echo $((temp_raw / 1000))
}
我们需要定义温度-转速映射关系。推荐使用分段线性控制策略:
| 温度范围(℃) | 风扇转速(%) | 说明 |
|---|---|---|
| <50 | 10 | 静音模式 |
| 50-60 | 30 | 轻度散热 |
| 60-70 | 50 | 平衡模式 |
| 70-80 | 70 | 加强散热 |
| >80 | 100 | 全速降温 |
将手动调速脚本升级为自动温控版本:
#!/bin/bash
# auto_fan_control.sh
# 配置参数
PWM_CHIP="pwmchip2"
PWM_NUM="0"
INTERVAL=5 # 温度检测间隔(秒)
MIN_SPEED=10 # 最低转速(%)
# 初始化PWM
init_pwm() {
# ...同前文init_pwm函数...
}
# 获取CPU温度
get_cpu_temp() {
# ...同前文get_cpu_temp函数...
}
# 根据温度计算目标转速
calculate_speed() {
local temp=$1
local speed
if [ $temp -lt 50 ]; then
speed=$MIN_SPEED
elif [ $temp -lt 60 ]; then
speed=30
elif [ $temp -lt 70 ]; then
speed=50
elif [ $temp -lt 80 ]; then
speed=70
else
speed=100
fi
echo $speed
}
# 主控制循环
main_loop() {
while true; do
local temp=$(get_cpu_temp)
local target_speed=$(calculate_speed $temp)
set_fan_speed $target_speed
echo "$(date '+%Y-%m-%d %H:%M:%S') - CPU: ${temp}℃, Fan: ${target_speed}%"
sleep $INTERVAL
done
}
# 启动程序
init_pwm
main_loop
优化建议 :
- 添加温度变化率检测,提前响应温度快速上升
- 实现转速变化平滑过渡,避免频繁调速
- 添加异常温度报警功能
4. 系统服务化与高级配置
为了让温控脚本在系统启动时自动运行,我们需要将其设置为systemd服务。
创建服务配置文件:
sudo nano /etc/systemd/system/fan-control.service
添加以下内容:
[Unit]
Description=Orange Pi 5 PWM Fan Control Service
After=multi-user.target
[Service]
Type=simple
ExecStart=/usr/local/bin/auto_fan_control.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
部署步骤:
# 将脚本复制到系统目录
sudo cp auto_fan_control.sh /usr/local/bin/
sudo chmod +x /usr/local/bin/auto_fan_control.sh
# 启用并启动服务
sudo systemctl daemon-reload
sudo systemctl enable fan-control.service
sudo systemctl start fan-control.service
# 检查服务状态
sudo systemctl status fan-control.service
高级配置选项 :
- 动态调参 :通过配置文件实现控制策略热更新
# /etc/fan-control.conf
INTERVAL=3
MIN_SPEED=20
TEMP_THRESHOLDS="50:30 60:50 70:70 80:100"
- 日志记录 :添加日志轮转配置
sudo nano /etc/logrotate.d/fan-control
添加内容:
/var/log/fan-control.log {
weekly
missingok
rotate 4
compress
delaycompress
notifempty
create 644 root root
}
- Web监控界面 (可选): 使用Python Flask创建简单的状态监控页面:
from flask import Flask
import subprocess
app = Flask(__name__)
@app.route('/status')
def status():
temp = subprocess.getoutput("cat /sys/class/thermal/thermal_zone0/temp")[:-3]
speed = subprocess.getoutput("cat /sys/class/pwm/pwmchip2/pwm0/duty_cycle")
return f"CPU Temp: {temp}℃, Fan Speed: {int(int(speed)/400)}%"
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
5. 故障排查与性能优化
在实际部署中可能会遇到各种问题,以下是常见问题及解决方案:
问题1:PWM通道无法导出
- 检查/boot/orangepiEnv.txt配置是否正确
- 确认没有其他进程占用PWM通道
- 尝试不同的PWM引脚组合
问题2:风扇转速不稳定
- 检查电源供应是否充足
- 尝试调整PWM频率(通常20-25kHz最佳)
- 添加滤波电容(如100nF)到PWM信号线
问题3:温度读取异常
- 验证thermal_zone路径:
ls /sys/class/thermal/
- 尝试其他thermal_zone编号
性能优化技巧 :
- 响应速度优化 :
# 减少温度检测间隔(但会增加系统负载)
INTERVAL=2
- 动态频率调整 : 根据负载情况自动调整检测频率:
get_cpu_load() {
local load=$(awk '{print $1}' /proc/loadavg)
echo ${load%.*}
}
# 在主循环中
local load=$(get_cpu_load)
local dynamic_interval=$((INTERVAL + load/10))
sleep $dynamic_interval
- 温度预测算法 : 实现简单的移动平均滤波:
# 初始化温度历史数组
declare -a temp_history=()
# 在计算目标转速前
temp_history+=($temp)
if [ ${#temp_history[@]} -gt 5 ]; then
temp_history=("${temp_history[@]:1}") # 移除最旧的值
fi
# 计算平均温度
local sum=0
for t in "${temp_history[@]}"; do
sum=$((sum + t))
done
local avg_temp=$((sum / ${#temp_history[@]}))
390

被折叠的 条评论
为什么被折叠?



