0x01 序言

最近在做一块单板,这块板子需要更低的待机功耗与工作功耗,但是没有任何物理方式进行功率的降低。思前想后,我终于找到了这个折中的方式,从板子上的继电器(那块单板的继电器是出奇的多——上百,也是功耗高的主要原因)。查看本片文章,您需要具备的知识有:

技能

熟悉程度

参考链接

模电

了解

暂无

数电

了解

暂无

PWM定义

熟练

暂无

变压器

熟练

暂无

[toc]

0x02 原理

首先,我们随便拿一个继电器作为演示。

python控制继电器模块 继电器pwm控制_嵌入式

一般靠谱的厂家的数据手册(Datasheet)里面都会有继电器的内部电气模型。

python控制继电器模块 继电器pwm控制_单片机_02


所以掌握数据源头相当于掌握了核心科技……基本上所有的继电器都可以被抽象为上图那样的电感线圈+二极管/三级管(选配),而这里我们为了简便起见,暂时按照最简单的单独电感线圈抽象模型进行计算。

从驱动端看过去,一个继电器就是一颗电感的一端,我们驱动继电器,其实就是在驱动一个电感线圈进行工作。每一个继电器都会有自己的额定工作电压与在额定工作电压下的工作电流。常见到的低压继电器主要是5、12、24、40、60等低压,电流在20~100mA不等,由于我们将继电器抽象为电感线圈,由于电感的特性可以得知,在电路稳态时,电感线圈的功耗最小(电阻极小),所需要的电流自然也很小。所以,可以在电感以趋于稳态时的某些时刻,对于电感线圈两端进行可靠的降低输入功率并保证其正常吸合来降低功耗。

平时使用的继电器波形为了省力,一般会采用直接驱动。也就是下面的波形

python控制继电器模块 继电器pwm控制_引脚_03


而经过我们抽象出的电感的波形就变成了一阶的瞬态电感+电阻耦合波形。

python控制继电器模块 继电器pwm控制_嵌入式_04


相应的,这是一种很大的浪费,所以,我们可以对于输入的控制波形进行一定的调制,使其得到绝佳的控制。

python控制继电器模块 继电器pwm控制_python控制继电器模块_05

当输入如上波形的控制信号时,对于继电器抽象出的电感来说。我们其实相当于通入了一个有着Base电压的、交流的、正负端可能被削顶的波形。当然,也有可能呈现为一个上下延较为平滑的三角波。

python控制继电器模块 继电器pwm控制_引脚_06


根据伟大的数学家莱布尼兹(工科是不是要站牛顿那边啊?)的积分学(工业PWM与SPWM的理论基石),我们可以将其等效为

输入电压 = 输入波形最高值 *(输入高电平时间 / 输入波形周期 )

于是,只要能够制作出满足这种条件的电平,就可以完成对于电压的调制。从而降低继电器的功耗。

0x03 实现误区

从原理看起来似乎是很简单的东西,你也许会感到好奇:如果单单是积分单个波形周期内的电平值就可以降低电压,那我能不能输入一个看起来不是那么正常的波形进行一定的控压呢?

python控制继电器模块 继电器pwm控制_单片机_07

答案自然是不能,因为这样做:
一是可能会不满足继电器对于动作电压的最小时间限制
二是这种波形经过傅里叶展开之后,可能单个时间点的平均电压可能会不满足继电器的吸附电压,导致继电器有频繁跳动的风险。
三是这种波形的设计无法简单的取得当前波形频率(单点可能存在多个重合区域且各个重合区域都不相同,也就无法计算波形周期)计算电压会带来很大的不便。所以最好不要用这种方式进行驱动,否则在主触电可能是几百伏的电压的情况下,这种操作简直是在玩火(继电器大电压频繁开关、无法有效吸合和卡阻,可能会产生无法去除的电弧导致燃烧)。

0x04 实现具体的步骤

因为某些原因,这里不方便直接贴出代码。但是我会以C语言的思维与形式编写伪代码、使用流程图的方式讲述实现细节。

首先,我们需要有一个PWM的驱动方式。在电路中有两种实现方式:单独驱动、开关驱动。

0x14 单独驱动

单独驱动就是使用单片机直接或者间接的驱动当前电路,生成PWM的方式仅在单片机上,这种方式硬件实现简单,软件实现复杂,稳定性较高(取决于实现方案),对于芯片的引脚要求极高,对电路设计的要求较低。如果是单板的大小受到严格限制可以采用这种实现方式。

0x24 开关驱动

