2021-07-10树莓派PWM控制三极管(S8050)实现风扇调速


前言

本人不是电子相关专业和计算机相关专业人士。作为一个树莓派的业余爱好者,前段时期总算入了块心心念念4b-4GB树莓派,秉承不断学习的精神,开始各种折腾,好在树莓派作为最火热的SBC,相关前辈的文档较多,着实给我们后生提供了不小的帮助。

由于家里面有块遗留笔记本(dell N5110)的散热风扇,于是起了给树莓派加装风扇的念头。该风扇也不知是什么品牌的风扇,风扇背面标签写着功率为5V0.5A,功率着实不小。该风扇有三根引出线(其中红色为正极,黑色为负极,黄色为测速线),完全符合我的折腾要求。虽然某宝有树莓派成品T9风扇驱动模块销售,但价格小贵(19块RMB,好像还不包邮),查阅了相关资料后,采用了三极管驱动方案(最能折腾的方案),现将过程详细记录如下。

物料清单:

1、树莓派4B-4GB加铝散热外壳

2、S8050和S8550三极管各一个

3、杜邦线若干

4、电洛铁一个

5、DT830B万用表一个

6、1K电阻一个

软件环境:

Raspberry Pi OS

改装后的效果如下,图中红色虚线处是用剪刀剪断的地方,后生可参考。


一、为什么要用三极管来驱动风扇

为了解决以下需求:

需求一:实现改装风扇随树莓派CPU温度变化而相应调整风扇转速。

需求二:降低噪声。默认连接风扇正负极后为全速运转,即使树莓派关机状态风扇都仍在全速运转,特别是夜深时分,风扇噪声听着实在太大。

需求三:节能节电。实测树莓派待机功耗3.5W左右,我的风扇功率2.5W,差不多快赶上树莓派功耗了,因此减小电力开销是必要的。

为了实现上述需求,使用树莓派产生PWM脉冲方波控制风扇转速是必然的选择,而树莓派GPIO引脚输出电流实在太小,不足以驱动风扇转动。树莓派GPIO引脚输出高电平电压为3.3V,默认驱动强度(电流)8mA,最大驱动强度16mA,详见官网此处文末据说峰值电流为50mA。

因此要实现PWM控制风扇转动需要利用三极管的电流放大原理,查阅相关资料,树莓派PWM控制风扇驱动使用的三极管主要为S8050(NPN)和S8550(PNP)两种。

二、三极管电流放大原理简介

三级管电流放大原理是利用基极微小的电流变化来控制集电极大的电流变化。

分为NPN型和PNP型,两种类型三极管电流方向相反,其有三个引脚:基极(base),集电极(collector),发射极(emitter)。下图为常见的To-92封装的S8050(NPN型)引脚示意图(图片来源于网络)。

三极管工作状态有三种,以NPN型的S8050为例:

1、截止状态

基极与发射极之间电压Ube小于导通电压(硅管为0.7V),此时三极管未导通,集电极与发射极之间电压Uce约等于电源电压Vcc,此时Ic=0。

2、放大状态

基极与发射极之间电压Ube大于导通电压(硅管为0.7V)即发射结正偏,基极电压小于集电极电压Ubc<0(集电结反偏)。在放大状态时,Ic=β*Ib,Ie=Ib+Ic。β是三极管的放大倍数,S8050一般为85~300之间。

 3、饱和状态

基极与发射极之间电压Ube大于导通电压(硅管为0.7V)即发射结正偏,基极电压大于集电极电压Ubc>0(集电结正偏),集电极电压与发射极电压很小Uce<Ube。此时三极管集电极电流不再受控于基极电流。

三、驱动树莓派风扇,三极管选NPN还是PNP?

先说结论:个人认为NPN型优于PNP型,即使用S8050比S8550好。

据网上各位前辈分享的资料,树莓派驱动风扇的三极管选择主要有NPN型的S8050和PNP型S8550两种。但到底那种方案好?电路怎么接?为什么这么接?没人详细说明原由。于是又去学习了三极管的相关基础知识,对此问题有了一定见解。

