4.1 系统功能描述
众所周知,现阶段我国的能源缺口十分巨大,但人们的生产及生活又少不了能源。以电力供求为例,有些家用电器的耗电量很厉害,如家庭取暖器、冷暖式空调等。有些工厂是名符其实的耗电大户,如电解铝、电解铜生产厂,炼钢厂等。
如能采取一些合理化的用电方式,则可望能较大幅度地节能或节支。其中的一个较好方法就是分时控制作业。现阶段,不仅工业用电而且生活用电均采取分时计费的方法。如果能将白天用电高峰期开动的一些设备改在夜晚用电低谷期使用,则不仅可大幅减轻电网的负载,而且还可有效减轻用户的用电费用。
我们以北京市电采暖低谷用电优惠办法实施细则来看,电采暖用户每年从11月1日起至次年3月31日止享受低谷用电优惠,低谷优惠时段为晚23:00至次日早7:00。
同样,工业生产中,低谷用电的用户也能享受到不少的优惠。
这里介绍的“工业生产节能时控器”就是用来定时控制生产设备工作的一种节能装置。由于其体积小、成本低,不仅适合嵌入到各种能耗大的工控设备中进行控制,也适合居民的家用电器节能控制。节能节支的效果显著。
“工业生产节能时控器”的核心实际上是一台基于单片机的数字电子钟,设有四位LED数码管显示,具有二个时间控制点(控制一路负载的通断)。因要求其具有低成本及小体积,因此设计时采用单片的精简型、小引脚单片机AT89C2051来实现“工业生产节能时控器”的所有功能。
4.2 系统方案及电路设计
4.2.1 系统方案
单片机是整个系统的核心,整个系统的所有运行如:产生精确时钟及计时,LED数码管显示器输出扫描显示,输出驱动继电器、驱动蜂鸣器、扫描按键输入等,均由单片机完成。这里也使用了有口皆碑的小引脚、高性能、低价位的精简单片机:AT89C2051。
按键输入电路负责输入单片机中一系列工作参数及功能设定:如进行走时设置、定时设置、…等。
LED数码管显示器主要用以显示走时及定时设置。
LED数码管显示器采用程序动态扫描显示,这样与静态显示相比,可节约大量的外部元器件。
电源部分负责对整个系统供电,平时交流电经降压后,整流、滤波、稳压成5V直流电源供系统工作,当交流电源断电时,则由3.6V镍铬电池进行后备供电,保持走时准确,但此时LED数码管显示器关闭显示,以降低3.6V镍铬电池的消耗。
4.2.2 单片机计时原理
单片机作精确的电子钟使用,其根本原因是利用了单片机内部的一个16位定时器(T0或T1),通过设置,使T0(或T1)产生一定时间(例如100ms)的精确定时中断。另外,还需建立几个软件计数器单元(例如deda,sec,min,…)进行配合。例如:每次定时中断时,使deda加1,…。当deda满10时(此时时间为1000ms)使sec加1,同时清除deda。当sec满60时(此时时间为60s)使min加1,同时清除sec。……这样即可完成精确计时。由于软件编程的灵活性,实际上可随意做出任意进制的正计时或倒计时电子钟。图4-2为单片机构成电子钟的原理框图。
图4-2 单片机构成电子钟的原理框图
4.2.3 LED数码管显示器显示原理
在单片机系统中,经常用LED(发光二极管)数码显示器来显示单片机系统的工作状态、运算结果等各种信息,LED数码显示器是单片机与人对话的一种重要输出设备。
1.LED数码显示器的构造及特点
图4-3是LED数码显示器的构造。它实际上是由8个发光二极管构成,其中7个发光二极管排列成“8”字形的笔画段,另一个发光二极管为圆点形状,安装在显示器的右下角作为小数点使用。通过发光二极管亮暗的不同组合,从而可显示出0~9的阿拉伯数字符号以及其它能由这些笔画段构成的各种字符。
图4-3 LED数码显示器的构造
LED数码显示器的内部结构共有两种不同形式,一种是共阳极显示器,其内部电路见图4-4,即8个发光二极管的正极全部连接在一起组成公共端,8个发光二极管的负极则各自独立引出。另一种是共阴极显示器,其内部电路见图4-5,即8个发光二极管的负极全部连接在一起组成公共端,8个发光二极管的正极则各自独立引出。
图4-4 共阳极显示器内部电路
图4-5 共阴极显示器内部电路
从尺寸上分,LED数码管的种类很多,常用的有0.3、0.5、0.8、1.0、1.2、1.5、1.8、2.3、3.0、4.0、5.0寸等。一般小于1.0寸的为单管芯,1.2~1.5寸为双管芯,1.8寸以上的为3个以上管芯。因而它们的供电电压要求不同,一般每个管芯的压降为2V左右。通常,0.8寸以下采用5V供电,1.0~2.3寸采用12V供电,3.0以上的选择更高电压供电。
LED数码显示器中的发光二极管共有两种连接方法:
共阳极接法。把发光二极管的阳极连在一起,使用时公共阳极接+5V,这时阴极接低电平的段发光二极管就导通点亮,而接高电平的则不点亮。
共阴极接法。把发光二极管的阴极连在一起,使用时公共阴极接地,这时阳极接高电平的段发光二极管就导通点亮,而接低电平的则不点亮。
驱动电路中的限流电阻R,通常根据LED的工作电流计算而得到,R=(Vcc-Vled)/Iled。式中,Vcc为电源电压(+5V),Vled为LED压降(一般取2V左右),Iled为工作电流(可取1~20mA)。R通常取数百欧姆。
我们使用的AT89C2051单片机,其I/O口具有20mA的灌电流输出能力,因此可直接驱动共阳极的LED数码显示器。
2.LED数码管显示的基本原理
为了显示数字或符号,要为LED数码显示器提供代码,因为这些代码是为显示字形的,因此称之为字形代码。
七段发光二极管,再加上一个小数点位,共计8位代码,由一个数据字节提供。各数据位的对应关系如下所示:
数据位 D7 D6 D5 D4 D3 D2 D1 D0
显示段 h(或称dp) g F e d c b a
LED数码显示器的字形(段)码表如下所示:
显示字形 字形码(共阳极) 字形码(共阴极)
0 C0H 3FH
1 F9H 06H
2 A4H 5BH
3 B0H 4FH
4 99H 66H
5 92H 6DH
6 82H 7DH
7 F8H 07H
8 80H 7FH
9 90H 6FH
A 88H 77H
B 83H 7CH
C C6H 39H
D A1H 5EH
E 86H 79H
F 8EH 71H
熄灭 FFH 00H
4.2.4 LED数码显示器的显示方法
在单片机应用系统中,LED数码显示器的显示方法有两种:静态显示法和动态扫描显示法。
1.静态显示法
所谓静态显示,就是每一个显示器各笔画段都要独占具有锁存功能的输出口线,CPU把欲显示的字形代码送到输出口上,就可以使显示器显示出所需的数字或符号,此后,即使CPU不再去访问它,显示的内容也不会消失(因为各笔画段接口具有锁存功能)。
静态显示法的优点是显示程序十分简单,显示亮度大,由于CPU不必经常扫描显示器,所以节约了CPU的工作时间。但静态显示也有其缺点,主要是占用的I/O口线较多,硬件成本也较高。所以静态显示法常用在显示器数目较少的应用系统中。图4-6为静态显示示意图。
图4-6中由74LS273(8D锁存器)作扩展输出口,输出控制信号由P2.0和/WR合成,当二者同时为0时,或门输出为0,将P0口数据锁存到74LS273中,口地址为FEEEH。输出口线的低4位和高4位分别接BCD-7段显示译码驱动器74LS47,它们驱动两位数码管作静态的连续显示。
图4-6 静态显示示意图
2.动态扫描显示法
动态扫描显示是单片机应用系统中最常用的显示方式之一。它是把所有显示器的8个笔画段a~h的各同段名端互相并接在一起,并把它们接到字段输出口上。为了防止各个显示器同时显示相同的数字,各个显示器的公共端COM还要受到另一组信号控制,即把它们接到位输出口上。这样,对于一组LED数码显示器需要由两组信号来控制:一组是字段输出口输出的字形代码,用来控制显示的字形,称为段码;另一组是位输出口输出的控制信号,用来选择第几位显示器工作,称为位码。在这两组信号的控制下,可以一位一位地轮流点亮各个显示器显示各自的数码,以实现动态扫描显示。在轮流点亮一遍的过程中,每位显示器点亮的时间则是极为短暂的(1~5mS)。由于LED具有余辉特性以及人眼视觉的惰性,尽管各位显示器实际上是分时断续地显示,但只要适当选取扫描频率,给人眼的视觉印象就会是在连续稳定地显示,并不察觉有闪烁现象。动态扫描显示由于各个数码管的字段线是并联使用的,因而大大简化了硬件线路。图4-7为动态显示示意图。
图4-7 动态显示示意图
在实际的单片机系统中,LED显示程序都是作为一个子程序供监控程序调用,因此各位显示器都扫过一遍之后,就返回监控程序。返回监控程序后,进行一些其它操作,再调用显示扫描程序。通过这种反复调用来实现LED数码显示器的动态扫描。
动态扫描显示在使用时必须反复调用显示子程序,若CPU要进行其它操作,那么显示子程序只能插入循环程序中,这往往束缚了CPU的工作,降低了CPU的工作效率。另外扫描显示电路中,显示器数目也不宜太多,一般在12个以内,否则会使人察觉出显示器在分时轮流显示。
这两种显示方式各有利弊:静态显示法虽然数据显示稳定,占用很少的CPU工作时间,但每个显示单元都需要单独的显示驱动电路,使用的电路硬件较多。动态扫描显示占用的CPU时间多,但使用的硬件少,能节省线路板空间及降低成本。
动态扫描显示由于外围元件少,节约了成本,充分发挥了软件的优势,因而在单片机系统中得到了广泛的应用。“工业生产节能时控器”也采用动态扫描显示。
4.2.5 电路设计
“工业生产节能时控器”电路原理见图4-8。共有4个按键,即on/off、up、left、set。这几个键的功能为:
on/off──定时1、2启动/撤消键;
up──输入数据递增键;
left──左移键;
set──工作模式设定键,共可设定“走时”、“调整走时时间”、“输入定时1”、“输入定时2” 四个模式。
图4-8 工业生产节能时控器电路图
“工业生产节能时控器”中,单片机AT89C2051只有15条I/O线,由于受I/O线数量限制,因此P1口中的Pl.0~P1.3既作为驱动4位LED数码管的数据输出一部分,同时也用作按键的输入。无疑,这种方式大大节约了硬件的I/O线,但也给编程者提出了更高的技术要求。关于按键输入的工作原理我们在后面的程序设计部分会加以详解。该机的继电器触点负载电流可达5A/220V,如欲控制更大功率的负载,可外接中间继电器、接触器或固态继电器(SSR)。正常工作时5V电源经R1对3.6V/60mA镍铬电池充电,当交流电源断电时,则由3.6V镍铬电池对单片机进行后备供电,保持走时准确,此时软件关闭LED数码管显示器显示,以降低3.6V镍铬电池的消耗。通过实际使用,发现“工业生产节能时控器”走时很准,每星期的误差在10秒之内。
4.3 程序设计
4.3.1 主程序设计
图4-9为主程序状态流程图。可见主程序只负责进行走时或调整时间的运算及显示,而判断按键输入则放在Tl定时中断 (10ms)服务子程序中。T0作为走时的基准被设置为l00ms定时中断。这种设计的优点是大大简化了主程序设计,并且CPU会定时关心键盘,只要定时中断时间足够短(如为几十ms),就不会漏掉每一次的按键输入。我们将显示走时、显示调整走时、显示调整定时1、显示调整定时2做成四个子程序,分别由set为0、1、2、3时散转后的“显示走时并判断定时1、2到否程序”、“显示调整走时程序”、“显示调整定时1程序”、“显示调整定时2程序”进行调用。为达到需输入的某位显示最亮(稳定显示),而其它三位显示较暗 (有闪烁感)的视觉效显,让三位需显示较暗的数码管每位点亮3ms,而显示最亮的那位数码管点亮36ms即可。
图4-9 主程序状态流程
4.3.2变量定义及初始化模块
uchar x[4]; /* 存放走时的数组*/
uchar y[4]={0,0,0,0}; /* 存放定时1的数组*/
uchar z[4]={0,0,0,0}; /* 存放定时2的数组*/
uchar deda=0; /*100mS计数单元清零*/
bit d_05s=0; /*0.5秒标志*/
bit o_f1=0; /*定时1启/停标志*/
bit o_f2=0; /*定时2启/停标志*/
uchar set=0; /*功能键标志*/
uchar h=0; //全局变量
uchar n=0;
uchar m=0;
uchar flag; //RAM区是否受干扰的标志
uchar left=0; //左移键标志
uchar sec=0; //走时单元sec、min、hour初始化
uchar min=0;
uchar hour=0;
uchar min1=0; //定时1单元min1、hour1初始化
uchar hour1=0;
uchar min2=0; //定时2单元min1、hour1初始化
uchar hour2=0;
uchar up=0; //递增键标志
bit flag1=0; //继电器输出控制的位标志,1时继电器吸合,0时继电器释放
/********************************************/
/*定时器T0初始化*/
void init_timer0()
{
TMOD=0x11; //定时器T0、T1方式1
TH0=-(50235/256); //装载100ms定时初值
TL0=-(50235%256);
IE=0x8a; //开CPU中断
TR0=1; //启动T0
}
/*定时器T1初始化*/
void init_timer1()
{
TH1=-(5000/256); ////装载10ms定时初值
TL1=-(5000%256);
}
4.3.3 判断按键输入的T1定时中断服务子程序模块
因为“工业生产节能时控器”的一个设计难点就是其按键输入部分,因此这里详细分析一下按键输入子程序模块。
学习过单片机技术的人都知道,单片机的按键输入一般可分为简单的独立式按键输入及行列式键盘输入两种。图4-10为简单的独立式键盘输入示意图,独立式键盘输入适合于按键输入不多的情况(<5个按键),具有占用口线较少、软件编写简单容易等特点。
图4-10 独立式键盘输入示意图
图4-11为行列式键盘输入示意图,列线接P1.0~P1.3,行线接P1.4~P1.7。行列式键盘输入适合于按键输入多的情况,如有16个按键输入,用简单按键输人用要占用2个输入口(共16位),而使用行列式键盘输入只需占用一个输入口(8位)。
图4-11 行列式键盘输入示意图
但行列式键盘输入软件编写较复杂,对初学者而言有一定的难度。在很多状态下,按键输人的值要同时要在LED数码管上显示出来。如一个按键设计为输入递增(加法)键,可以设计成每点按一下,数值递增加l,同时在LED数码管上显示出来;也可设计成持续按下时,数值以一定时间间隔(如0.3秒)累加。但是当欲输入值较大时(如三位LED数码管作输入显示时的输入值最大为999),则可能按下键的时间太长(最长达300秒),看来这种方式只适用于一位或至多两位数值(最大99)的输入。当然你也可多设几个键,每个键只负责一位数值的输入,但这样会占用较多的口线,浪费宝贵的硬件资源。
大家可能见到过,一些进口的温度控制器(如日本RKC INSTRUMENT INC.生 产的REX一C700温控器)的面板设计为:温度测量值用4位LED数码管显示,输入设定值显示也用4位LED数码管,输入按键只有4个,一个为“模式设定键”,一个为“左移键”,另两个为“加法键”、“减法键”。欲输入设定值(温控值)时,按一下“模式设定键”,程序进入设定状态,此时输入设定值显示的4位LED数码管中,个位显示最亮(稳定显示),而十、百、千位显示较暗(有闪烁感),说明可对个位进行输入。按下“加法键”或“减法键”,即可输入个位数的值;点按一下“左移键”,变为十位显示最亮,而个、百、千位显示较暗,说明可对十位进行输入。按下“加法键”或“减法键”,即可输入十位数的值;……这样可完成4位数的输入。完成输入后,再按一下“模式设定键”,程序即退出设定状态,进入工作运行。用这种输入方法,不仅输入4位数用4个键即可,再多位(5位至24位)的输入也用这4个键就够了。
“工业生产节能时控器”就是采用这种按键输入方式设计的。下面为按键输入子程序模块详解。
void zd1(void) interrupt 3 //声明定时1中断函数
{ //定时l中断函数开始
uchar i,j;i=P1;j=P3; /*定义i、j为无符号字符型局部变量,将当时的Pl口、P3口状态送i、j暂存*/
TH1=-(5000/256);
TL1=-(5000%256); //定时器Tl重新载入10ms初值
if(m==1)n++; /*如变量m等于1,则变量n递增。说明:m、n为整个程序开始时定义的元符号字符型全局变量*/
if(n>=30){n=0;m=0;} //如变量n大于等于30,则m、n清零
P3_7=0; //P3.7置0,准备读取按键输入
P1=0xff; //P1口置全1,准备读取按键输入
if(P1!=0xff) //如果P1口不等于全1,说明4个按键中有键按下
{ //进入if(P1!=0xff)语句范围
if(n==0)m=1; //如果n等于0,进入if(n==0)语句,m置l
{if(n==1) /*如果n等于1,迸入if(n==1)语句,同时进行下面的具体判断按键语句。作用效果为:开始时m、n均赋0,一旦有键按下,第一次中断产生时m赋l;第二次中断产生时n递增。当n等于1时(第二次中断产生)进入下面的具体判断按键语句。若持续按下键,则第三次中断产生~第三十一次中断产生时,程序不进入具体的判断按键语句过程(因这时n不等于1)。由于中断每10ms产生一次,这样可实现每0.31秒(31x10=0.31秒)进行一次加法或移位的操作,与人眼的视觉特性相吻合。
{ //进入具体判断按键语句范围*/
if(P1_0==0){set++;left=0;} /*如果P1.0等于0(即电路中的set键按下),变量set递增,变量left清0。说明:set、left是为了判断模式设定及左移而在整个程序开始时定义的无符号字符型全局变量*/
if(set>=4)set=0; /*如果set大于等于4,则set清0。说明:set值只能在0~3间变化,只有4种工作模式(走时及输出控制模式、走时调整模式、定时l调整模式、定时2调整模式)*/
if(set==1)flag=0x55; /*在set等于1时,向RAM区标志变量flag写入55H。说明,flag是在整个程序开始时定义的无符号字符型全局变量,用作判断RAM区是否受干扰的依据*/
if(P1_1==0)left++; //如果P1.1等于0(即电路中的left键按下),变量left递增
if(left>=4)left=0; /*如果left大于等于4,则left清0。说明:left值只能在0~3间 变化,LED数码管只有4位显示*/
if(P1_2==0){up++; /*如果P1.2等于0(即电路中的up键按下),进入if(P1_2==0)语句,变量up递增。说明:up是为了判断数值增量而在整个程序开始时定义的无符号字符型全局变量*/
switch(left) //随即进入switch(left)开关语句
{ //switch(left)开关语句开始
case 0:{if(up>=10)up=0;}break; /*left值为0时,如果up大于等于10,则up清0。随即退出。说明:电子钟的个位可在0~9之间调整*/
case 1:{if(up>=6)up=0;}break; /*left值为1时,如果up大于等于6,则up清0。随即退出。说明:电子钟的十位可在0~5之间调整*/
case 2:{if(up>=10)up=0;}break; /*left值为2时,如果up大于等于10,则up清0。随即退出。说明:电子钟的百位可在0~9之间调整*/
case 3:{if(up>=3)up=0;}break; /*left值为3时,如果up大于等于3,则up清0。随即退出。说明:电子钟的千位可在0~2之间调整*/
default:break; //:若left为其它值,也退出
} // switch(left)开关语句结束
} // if(P1_2==0)语句结束
if(P1_2==0){ /*如果P1.2等于0(即电路中的up键按下时),进入if(P1_2==0)语句,同时进入switch(set)开关语句*/
switch(set) // switch(set)开关语句开始
{case 0:break; //set值为0时,退出
case 1:x[left]=up;break; /*set值为1时,将此时up值送入X数组的第len位。随即退出。说明:X数组是显示走时缓存区*/
case 2:{y[left]=up;if(P1_3==0)o_f1=!o_f1;}break; /*set值为2时,将此时up值送入Y数组的第left位。若此时P1.3等于0(即电路中的on/off键按下),则定时1启停标志位o_f1取反(启动/关闭)。随即退出。说明:Y数组是定时l记忆缓存区。O_f1是为了判断定时1启动/关闭而在整个程序开始时定义的位标志*/
case 3:{z[left]=up;if(P1_3==0)o_f2=!o_f2;}break; /*set值为3时,将此时up值送入Z数组的第left位。若此时P1.3等于0(即电路中的on/off键按下),则定时2启停标志位o_f2取反(启动/关闭)。随即退出。说明:Z数组是定时2记忆缓存区。O_f2是为了判断定时2启动/关闭而在整个程序开始时定义的位标志*/
default:break;} //若set为其它值,也退出。switch(set)开关语句结束
} //if(P1_2)语句结束
else { //else语句开始
switch(set) /*又进入switch(set)开关语句。说明:上一个switch(set)开关语句是将按键产生的up值送入X、Y、Z数组存放,现在这个switch (set)开关语句是调出X、Y、Z数组内容至变量up,以便在原来的基础上递增。例如:原来的X[0]值为5,则 在调整时个位LED数码管显示就从5开始往上调,而不会产生从0或其它值开始上调的情况,适合人的一般直觉*/
{case 0:break; //set值为0时,退出
case 1:up=x[left];break; /*set值为1时,将此时X数组的第left位值送入变量up。随即退出*/
case 2:{up=y[left];if(P1_3==0)o_f1=!o_f1;}break; /*set值为2时,将此时Y数组的第left位值送入变量up。若此时P1.3等于0(即电路中的on/off键按下),则定时1启停标志位o_f1取反(启动/关闭)。随即退出*/
case 3:{up=z[left];if(P1_3==0)o_f2=!o_f2;}break; /*set值为3时,将此时Z数组的第left位值送入变量up。若此时P1.3等于0(即电路中的on/off键按下),则定时2启停标志位o_f2取反(启动/关闭)。随即退出*/
default:break;} //若set为其它值,也退出
} } //switch(set)开关语句结束。else语句结束
}} //if(n==0)语句结束。If(P1!=0xff)语句结束
P1=i;P3=j; /*本次定时中断快结束时,将暂存于I,j的当时Pl口、P3口状态还原*/
} //定时1中断服务子函数结束
4.3.4 完整代码
#include <AT89x051.H> //器件配置文件
#define uchar unsigned char //变量类型的宏定义
#define uint unsigned int
uchar DATA_7SEG[10]={0xfc,0x60,0xda,0xf2,0x66,
0xb6,0xbe,0xe0,0xfe,0xf6};/*0~9的数码管段码*/
uchar x[4]; /* 存放走时的数组*/
uchar y[4]={0,0,0,0}; /* 存放定时1的数组*/
uchar z[4]={0,0,0,0}; /* 存放定时2的数组*/
uchar act[4]={0xfe,0xfd,0xfb,0xf7}; /*四位数码管的位选码*/
void delay(uint k); //延时子函数声明
void conv(); //走时转换子函数声明
void p_out(); //判别定时到否子函数声明
void p_out1(); //判别定时到否子函数1声明
void t_adj(); //走时调整子函数声明
uchar deda=0; /*100mS计数单元清零*/
bit d_05s=0; /*0.5秒标志*/
bit o_f1=0; /*定时1启/停标志*/
bit o_f2=0; /*定时2启/停标志*/
uchar set=0; /*功能键标志*/
uchar h=0;
uchar n=0;
uchar m=0;
uchar flag;
uchar left=0;
uchar sec=0;
uchar min=0;
uchar hour=0;
uchar min1=0;
uchar hour1=0;
uchar min2=0;
uchar hour2=0;
uchar up=0;
bit flag1=0;
bit flag2=0;
/*显示走时*/
void time()
{
for(h=0;h<4;h++)
{P1=DATA_7SEG[x[h]]; //输出数码管的字形码
P3=act[h];p_out(); //输出数码管的位码。判断输出
if(P3_2==0){if(d_05s==1)P1_0=1;else P1_0=0;} /* 百位数码管点亮时,控制小数点
(秒点)闪烁*/
delay(2);} //每位数码管点亮2ms
if(o_f1==1){P1=0x01;P3_3=1;P3_2=1;P3_1=0;P3_0=1;}/* 若定时1启动,
点亮十位数码管小数点*/
else{P1=0x00;P3_3=1;P3_2=1;P3_1=1;P3_0=1;}p_out();/*否则十位数码管
小数点不亮*/
delay(2);
if(o_f2==1){P1=0x01;P3_3=1;P3_2=1;P3_1=1;P3_0=0;} /* 若定时2启动,
点亮个位数码管小数点*/
else{P1=0x00;P3_3=1;P3_2=1;P3_1=1;P3_0=1;}p_out();/*否则个位数码管
小数点不亮*/
delay(2);
}
/*定时器T0初始化*/
void init_timer0()
{
TMOD=0x11;
TH0=-(50235/256);
TL0=-(50235%256);
IE=0x8a;
TR0=1;
}
/*定时器T1初始化*/
void init_timer1()
{
TH1=-(5000/256);
TL1=-(5000%256);
}
/*延时k*1ms子函数*/
void delay(uint k)
{
uint i,j;
for(i=0;i<k;i++){
for(j=0;j<60;j++)
{;}}
}
/*100mS定时中断服务子函数*/
void zd0(void) interrupt 1
{
TH0=-(50235/256); //重装100ms定时初值
TL0=-(50235%256);
deda++; //100ms计时单元递增
}
/*10mS定时中断服务子函数*/
void zd1(void) interrupt 3
{uchar i,j;i=P1;j=P3;
TH1=-(5000/256);
TL1=-(5000%256);
if(m==1)n++;
if(n>=30){n=0;m=0;}
P3_7=0;
P1=0xff;
if(P1!=0xff)
{
if(n==0)m=1;
{if(n==1)
{
if(P1_0==0){set++;left=0;}
if(set>=4)set=0;
if(set==1)flag=0x55;
if(P1_1==0)left++;
if(left>=4)left=0;
if(P1_2==0){up++;
switch(left)
{
case 0:{if(up>=10)up=0;}break;
case 1:{if(up>=6)up=0;}break;
case 2:{if(up>=10)up=0;}break;
case 3:{if(up>=3)up=0;}break;
default:break;
}
}
if(P1_2==0){
switch(set)
{case 0:break;
case 1:x[left]=up;break;
case 2:{y[left]=up;if(P1_3==0)o_f1=!o_f1;}break;
case 3:{z[left]=up;if(P1_3==0)o_f2=!o_f2;}break;
default:break;}
}
else {
switch(set)
{case 0:break;
case 1:up=x[left];break;
case 2:{up=y[left];if(P1_3==0)o_f1=!o_f1;}break;
case 3:{up=z[left];if(P1_3==0)o_f2=!o_f2;}break;
default:break;}
}}
}}
P1=i;P3=j;
}
/*显示调整走时*/
void t_adj()
{p_out1();
for(h=0;h<4;h++) //四位数码管每位点亮3ms
{if(h==left)P1=0x00;
else {P1=DATA_7SEG[x[h]];
P3=act[h];p_out();
delay(3);}}
P1=DATA_7SEG[up];P3=act[left];
hour=x[3]*10+x[2];min=x[1]*10+x[0];
p_out();
delay(12); //需调整的数码管点亮12ms
if(o_f1==1){P1=0x01;P3_3=1;P3_2=1;P3_1=0;P3_0=1;} /*若定时1启动,
点亮十位数码管小数点*/
else{P1=0x00;P3_3=1;P3_2=1;P3_1=1;P3_0=1;}p_out();/*否则十位数码管
小数点不亮*/
delay(12); //需调整的数码管点亮12ms
if(o_f2==1){P1=0x01;P3_3=1;P3_2=1;P3_1=1;P3_0=0;} /*若定时2启动,
点亮个位数码管小数点*/
else{P1=0x00;P3_3=1;P3_2=1;P3_1=1;P3_0=1;}p_out();/*否则个位数码管
小数点不亮*/
delay(12); //需调整的数码管点亮12ms
}
/*显示调整定时1*/
void t1_adj()
{p_out1();
for(h=0;h<4;h++) //四位数码管每位点亮3ms
{if(h==left)P1=0x00;
else {P1=DATA_7SEG[y[h]];
P3=act[h];p_out();
delay(3);}}
P1=DATA_7SEG[up];P3=act[left]; //需调整的数码管点亮18ms
hour1=y[3]*10+y[2];min1=y[1]*10+y[0];
p_out();
delay(18);
if(o_f1==1){P1=0x01;P3_3=0;P3_2=1;P3_1=0;P3_0=1;} /*若定时1启动,
点亮十位数码管小数点*/
else{P1=0x01;P3_3=0;P3_2=1;P3_1=1;P3_0=1;} /*否则十位数码管
小数点不亮*/
p_out();
delay(18); //需调整的数码管点亮18ms
}
/*显示调整定时2*/
void t2_adj()
{p_out1();
for(h=0;h<4;h++) //四位数码管每位点亮3ms
{if(h==left)P1=0x00;
else {P1=DATA_7SEG[z[h]]; //需调整的数码管点亮18ms
P3=act[h];p_out();
delay(3);}}
P1=DATA_7SEG[up];P3=act[left];
hour2=z[3]*10+z[2];min2=z[1]*10+z[0];
p_out();
delay(18);
if(o_f2==1){P1=0x01;P3_3=1;P3_2=0;P3_1=1;P3_0=0;} /*若定时2启动,
点亮个位数码管小数点*/
else{P1=0x01;P3_3=1;P3_2=0;P3_1=1;P3_0=1;} /*否则个位数码管
小数点不亮*/
p_out();
delay(18); //需调整的数码管点亮18ms
}
/*时、分、秒计时单元转换*/
void conv()
{
if(deda<=5)d_05s=0; //每500ms,秒点取反
else d_05s=1;
if(deda>=10){sec++;deda=0;} //每1000ms,秒单元递增
if(sec>=60){min++;sec=0;} //每60s,分单元递增
if(min>=60){hour++;min=0;} //每60m,时单元递增
if(hour>=24){hour=0;} //每24h,时单元清零
}
/*判别定时到否子函数*/
void p_out()
{
if(o_f1==1) //若定时1启动
{if(hour==hour1) //定时1时间到
{if(min==min1)
{flag1=1;if(d_05s==1)P3_4=0;else P3_4=1;}}} //继电器输出标志置1,蜂鸣器间断鸣响
if(flag1==1) P3_5=0; //若输出标志为1,继电器吸合
if(o_f2==1) //若定时2启动
{if(hour==hour2) //定时2时间到
{if(min==min2)
{flag1=0;if(d_05s==1)P3_4=0;else P3_4=1;}}} //继电器输出标志置0,蜂鸣器间断鸣响
if(flag1==0) P3_5=1; //若输出标志为0,继电器释放
}
/*判别定时到否子函数1*/
void p_out1()
{
if(o_f1==1) //若定时1启动
{if(hour==hour1) //定时1时间到
{if(min==min1)
{flag1=1;}}} //继电器输出标志置1
if(flag1==1) P3_5=0; //若输出标志为1,继电器吸合
if(o_f2==1) //若定时2启动
{if(hour==hour2) //定时2时间到
{if(min==min2)
{flag1=0;}}} //继电器输出标志置0
if(flag1==0) P3_5=1; //若输出标志为0,继电器释放
}
/*主函数*/
void main()
{
init_timer0(); /*定时器T0初始化*/
init_timer1(); /*定时器T1初始化*/
while(1)
{
/*将显示缓冲数组内容移到时、分计时单元*/
hour=x[3]*10+x[2];min=x[1]*10+x[0];
hour1=y[3]*10+y[2];min1=y[1]*10+y[0];
hour2=z[3]*10+z[2];min2=z[1]*10+z[0];
conv(); /*时、分、秒及100mS单元转换*/
/*将转换后的时、分计时单元内容移到显示缓冲数组*/
x[3]=hour/10;x[2]=hour%10;x[1]=min/10;x[0]=min%10;
y[3]=hour1/10;y[2]=hour1%10;y[1]=min1/10;y[0]=min1%10;
z[3]=hour2/10;z[2]=hour2%10;z[1]=min2/10;z[0]=min2%10;
switch(set)
{
case 0:time();break;/*显示走时*/
case 1:t_adj();break;/*显示调整走时*/
case 2:t1_adj();break;/*显示调整定时1*/
case 3:t2_adj();break;/*显示调整定时2*/
default:break;
}
if(flag!=0x55) /*判断RAM受干扰标志,若受干扰,
则显示器闪烁告警*/
{uchar i;P3_4=1;P3_5=1;
for(i=0;i<100;i++) /*点亮显示器200mS*/
{
P1=DATA_7SEG[x[3]];P3=0xf7;delay(1);
P1=DATA_7SEG[x[2]];P3=0xfb;delay(1);
P1=DATA_7SEG[x[1]];P3=0xfd;delay(1);
P1=DATA_7SEG[x[0]];P3=0xfe;delay(1);
}
P1=0xff;P3=0xff;delay(400); /*熄灭显示器200mS*/
P3_7=0;
/*如有键按下达0.5S则退出闪烁状态同时掉电标志置55H。*/
if(P1!=0xff){
delay(10);if(P1_0==0)flag=0x55;F0:if(P1!=0xff)goto F0;
}P3_7=1;
}
else TR1=1;/*启动定时器1*/
}}
4.4 调试应用
4.4.1 功能调试
“工业生产节能时控器”由于元器件不多,一般不需作太多调整即可使用,但应确保元器件质量及焊接质量。通电时测工作电流,在继电器K释放、蜂鸣器不响、LED数码管显示器点亮的情况下,整机电流约为25mA。太大或太小都不正常。
输入设定过程:
1. 通电后四位LED数码管显示器开始闪烁,显示0000。原因是初次上电时,RAM标志flag为一随机数。按下set键1秒后,数字变为稳定显示,说明flag内已置入55H。
2. 点按set键后,个位显示最亮而其余三位数码管显示较暗(小数点均不亮),说明可进行校时,个位最亮说明此时可对个位进行调整,按下up键即可调整个位数字;点按left键,变成十位显示最亮而其余三位数码管显示较暗,说明可对十位进行调整,按下up键即可调整十位数字;……同理,我们可对百位和个位进行调整。
3. 点按set键后,个位显示最亮而其余三位数码管显示较暗(千位小数点点亮),说明可进行定时1调整,个位最亮说明此时可对个位进行调整,按下up键即可调整个位数字;点按left键,变成十位显示最亮而其余三位数码管显示较暗,说明可对十位进行调整,按下up键即可调整十位数字;……同理,我们可对百位和个位进行调整。定时1的时间调整完毕后,若点按on/off键,则十位数码管的小数点发亮,说明定时1启动,这样时间走到定时1设定值时,继电器会吸合,同时蜂鸣器鸣响1分钟。再点按on/off键,则十位数码管的小数点熄灭,说明定时1撤消。
4. 点按set键后,个位显示最亮而其余三位数码管显示较暗(百位小数点点亮),说明可进行定时2调整,个位最亮说明此时可对个位进行调整,按下up键即可调整个位数字;点按left键,变成十位显示最亮而其余三位数码管显示较暗,说明可对十位进行调整,按下up键即可调整十位数字;……同理,我们可对百位和个位进行调整。定时2的时间调整完毕后,若点按on/off键,则个位数码管的小数点发亮,说明定时2启动,这样时间走到定时2设定值时,继电器会释放,同时蜂鸣器鸣响1分钟。再点按on/off键,则个位数码管的小数点熄灭,说明定时2撤消。
5. 点按set键后,LED数码管显示器变为稳定显示,进入正常计时工作状态。
4.4.2 使用及维护
本机嵌入到生产设备中时,应安装固定好,同时做好防潮防油等工作。家庭使用时,应选配一个合适美观的外壳。
联系客服