天气太热,睡觉用鸿运扇吹风,有时候会觉得最慢档的风力还是嫌大。我先后尝试给鸿运扇串接一个吊扇的电感调速器,也试过用可控硅调压作调速器,但即便电源电压调到较低了,低到风扇勉强能启动的程度,但还是感到风扇转速偏大,边吹边睡不太舒服。
这也可能我的这个风扇低压启动性能不好。最后我改用单片机控制,用PWM方波控制这个鸿运扇。通过调整不同占空比来控制转速,结果效果比电感调速或者可控硅调压调速好,风扇转速可以调得很慢,还可以通过间隙性给风扇供电,模拟自然风效果。最后再加个红外遥控功能,用起来比较满意。
现将对应的51单片机红外接收遥控风扇控制电路及软件代码介绍如下,供有兴趣的朋友参考。


图3,装在饮料瓶里的红外接收电路+输出插座。

图4,红外接收+控制电路原理图。
软硬件说明:
图4里用标称输出5V的USB电源模块给系统供电。因为实测其输出电压为6V,所以在其5V输出端串接了一个二极管降压。
考虑到控制PWM输出的电源开关会频繁地通断,普通电磁继电器的触点寿命有限,所以采用固态继电器控制220V交流输出插座。
图4还画了一个双抛开关继电器,原来想用来控制风扇的快慢档线圈转换的。使用中发现,只要调整PWM不同占空比来控制风扇转速已经能基本满足要求,这个器件就多余了。
(4)此前是4keys控制:
0x16=“0”=总电源开关,控制固态继电器-》开通或关断,每按键一次翻转一次。
0x0c=“1”=快慢档控制,控制动圈继电器-》双刀双抛开关实现快慢档转换,每按键一次翻转一次。
0x44=“VOL-”=减速,主继电器断电时间控制,=speed-(主继电器断电时间增加到10就变1)。
0x40=“VOL+”=加速,主继电器开通时间控制,=speed-(主继电器开通时间增加到8就变1)。
(5)后来改为8keys控制:
0x43=“PAUSE/play”=总电源开关,控制固态继电器-》开通或关断,每按键一次翻转一次。
0x09=“EQ”=快慢档控制,控制动圈继电器-》双刀双抛开关转换,每按键一次翻转一次。
0x44=“PREV”=减速,主继电器断电时间增加到10就停止。
0x40=“NEXT”=加速,主继电器断电时间减少到1就停止)。
0x15=“VOL-”=减速,主继电器开通时间减少到1就停止。
0x07=“VOL+”=加速,主继电器开通时间增加到10就停止。
(6)单片机软件下载用CH340下载线(如下图5,只用TXD+RXD+GND等3根线)。