1、选NPN型的S8050还是PNP型的S8550的问题

根据三极管工作原理。

当采用S8050时,由于NPN型三极管基极电压大于0.7V时导通,也就是说,当树莓派GPIO引脚低电平时(0V)三极管能可靠的截止,风扇能可靠停转。随着PWM占空比增大,基极等效电压从零开始提高时,三极管逐步工作于截止—>放大—>饱和的状态,也就是风扇转速能可靠的由PWM控制。

当采用S8550时,PNP三极管由于其工作原理是高电位截止,低电位导通。由于树莓派风扇供电通常采用5V(据说直接电源的5V),与GPIO引脚最高电平3.3V存在1.7V的压差。相当于PWM占空比最大输出的高电平3.3V"堵不住"风扇电源的5V电压,三极管不能可靠截止,导致风扇不能可靠停转。最直接的体现就是把驱动程序设置开机自启,重启树莓派时,风扇可能会莫名其妙的全力运转(亲测多次出现)。猜测原因是重启后GPIO引脚还未初始化供电为高电平时,由于风扇电源直接电源5V,集电极电压远大于基极电压三极管导通所致。当然也可以采用在集电极增加电阻等方式降压,保证风扇可靠截止。

2、三极管电路的接线研究

网上树莓派采用三极管驱动风扇的接线方式主要有下图所示三种。个人分析电路接线最优。

 

当采用S8050三极管时,接线应采用下图的电路① (三极管基极1千欧的电阻可省掉)

电路②存在的问题:由于风扇处于三极管发射极(e极)上,风扇要运转,e极电压至少要大于3v左右,导致需要施加较大的电压在基极(b极)才能使风扇运转,pWM控制范围会减小。

当采用S8550三极管时,接线应采用下图的电路③ (三极管基极1千欧的电阻可省掉)

分析:风扇在集电极(c极)上会形成压降,风扇负极与集电极之间的电压小于基极电压,即Ueb小于零,三极管理论上能可靠截止,风扇能可靠停转。

那S8550为什么不能把风扇接到集电极(c极)与地(GND)之间?

因为电源5V直接发射极(e),发射极常电压远高于基极(b)PWM控制的高电平(3.3V)电压,风扇不能停转。

3、S8050驱动风扇时伏安特性表

(1)电路①不加基极电阻

家中有一个5块钱买的DT830B万用表,为了学习三极管输入输出特性,测了下电路①不加1k的基极限流电阻Rb时,S8050驱动风扇的各项电压和电流参数,统计如下表。

S8550未作测试。

可以看出:

当pwm占空比为100,三极管基极不加限流电阻时,树莓派GPIO引脚输出电流达到了35mA。远超官方文档的GPIO引脚最大驱动电流16mA。

当pwm占空比约为80时,三极管进入了饱和状态,集电极电流突增,风扇转速控制不够线性。

风扇功率越大,风扇产生的压降越大。

(表一)S8050驱动风扇数据统计表
占空比 0 5 10 20 30 40 50 60 70 80 90 100
Ib(mA) 0.00 1.70 3.30 6.90 10.40 13.90 17.40 20.90 24.40 27.90 31.40 35.00
Ic(mA) 0.00 36.30 47.00 45.20 46.80 55.90 69.50 88.00 111.90 145.00 260.00 300.00
Vbe(V) 0.00 0.06 0.10 0.18 0.27 0.35 0.44 0.53 0.62 0.72 0.83 0.91
Vce(V) 4.02 1.91 1.95 1.91 1.82 1.59 1.35 1.08 0.79 0.53 0.28 0.14
Vbc(V) -4.02 -1.86 -1.86 -1.73 -1.56 -1.26 -0.92 -0.56 -0.18 0.19 0.54 0.78
Vab(V) 0.01 3.18 3.19 3.18 3.24 3.45 3.69 3.95 4.22 4.48 4.63 4.75
β 0 21.35 14.24 6.55 4.50 4.02 3.99 4.21 4.59 5.20 8.28 8.57
Vce+Vab 4.03 5.09 5.14 5.09 5.06 5.04 5.04 5.03 5.01 5.01 4.91 4.89
功率(w) 0.00 0.18 0.24 0.23 0.24 0.28 0.35 0.44 0.56 0.73 1.28 1.47