开关驱动设计理念在于对所有或者多个继电器使用同一PWM进行驱动,单片机仅作为控制PWM输出的控制器。** 实现较单独驱动较为复杂,使用单片机驱动某个芯片,由芯片将极少的驱动引脚扩展成较多的驱动引脚控制另外一些芯片。这些芯片/电路可以选择性的打开某些继电器的引脚并使其接入指定的PWM生成电路生成的波形。** 这样对于单片机的驱动引脚来说就不必关注于PWM波形,而将重心转移到切换继电器状态中。这种方式对于软件要求较低,对硬件电路要求极高,且PWM生成电路需要有很强的带载能力。且需要较大的布板容积。如果单片机资源有限时,可以采用这种方式。

有了PWM驱动源的解决方案后,我们仅需要对指定的继电器发送 开启关闭即可。因为开关驱动软件上实现太简单了,所以这里仅列了单独驱动的软件上的实现可以的参考代码(当然,也很简单):

tyepdef struct 
{
    unsigned int 各个继电器的对应状态;  //为节省空间,可以将单个继电器的状态按位区分。
    unsigned int 继电器的控制占空比;    //调整后可以固化在系统内部
    unsigned int 继电器的频率;	     //如上
    unsigned int 继电器的驱动方式;      //用于标识时单独驱动
}
void PwmCtrl()	//继电器实际的驱动方式,一个极为简单的PWM生成电路
{
    if (继电器的控制占空比 < 现在的控制时刻)
    {
        for(轮询当前继电器是否有开启位)
        {
            if(继电器对应状态有开启位)
            {
				继电器控制引脚输出高电平。
            }

        }    
        
    }
    else	//现在已经满足指定占空比了。
    {
        强制关闭所有继电器
        
    }
    if(当前运行时刻 >= 继电器的频率)
    {
        
        运行时刻清零
        
    }
        当前运行时刻+1
    
}
void main()
{
    
    修改相关的继电器状态位。
        PwmCtrl();
}

而对于开关驱动的方式而言,工作电路可能较为复杂。所以在这里我谨以个人的绵薄之力,画出各个模块的组成框图,以简单的标识。本人的电路功底不深,如有错误尽请见谅。

python控制继电器模块 继电器pwm控制_引脚_08

其中,PWM电路可以使用H桥、图腾柱等电路进行实现,首推就是H桥,如果在使用大电流MOS可以获得极大的带载能力,较为适合多个继电器的驱动。而图腾柱在安全和稳定性上更胜一筹(H桥一不小心容易出现较大的问题),但是驱动能力可能远没有H桥强,可能需要多个电路才可以驱动较多的继电器。

选择输出电路则可以使用三极管、MOS管、与或门等可以选通信号的电路进行驱动,只要保证电路可以被有效的控制即可。

同样的,因为单独驱动的电路框图太过简单,本文暂时略过其框图(如果真想要可以去掉开关驱动框图中的PWM生成电路与选择输出电路即为单独驱动的电路框图)。

0x05 可能出现的意外

0x15 尖啸

你可能发现,当改成了PWM驱动的方式,我们的驱动电流的确下降了很多(为什么下降的可以仔细观察我之前的一阶非线性电感曲线),但是可能继电器会传出令人十分烦恼的高频的尖啸声(类似频率较高的蝉鸣或蟋蟀声)。

这个其实维基之类的科普已经很好了,但是在这里还是有必要提一下:**尖啸的根源在于电感线圈在继电器内并没有被可靠固定,或继电器并没有可靠固定在单板上。**因为我们使用了较高的频率通入电感线圈,电感就会产生磁场,而受制于继电器的工作环境,会产生较大的吸力以及斥力(电感线圈自身的自感电动势带来的反向磁感线导致了磁场互斥)。这种吸力–>斥力的转化速度极快,就会使空气震动发出一定的声音。这种声音一般是极高频率的,但是取决于线圈的质地、空气与继电器内部抗震器件的阻力以及在继电器内部的状态,震动会逐渐降低,声音也会逐渐衰减到人耳可以听到的啸叫声。这种情况下其实不用担心,但是考虑到对于耳朵的保护与对于继电器的保护,最好还是通过实验将频率渐渐放置在一个较为合理的位置。

python控制继电器模块 继电器pwm控制_python控制继电器模块_09

0x25 某些机械卡阻、继电器不规律时断时续

其实这个问题就是上文讲述实现误区上面的可能出现的波形。引发的问题,在某些波形无法满足继电器的需求的时候,继电器可能会出现卡租(电压较低)、不规则跳动(控制信号小于最低保持信号)。

0x06 总结

在本人测试的情况下,似乎任何时刻都可以稳定的开关继电器的方式在占空比为80%以内的时候。在功耗要求较高的情况下,也可以使用开启继电器(100%占空比)–>等待1s–>降低占空比,这种方式甚至可以降低到50%也可以正常保证驱动。但无疑增加了硬件电路的实现难度(开关驱动方式而言)。

0x07 发散思维

不知道看到这里的各位,如果我们将继电器抽象为一个电感线圈回路,那么如果向其通入恰好可以让其谐振的频率又会是什么现象呢?这个大家可以思考一下。也可以和我讨论一下。