|
自家用多丽牌落地风扇,型号是FS11-40,最近出现故障,无规律自动开机、关机,无规律自变档位。特别是晚上睡觉后自动开机,很是烦恼。上网查询打算网购一块电脑板,但均找不到匹配的。家里正好有一些单片机配件,决定自己改装及维修。 第一步:拆机。
观察电路板上单片机芯片,型号为“KEJIE-38/S8EC_94H4”,DIP20封装,测单片机供电只有4.5V,怀疑供电不足,在断开220V交流电的情况下,用直流5V直接供电,故障依然无规律出现,所以基本判断芯片内部损坏,决定自己改装。
第二步:改装准备。
1.遥控器的匹配,在单片机开发板上测试原机遥控器,无响应,读不出键值。试了家里海信电视的遥控器,能读出键值,最下面四个按键键值分别为“62、64、63、65”,这四个按键电视机用不上,所以决定用作风扇的控制,功能分别定为“关机、开机/风速、摇头、定时”。
2.功能增减:
风扇原遥控器有“风类”调节功能,由单片机“定时器+PWM”控制电机模拟各种风类,由于这个功能平时不常用,所以删减了这个功能。
家人经常有出门不关风扇的情况发生,决定增加无条件定时关机功能,定时时间为4小时,一但开机,4小时后必定自动关机。
原电路采用15个LED显示工作状态,包括显示“1、2、3”档位和“0.5h、1.0h、2.0h、4.0h”定时状态和风类等信息。这15个LED和4个按键采用查理复用电路或是矩阵电路,没有深究,因待改装的单片机型号为STC89C52,IO口够用,所以决定用两位共阳数码管代替原机的15个LED,4个按键保留。
第三步:改装。
1.将原机单片机芯片拆下,焊下15个LED,保留原机阻容降压电路、整流滤波电路、5.1V稳压电路、4个按键、红外接收管、4路双向可控硅电路等。将数码管用硅胶固定在原LED相应位置,8个段线分别加100欧限流电阻,2个位线分别加8550三极管驱动。用洞洞板焊了一个最小系统板,用硅胶固定在原电路板的背面,不妨碍安装即可。
2.连线,数码管10线、按键4线、风扇电机3线、摇头电机1线、蜂鸣器1线、电源2线、红外接收管1线、共22根线。
第四步:编程与仿真。
除红外遥控功能外,其他功能都可以仿真。程序附后。
第五步:程序下载与测试。
测试成功,最终效果不错。附件中有视频。
附单片机程序(部分):
#include <reg52.h>
#include <intrins.h>
#define uint unsigned int
#define uchar unsigned char
uchar code SEG[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //数码管代码
sbit IR_GET = P3^2; //红外接收头数据输入端
uchar RXDDATA[]={0x00,0x00,0x00,0x00}; //存放接收到的四组红外编码
uchar IRDATA; //遥控键键值
sbit COM1 = P1^6; //数码管十位
sbit COM2 = P1^7; //数码管个位
sbit KEY1 = P3^4; //关
sbit KEY2 = P3^5; //开/风速
sbit KEY3 = P3^6; //摇头
sbit KEY4 = P3^7; //定时
sbit LED1 = P1^0; //一档,仿真用LED,实际接电机低速档(蓝色线)
sbit LED2 = P1^1; //二档,电机中速档(白色线)
sbit LED3 = P1^2; //三档 电机高速档(红色线)
sbit LED4 = P1^3; //摇头,电机高速档(紫色线)
sbit BEEP = P1^4; //蜂鸣器
uchar keypress; //KEY2的按键值,即档位值
uint keytime; //倒计时时间,单位为小时,数码管显示值
int time=14759; //倒计时时间,单位为秒
uint cnt1; //定时器1计数变量
uchar flag=0; //档位标志
bit L_R=0; //摇头状态变量
bit L_R_flag; //摇头标志
bit key4_flag; //定时按键标志
/*--------------1ms延时---------------*/
void delay_ms(uint xms)
{
uchar i;
while(xms--)
{
i=123;
while(i--);
}
}
/*--------红外专用延时,约0.1ms延时函数----------*/
void delay(uchar x)
{
unsigned char i;
while(x--)
{
for (i = 0; i<10; i++);
}
}
/*--------数码管显示一:显示档位---------------*/
void show_seg1(uchar dat)
{
if(flag!=0)
{
if(L_R==1) //如果是摇头状态,则十位显示“-”,否则不显示
{
P2=0xbf; //“-”
COM1=0;
delay_ms(4);
COM1=1;
}
P2=SEG[dat%10]; //个位显示档位“123”
COM2=0;
delay_ms(4);
COM2=1;
}
}
/*--------数码管显示二:显示倒计时时间---------------*/
void show_seg2(uchar time)
{
if(cnt1<500) P2=SEG[time/10] & 0x7f; //显示时间十位,带秒点并闪烁
else P2=SEG[time/10];
COM1=0;
delay_ms(4);
COM1=1;
P2=SEG[time%10]; //显示时间个位
COM2=0;
delay_ms(4);
COM2=1;
}
/*--------蜂鸣器函数---------------*/
void beep() //蜂鸣器,这里选用有源蜂鸣器
{
BEEP=0; //若选用无源蜂鸣器,则可模拟输出方波驱动发声
delay_ms(70);
BEEP=1; //关闭蜂鸣器
}
/*--------------按键扫描---------------*/
void scan_key()
{
if(KEY1==0) //关机控制
{
beep(); //使用蜂鸣器发声时的延时作消抖
if(KEY1==0)
{
flag=0; //档位标志复位,关闭风扇电机
keypress=0; //KEY2的按键值复位
L_R_flag=0; //摇头状态标志复位,关闭摇头电机
}
}
if(KEY2==0) //开机、档位变换控制
{
beep();
if(KEY2==0)
{
keypress++; //档位加
if(keypress > 3) keypress = 1; //值的约束,在开机状态下,轮流显示“123”,表示123档
flag = keypress; //档位值赋给状态标志
while(!KEY2)show_seg1(keypress); //按键松手检测
}
}
if(flag!=0) //摇头控制
{
if(KEY3==0)
{
beep();
if(KEY3==0)
{
L_R = ! L_R; //摇头状态翻转,开或关两个状态
LED4 = ! L_R; //摇头电机开启或状态, P1^3输出0为开启摇头电机
while(!KEY3)show_seg1(keypress);
}
}
}
if(flag!=0) //定时设置
{
if(KEY4==0)
{
beep();
if(KEY4==0)
{
key4_flag = 1; //定时状态标志
keytime = keytime+5; //按键每按一次加5,即“0-5-10-15-20-25-30”,这里加大了10位,后面需缩小10倍处理
//实际显示“0.0-0.5-1.0-1.5-2.0-2.5-3.0”,单位为小时
if(keytime > 30) keytime = 0; //定时时间最大定为3小时,用户可以更改
if(keytime!=0) time = keytime*360+359; //时间换算成秒,这里本应为“*3600”,因需缩小10倍处理,故为“*360”
while(!KEY4) show_seg2(keytime) ;
}
}
}
}
/*--------------定时器1初始化---------------*/
void Timer_Init(void) //1000微秒@12.000MHz
{
TMOD |= 0x10; //设置定时器模式
TL1 = 0x18; //设置定时初始值
TH1 = 0xFC; //设置定时初始值
ET1 = 1; //使能定时器0中断
}
/*------------外部中断EX0初始化-------------*/
void init()
{
EX0= 1; //使能 INT0 外部中断
IT0 =1; //外中断0下降沿触发
IR_GET=1; //I/O口初始化
}
/*--------------主程序---------------*/
void main()
{
Timer_Init();
init();
EA = 1;
beep(); beep(); beep();
while(1)
{
scan_key();
if((key4_flag==0)|(keytime==0)) show_seg1(keypress); //非定时状态或定时设定时间为0时,显示档位值
else show_seg2(time/360); //否则显示倒计时时间
switch(flag)
{
case 0: LED1 = 1; LED2 = 1; LED3 = 1; LED4 = 1; L_R=0; keypress=0; keytime=0; TR1=0; time=14759; break;
case 1: LED1 = 1; LED2 = 1; LED3 = 1; LED1 = 0; TR1=1; break; //考虑到档位电压的冲撞,先关闭所有档位再打开,下同
case 2: LED1 = 1; LED2 = 1; LED3 = 1; LED2 = 0; TR1=1; break;
case 3: LED1 = 1; LED2 = 1; LED3 = 1; LED3 = 0; TR1=1; break;
}
}
}
/*--------------定时器中断处理---------------*/
void Timer1_Isr(void) interrupt 3
{
TL1 = 0x18; //重装初始值
TH1 = 0xFC; //重装初始值
cnt1++;
if(cnt1>1000) //1秒溢出
{
cnt1=0;
time--; //定时时间秒自减1
if(time<358) {flag=0;time=14759;} //时间到,关机
}
}
/*--------------外部中断处理:处理红外码--------------*/
void intt_0() interrupt 0 //下降沿触发:接收不到红外时OUT高电平,接收到红外时OUT低电平。
{
uchar four,one,num=0;
EX0 = 0; //关中断0使能,防止处理过程中再接收红外信号
delay_ms(2); //稍延时2ms,防干扰
if (IR_GET) //再检测红外接收脚(9ms的前导低电平),为高电平说明是干扰
{
EX0 =1; //使能中断0
return; //退出中断程序
}
while(!IR_GET); //等IR变为高电平,跳过9ms的前导低电平信号。
while (IR_GET); //等 IR 变为低电平,跳过4.5ms的前导高电平信号。
for (four=0;four<4;four++) //四组数据
{
for (one=0;one<8;one++) //每组数据8位
{
while (!IR_GET); //等 IR 变为高电平
while (IR_GET) //计算IR高电平时长(低电平时长是一样的,不用计)
{
delay(1); //计时
num++; //计时N次
if (num>=20) //20*0.1ms=2ms
{ //数据“1”的时长最长也就1.685ms,计数超过则数据错误,退出中断
EX0=1; //使能中断0
return; //退出中断
}
} //高电平计数完毕
RXDDATA[four]>>=1; //从低位读出,随着one的循环8次刚好读出一字节
if(num>6&&num<20) // 20*0.1ms=2ms>1.685ms
RXDDATA[four]|=0x80; //数据“1”
num=0; //计时值清0,为下一位数据的计时做准备
}//一组数据接收结束
}//全部四组数据接收结束
if (RXDDATA[2]!=~RXDDATA[3]) //检测接收到的数据是否正确
{ //不正确则
EX0=1; //使能中断0
return; //退出中断
}
IRDATA=RXDDATA[2];
switch(IRDATA)
{
……
}
EX0 = 1; //处理完红外接收,使能中断0,退出中断0
}
|
评分
-
查看全部评分
|