注:

1)三极管基极接的是树莓派12号引脚。

2)Vab是风扇正负极两端电压差。

(2)电路基极增加1k电阻时的伏安特性

在三极管基极增加1k限流电阻(实测为1015欧)Rb时,ib=(3.3-0.7)/1015=2.56mA,与实际测量2.4mA的电流基本相符,表格中空值未测量。

可以看出:

在基极增加1K限流电阻后,当PWM占空比为100时,树莓派GPIO引脚输出电流降为了2.4mA。

驱动风扇的三极管集电极电流明显减小,风扇功率变小了。

三极管在pwm占空比为90左右进入饱和状态,风扇实际功率只有标称功率的一半。

表二

占空比 0 5 10 20 30 40 50 60 70 75 80 90 100
Ib(mA) 0.208 0.419 0.644 0.863 1.2 1.4 1.6 1.75 1.9 2.1 2.4
Ic(mA) 32.7 45.3 46.9 56.1 69.5 88.1 110 120 142.5 250 230
Vbe(V) 0.73 0.82
Vce(V) 0.6 0.4
Vbc(V) -0.02 0.12 0.38
Vab(V)
β 157.21 108.11 72.83 65.01 57.92 62.93 68.75 68.57 75.00 119.05 95.83

(3)同时,我也产生了几个疑问,请各位大师指点,不胜感激!

1)表一中,为什么占空比为5时,Vbe等效电压为0.06V时三极管会导通(风扇转动)?

2)表一中,三极管放大倍数(β)整体为什么会这么低?且占空比为50时达到最低?

3)表二中,增加基极限流电阻后,为什么pwm占空比100时,三极管Ic电流会比占空比90的低?

4)增加基极限流电阻后,相比表一,表二放大倍数为什么会增加?而相应占空比对应的电流反而变化不大?

5)为什么三极管工作在放大区时,其放大倍数会变化(或者说Ib,Ic不呈线性比例关系)?,占空比增大时,β总体越变越小?在某个位置又突然增大?

四、电路接线

S8050三极管基极(b)串联1K电阻接树莓派GPIO12号引脚(硬件pwm引脚)

风扇正极接树莓派GPIO4号引脚(5V)

风扇负极接三极管集电极(c)

三极管发射极(e)接GPIO6号引脚(地GND)

最终接线如下图所示:

其中:黄色线是PWM信号线,接三极管基极;粗白色线是风扇的测速线,没有可以不接。

五、驱动代码

1、驱动库的选择--wiringpi

由于我只是业余人士,虽然以前学过一点C语言,但已基本忘记了,现在会写一点python,因此风扇驱动代码采用的python来编写。

树莓派常用操作GPIO库有RPI.GPIO,pigpio,wiringpi这三个库,三个库均支持pwm输出控制,其中只有wiringpi支持硬件PWM。三个库的详情网上颇多,此处不表。

RPI.GPIO库使用人群最广,也最通用。

Pigpio库输出PWM波形比RPI.GPIO库更稳定可靠,每次开机需要用sudo pigpio命令开启一个守护进程才能运行。

Wiringpi支持硬件PWM控制,能完美稳定的设置PWM频率至20Khz以上,使风扇运行更平衡,有效解决风扇震动及低高频噪音问题。

2、关于风扇噪音问题

在使用RPI.GPIO,pigpio库的软件pwm时,经测试在使用pwm低频率时,风扇在低转速时会发出哒哒声或间歇式的嗡嗡声,运行明显不线性,总是一顿一顿的。

在使用pwm高频率时,风扇高速运转会发出类似蜂鸣器啸叫的高频噪音。经过多次尝试后,无论如何调整pwm控制频率噪音问题均无明显改善。

最终找到了此大神的文章,总算拨云见日了。遂学习采用wiringpi的硬件PWM方式控制风扇,至此,完美解决风扇噪音问题。