(7)P02=HYDZRbeeper(PIN38)=P02+10k上拉+1K串接9014-B极,9014-C极+LED+BEEPER.
(8)P03=J1(WANJIA双刀双抛继电器,5A250V)=P03+10k上拉+1K串接9014-B极,9014-C极+WJ113H继电器(RJ=55欧,IJ=58MA)
(9)P04=Jmain(总开关固态继电器)=P03+10k上拉+1K串接9014-B极,9014-C极+固态继电器(IJmain=12MA)。负载接50W灯,导通时,输出接点压降=8VAC。
(10)38k红外遥控器(配1838红外接收头=HS0038B):
(11)遥控器按键值
//ucharcodeTest1[4]={0x16,0x0c,0x08,0x42,};//遥控键0-1-4-7
//ucharcodeTest2[4]={0x18,0x5e,0x1c,0x5a,};//遥控键2-3-5-6
//ucharcodeTest3[4]={0x42,0x52,0x4a,0x15,};//遥控键7-8-9-+
按键名称数据码,按键名称数据码,按键名称数据码,按键名称数据码
00x1610x0c20x1830x5e
40x0850x1c60xaa70x42
80x5290xab-0x07+0x15
eq0x09next0x40prev0x44play0x87
PAUSE/PLAY0x43
--------------------------===================================------------------------------
(12)源码:
unsignedcharIRtime;//红外信号位周期时长计时器,由T0中断计数。
bitirpro_ok,IRrec_Ok;
unsignedcharIRcode[4];//将接收的红外32位数据按四个字节数据保存
unsignedcharIR_time[33];//分别寄存33个bit的时间
sbitBeep=P0^2;//红外接收提示LED灯+beeper
sbitSpeed=P0^3;//9014驱动的动圈继电器J1-快慢档输出端口
sbitPower=P0^4;//固态继电器Jmain-总电源控制输出端口
defineOFF0;//定义led=0=off
defineFALSE0;
ucharTon,Toff;//Ton是主继电器开通延时时间,Toff是主继电器关断延时时间。
ucharTon_count,Toff_count;//Ton_count是主继电器开通计数器,Toff_count是主继电器关断计数器。
ucharF_Speed;//调速模式标志,=TRUE=1是调速状态,=0是正常速度状态。
/*--------------------函数声明----------*/
voidIr_work(void);
voidIrcode_pro(void);
voidDelay_1ms(unsignedintTx)
{unsignedintt1,t2;
for(t1=0;t1Tx;t1++)for(t2=0;t2120;t2++);
}
voidBeeper(unsignedintTx)
{Beep=ON;Delay_1ms(Tx);Beep=OFF;Delay_1ms(10);
}
definePreloadT0L(65536-250)%256;
definePreloadT1L(65536-50000)%256;
unsignedintPreT0h,PreT0l;
unsignedintPreT1h,PreT1l;
//===================================================================
//T0=0001,设为16位定时工作方式0
voidTIME0_init(void)
{TMOD=0Xf0;//低位T0计数方式清0
TMOD|=0X01;//T0=计数方式1=16位,
//T0方式选择位C/T=0=定时工作方式1,=16位,不自动重装参数
PreT0h=PreloadT0H;PreT0l=PreloadT0L;
TH0=PreT0h;TL0=PreT0l;
TR0=1;
ET0=1;//允许计数器0中断
PT0=1;//T0为最高优先级
}
/*-----------------------*/
voidtime0_isr(void)interrupt1using1
{IRtime++;//用于计数2个下降沿之间的时间
TH0=PreT0h;TL0=PreT0l;
}
//==================================================================
//T1设为16位定时工作方式0
voidTIME1_init(void)
{
TMOD=0X0f;//高位T1计数方式清0
TMOD|=0X10;//T1=计数方式1=16位,
//T1方式选择位C/T=0=定时工作方式1=16位,不自动重装参数
PreT1h=PreloadT1H;PreT1l=PreloadT1L;
TH1=PreT1h;TL1=PreT1l;
TR1=1;
ET1=1;//允许计数器1中断
}
/*----------------------------------*/
ucharTxms_count=0;
voidtime1_isr(void)interrupt3
{TH1=PreT1h;TL1=PreT1l;
if(Txms_count++=20)
{Txms_count=0;
if(F_Speed==1)//=1是调速状态
{
if(Ton_count++Ton)
{Power=ON;}
elseif((Ton_count=Ton)(Toff_count++Toff))
{Power=OFF;}
if((Ton_count=Ton)(Toff_count=Toff))
{Beeper(2);//=心跳信号,在慢速工作状态时,每个"通电+断电结束"周期响一短声。
Ton_count=0;Toff_count=0;}//定时时间到,计数器清零
}
}
}
/*----------------------------*/
voidEX0init(void)
{IT0=1;//指定外部中断0下降沿触发,INT0()
EX0=1;//使能外部中断
}
/*-----------------------------*/
voidEX0_ISR(void)interrupt0//外部中断0服务函数
{//接收红外信号处理,将接收到各位数占时存储下来
staticunsignedcharIRcount;//接收位数计数器
staticbitstartflag;//是否开始处理标志位
if(startflag)
{
if(IRtime32)//IRtime32即认为是引导码,丢弃
//引导码=TC9012的头码=53*IRtime=9ms+4.5ms,33*IRtime=8.448ms
//whenIRtime=250us,33*IRtime=8250us,32*IRtime=8000us
{IRcount=0;}//IR_time[0]=引导码的时间长度=9ms+4.5ms,
IR_time[IRcount]=IRtime;//存储每个位电平的持续时间,用于以后判断是0还是1
IRtime=0;//清零
IRcount++;//接收位数计数器
if(IRcount==33)//收够32位数据了
{IRrec_Ok=1;IRcount=0;}
}
else
{IRtime=0;startflag=1;}
}
voidIr_work(void)//根据接收到的红外键值跳转处理程序--
{
switch(IRcode[2])//只判断第三个数码值
{
case0x43://key=PAUSE/play=暂停=Jmain主继电器开通或关断,每按键一次翻转一次
F_Speed=FALSE;//FALSE=0==正常速度状态
Power=!Power;Beeper(200);//跟踪Power=~Power;是同样效果-20220731
break;
case0x09://key=EQ=快慢控制继电器)快速或慢速控制(接通1为慢速,接通2为快速)
F_Speed=FALSE;//FALSE=0=正常速度状态
Speed=!Speed;Beeper(200);//每按键一次翻转一次
break;
case0x44://=PREV=Toff--(Jmain主继电器开通时间减少)
F_Speed=TRUE;//TRUE=1=调速状态
if(Toff--2)
{Toff=1;Beeper(1000);}//从2开始,10档速度,每按键一次,断电时间-1s
Beeper(200);break;
case0x40://=NEXT=Toff++(Jmain主继电器开通时间++)
F_Speed=TRUE;//TRUE=1
if(Toff++10)
{Toff=10;Beeper(1000);}//从2开始,10档速度,每按键一次,断电时间+1s
Beeper(200);break;
case0x15://=VOL-=Ton--(Jmain主继电器开通时间--
F_Speed=TRUE;//TRUE=1
if(Ton--2)
{Ton=1;Beeper(1000);}//从2开始,8档速度,每按键一次,通电时间-1s
Beeper(200);break;
case0x07://=VOL+=Ton++(Jmain主继电器开通时间--
F_Speed=TRUE;//TRUE=1
if(Ton++8)
{Ton=8;Beeper(1000);}//从2开始,8档速度,每按键一次,通电时间+1s
Beeper(200);break;
default:F_Speed=FALSE;break;
}
irpro_ok=0;//处理完成标志
}
voidIrcode_pro(void)
//将接收的32字节存储在IR_time[32],根据时长判断个位是0或1,转为四个字节保存
{unsignedchari,j,k;unsignedcharCode32b,value;
k=1;//将引导码=IR_time[0]去掉,所以令k=1;
for(i=0;i4;i++)//处理4个字节
{
for(j=1;j=8;j++)//处理每1个字节的8位数
{
Code32b=IR_time[k];//1th,取出32位数的的第一位IR_time[1],根据时长来判断是0或1。
if(Code32b6)//大于某值为1,这个和晶振有关系
//如果收到1,对应的高电平时间=1.168ms,IR_time[]=1.168ms/IRtime,必定大于等于4。
//如果收到0,对应的高电平时间=0.56ms,IR_time[]=0.56ms/IRtime,必定=2。
//7*IRtime=1750us,6*IRtime=1500us,560/250=2.24,1168/250=4.67个IR_time
//另有资料说,1对应的高电平时间=1.69ms,IR_time[]=1690us/250=6.76个IR_time。
//结论:用4或8会得乱码。用5-7解码结果都对,取6较合理。
value|=0x80;//令最高位=1
if(j8){value=1;}//处理下一位
k++;
}
IRcode[i]=value;//将接收的8位数据按1个字节保存
value=0;
}
irpro_ok=1;//将接收的32位数据按四个字节保存,处理完毕标志位置1
}
/*-----------主函数-------*/
voidmain(void)
{//MCU通电瞬间的端口高电平,会令各输出端口的9014导通+继电器吸合通电,改用PNP的9012可避免此问题
Power=OFF;Speed=OFF;Beep=OFF;//开机先令几个输出端口=0,防止继电器通电!
Delay_1ms(500);Beeper(200);
EX0init();//初始化外部中断
TIME0_init();//初始化定时器0-红外接收定时
TIME1_init();//初始化定时器1-继电器开关定时
Ton=3;Toff=2;//Ton是主继电器开通和关断的延时时间,一秒为单位,调速才用到
Ton_count=0;Toff_count=0;//主继电器开通和关断计数器,调速时,在T1中断1S后计数
EA=1;//开总中断
while(1)//主循环
{
if(IRrec_Ok)//如果在INT0接收到正确的红外信号,就跳转这里进行红外数据处理
{Beeper(30);//接收到红外按键信号,发出提示音
Ircode_pro();//将接收的32位时长数据按四个字节提取并保存到IRcode[i]
IRrec_Ok=0;
}
if(irpro_ok)//数据处理好后,做对应工作
{Ir_work();}//根据红外键值跳转处理程序
}
}
写于20220813