详见:https://talk.quwj.com/topic/1714

3、库安装

RPI.GPIO和pigpio库在Raspberry Pi OS中已集成,无需另行安装。

树莓派python安装wiringpi库只需要以下命令即可:

sudo pip3 install wiringpi

Tips:如果不使用sudo而直接使用pip3 install wiringpi安装库wiringpi库,python会出现import找不到模块的错误:importError: No module named 'wiringpi'。

4、驱动代码

由于本人水平有限,主要参考了大神们的代码,也改得比较随性,冗余代码较多。

此代码仅适用于NPN型三极管驱动的树莓派风扇

PNP型三极管驱动的要适当修改,最终代码如下:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
#  fancontrl.py
#  
#  代码修改来源于:https://talk.quwj.com/topic/1714
#  


# 使用wiringpi库
import wiringpi as PI
# 风扇控制针脚,编号为:物理12号,BCM18号,wiringpi针脚为1号
PIN_FAN = 1
# 风扇最小转速比例
speed_base = 20
# 最小控制温度,低于它就停转
min_temp = 45
# 最大控制温度,高于它就全速运转
max_temp = 70
# 风扇启动温度
sta_temp = 55

PI.wiringPiSetup()
PI.pinMode(PIN_FAN,PI.PWM_OUTPUT)
#pwm_mode_ms为一个时钟周期内电平为连续高电平和低电平模式。
PI.pwmSetMode(PI.PWM_MODE_MS) 
#分频数值,bcm2835的默认晶振频率为19.2mhz,19.2mhz/8=2.4mhz
PI.pwmSetClock(8) 
#设置脉冲宽度为100us,即pwm时钟周期为100us,此时对应的pwm输出频率为2.4mhz/100=24khz。
PI.pwmSetRange(100) 
#初始化引脚为电平(即停转风扇)
PI.pwmWrite(PIN_FAN,0) 
PI.delay(50)




# 获取CPU温度,单位为千分之一度
def get_cpu_temp():
    temp_file = open( "/sys/class/thermal/thermal_zone0/temp" )
    cpu_temp = temp_file.read()
    temp_file.close()
    return float(cpu_temp) / 1000
# 获取CPU时钟频率,Mhz
def get_scaling_cur_freq():
    temp_file = open( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" )
    scaling_cur_freq = temp_file.read()
    temp_file.close()
    return int(float(scaling_cur_freq) / 1000)
# 获取CPU最大时钟频率,Mhz
def get_scaling_max_freq():
    temp_file = open( "/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq" )
    scaling_max_freq = temp_file.read()
    temp_file.close()
    return int(float(scaling_max_freq)/ 1000)

scaling_max_freq = get_scaling_max_freq() #调用获取最大频率函数

if __name__ == "__main__":
    speed = 0 # pwm驱动的占空比初始化
    old_speed = 0 # 设置上一次转速变量
    is_work = 0 # 风扇工作状态开关
    ''' 
    # 缓慢启动至最大,speed即是占空比
    for i in range(speed,10,100):
        PI.pwmWrite(PIN_FAN, i)
        PI.delay(250)
    '''
    try:
        while True:
            current_temp = get_cpu_temp() #获取cpu温度
            scaling_cur_freq = get_scaling_cur_freq() #获取CPU频率
            if current_temp >= sta_temp:
                is_work = 1
            if is_work == 1:
                # 当前温度小于最小温度,转速为零
                if (current_temp <= min_temp):
                    speed = 0
                # 当前温度大于最大温度,转速全开
                elif (current_temp > max_temp):                    
                    speed = 100
                    print("full speed!\n",end ='')
                else:
                    # 在低温阈值和高温阈值之间时 根据温度线性使用PWM控制风扇转速
                    # 转速=基础转速+(当前温度-最小温度/最大温度-最小温度)*(100-基础转速)
                    speed =speed_base+(current_temp - min_temp) /(max_temp-min_temp) *(100-speed_base)      
                    # 四舍六入取整。
                    speed = round(speed) 
                '''
                循环线性变换速度 避免急启急停
                '''
                if (0 < speed <=100):
                    if (old_speed < speed):
                        for i in range(old_speed,speed,1):
                            PI.pwmWrite(PIN_FAN, i)
                            PI.delay(100)
                    else:
                        for i in range(old_speed,speed,-1):
                            PI.pwmWrite(PIN_FAN, i)
                            PI.delay(100)
                    old_speed = speed
                    is_work =1
                    print(f"current temp: {current_temp:.2f}\u2103,frequency: {scaling_cur_freq:4}/{scaling_max_freq}Mhz,fan speed: {speed:3}%")
                else: 
                    print("fan stop!\n",end ='')
                    # (speed=0)风扇停转,设置工作状态
                    if(old_speed <= 100):
                        i = old_speed
                        while (i >= speed):
                            PI.pwmWrite(PIN_FAN, i)
                            i -=1
                            PI.delay(100)
                        old_speed = 0
                        is_work = 0
            else:
                PI.delay(5000)
                print("Waiting for fan to start!")
            # 控制频率延时
            PI.delay(3000)
            
    except KeyboardInterrupt:
            #风扇停转
            PI.pwmWrite(PIN_FAN,0)
            print("quit sucess!")
            

5、使用supervisor实现开机启动

把上述代码保存为:fancontrl.py

放在/home/pi/Downloads/目录下

(1)supervisor安装
sudo apt-get install supervisor
(2)主配置文件
supervisor主配置文件:sudo vi /etc/supervisord.conf

(3)添加子配置文件
新建子进程配置文件,路径:sudo vi /etc/supervisord.d/fancontrl.conf

在子配置文件添加内容如下:

[program:fancontrl]
command = python3 /home/pi/Downloads/fancontrl.py
autostart=true

(4)supervisor管理命令
sudo systemctl restart supervisor //重启服务
sudo supervisorctl status        //查看所有进程的状态
sudo supervisorctl stop fancontrl       //停止fancontrl 
sudo supervisorctl start fancontrl      //启动fancontrl 
sudo supervisorctl restart fancontrl       //重启fancontrl 
sudo supervisorctl update        //配置文件修改后使用该命令加载新的配置
sudo supervisorctl reload        //重新启动配置中的所有程序 

6、相关参考链接:

wiringpi的github地址:https://github.com/WiringPi/WiringPi/

wiringpi库详解:https://www.cnblogs.com/lulipro/articles/5992172.html

树莓派智能变频散热风扇开售,驱动开源:https://shumeipai.nxez.com/2020/07/02/rpi-fan-on-sale.html

树莓派实现温度监控并控制风扇散热:https://shumeipai.nxez.com/2017/07/13/raspberry-pi-to-achieve-temperature-monitoring-and-control-fan-cooling.html

【技术分享】初玩树莓派B(三) 控制蜂鸣器演奏乐曲:https://www.anquanke.com/post/id/84711

BCM2835_PDF_Datasheet:https://www.petervis.com/Raspberry_PI/BCM2835_PDF_Datasheet/BCM2835_PDF_Datasheet.html


总结

活到老,学到老,折腾之路还漫长,且学且珍惜。


版权声明:本文为CSDN博主「luzze__123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/luzze__123/article/details/118641526

生成海报
点赞 0

luzze__123

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

PySerial库的简单用法

import serial API:pySerial API — pySerial 3.4 documentation 这次尝试适用PySerial库是为了从树莓派小车连接的UWB定位模块的串口中,获取与解析出串

树莓派pico从零开始的入门(一)

准备 1.网购一块树莓派pico开发板及套件(50Rmb) 2.一台电脑 开始 1.使用usb将树莓派pico与电脑连接 2.打开后来到树莓派pico的文件夹下面:有两个文件打开htm文件来到这里 https://www.raspberry

树莓派(python)与arduino串口通信

第一步:先设置硬件串口分配给GPIO串口 输入sudo raspi-config命令进入树莓派系统配置界面,选择第三个Interfacing Options 进去选择Serial Port 然后选择关闭串口登录功能&#xff0