(1)显示年月日,时分秒,星期与温度
(2)年月日,时分秒均可调,具备自动调节闰年的功能
(3)具有闹钟功能,且开关控制闹钟的开启与关闭
(4)具有整点报时功能
一、概述、设计思路
该设计方案是以ATC51单片机为核心,采用LCD1602液晶屏幕显示系统,辅以时钟模块,温度采集模块、日期提醒、键盘时间调整预设置等模块,所构建的数字时钟系统,能动态显示实时时钟的时、分、秒,数据显示(误差在30每天),对闹铃方式与温度调节模块进行了重点设计 实现SB0、SB1、SB2、SB3四个键实现时钟正常显示,调时,及闹钟时间设置。本系统设计大部分功能有软件来实现,使电路简单明了,系统稳定性也得大大提高。
二、系统组成与工作原理
1、工作原理:
本设计采用STCC51单片机作为本次课程设计的控制模块。单片机可把由DS18B20、DS1302、AT24C02中的数据利用软件来进行处理,从而把数据传输到显示模块,实现温度、日历和闹铃的显示。以LCD液晶显示器为显示模块,把单片机传来的的数据显示出来,并且显示多样化,在显示电路中,主要靠按键来实现各种显示要求的选择与切换。
2、总是设计框架图:
3、系统硬件概述:
该电路是由单片机STCC52为核心,具有在线编程功能,低功耗,时钟电路采用芯片1302,它是一款高性能、低功耗、自带RAM的实时时钟芯片,具有掉电自动保存功能。可自动对秒、分、时、日、周、月、年进行计数,具有闰年补偿功能,而且精度高位的RAM做为数据暂存区,工作电压2.5V~5.5V范围内,2.5V时耗电小于300nA。显示模块采用LCD1602显示屏,它内置192种字符,可显示32个符号或数字,清晰可见,而且功率消耗小寿命长抗干扰能力强。其工作电压为5v。温度采集模块采用温度传感器18b20。
三、单元电路的设计与分析
整个电子时钟系统电路可分为六大部分:处理单元(CPU)、复位电路部分、显示部分、键盘输入部分、温度采集部分。
1、MCS-51单片机
S51各引脚功能介绍:
VCC:
S51 电源正端输入,接+5V。
VSS:
电源地端。
XTAL1:
单芯片系统时钟的反相放大器输入端。
XTAL2:
系统时钟的反相放大器输出端,一般在设计上只要在 XTAL1 和 XTAL2 上接上一只石英振荡晶体系统就可以动作了,此外可以在两引脚与地之间加入一 20PF 的小电容,可以使系统更稳定,避免噪声干扰而死机。
RESET:
S51的重置引脚,高电平动作,当要对晶片重置时,只要对此引脚电平提升至高电平并保持两个机器周期以上的时间,ATS51便能完成系统重置的各项动作,使得内部特殊功能寄存器之内容均被设成已知状态,并且至地址0000H处开始读入程序代码而执行程序。
EA/Vpp:
"EA"为英文"External Access"的缩写,表示存取外部程序代码之意,低电平动作,也就是说当此引脚接低电平后,系统会取用外部的程序代码(存于外部EPROM中)来执行程序。因此在8031及8032中,EA引脚必须接低电平,因为其内部无程序存储器空间。如果是使用 8751 内部程序空间时,此引脚要接成高电平。此外,在将程序代码烧录至8751内部EPROM时,可以利用此引脚来输入21V的烧录高压(Vpp)。
ALE/PROG:
端口3的管脚设置:
P3.0:RXD,串行通信输入。
P3.1:TXD,串行通信输出。
P3.2:INT0,外部中断0输入。
P3.3:INT1,外部中断1输入。
P3.4:T0,计时计数器0输入。
P3.5:T1,计时计数器1输入。
P3.6:WR:外部数据存储器的写入信号。
P3.7:RD,外部数据存储器的读取信号。
2、复位电路
电路图如下:
上电自动复位是通过外部复位电路的电容充电来实现的。只要Vcc的上升时间不超过1ms,就可以实现自动上电复位。
3、时钟电路
时钟是单片机的心脏,单片机各功能部件的运行都是以时钟频率为基准,有条不紊的一拍一拍地工作。因此,时钟频率直接影响单片机的速度,时钟电路的质量也直接影响单片机系统的稳定性。常用的时钟电路有两种方式:一种是内部时钟方式,另一种为外部时钟方式。本文用的是内部时钟方式。
电路图如下:
3.2 时钟电路模块设计
DS1302内含有一个实时时钟/日历和31字节静态RAM,通过简单的串行接口与单片机进行通信。图中VCC1为后备电源,VCC2为主电源。DS1302由VCC1或VCC2两者中的较大者供电。所以在主电源关闭的情况下,也能保持时钟的连续运行。X1和X2是振荡源,外接32.768KHz晶振用来为芯片提供计时脉冲。RST是复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。DS1302
的硬件接线图如图所示:
3.3 温度传感采集模块设计
如图所示。采用数字式温度传感器DS18B20,它是数字式温度传感器,具有测量精度高,电路连接简单特点。
3.4 按键设计
电路由三个按键开关组成,分别与单片机的I/O相连,控制调整电路的加、减以及确定。
按键的开关状态通过一定的电路转换为高、低电平状态。按键闭合过程在相应的I/O端口形成一个负脉冲。闭合和释放过程都要经过一定的过程才能达到稳定,这一过程是处于高、低电平之间的一种不稳定状态,称为抖动。抖动持续时间的常长短与开关的机械特性有关,一般在5-10ms之间。为了避免CPU多次处理按键的一次闭合,应采用措施消除抖动。本文采用的是式按键,直接用I/O口线构成单个按键电路,每个按键占用一条I/O口线,每个按键的工作状态不会产生互相影响。
P1.0口表示功能移位键,按键选择要调整的时十位、时个位、分十位或分个位。
P1.1口表示数字“+“键,按一下则对应的数字加1。
P1.2口表示数字“-”键,按一下则对应的数字减1。
P1.3口表示时间表的切换,程序默认为日常时间表,当按下该开关,使输入为低电平时,表示当前执行的是考试时间表,并有绿发光二极管显示。再按键,使键抬起,输入维高电平时,表示当前执行的是日常作息时间表,用红发光二级管显示。
4、显示电路
采用LCD显示,LCD显示具有丰富多样性,灵活性,电路简单、易于控制而且功耗小,对于信息量多的系统,是比较适合的,LCD液晶显示模块采用LCD1602型号,具有很低的功耗,正常工作室电流仅2.0mA/5.0V。通过编程实现总动关闭屏幕能够更有效地降低功耗。LCD1602分两行显示,每行可现实多达16个字符,其内部的字符发生器已经存储了160个不同的点阵字符图形,通过内部指令可实现对其显示多样的控制。
四、程序设计
#include #include"DS18B20_3.H" #define uint unsigned int #define uchar unsigned char uchar a,b,miao,shi,fen,ri,yue,nian,week,flag,key1n,temp,miao1,shi1=12,fen1=1,miao1=0,clock=0 ; //flag用于读取头文件中的温度值,和显示温度值 #define yh 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80) #define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40) //液晶屏的与C51之间的引脚连接定义(显示数据线接C51的P0口) sbit rs=P2^0; sbit en=P2^2; sbit rw=P2^1; //如果硬件上rw接地,就不用写这句和后面的rw=0了 sbit led=P2^6; //LCD背光开关 //DS1302时钟芯片与C51之间的引脚连接定义 sbit IO=P1^1; sbit SCLK=P1^0; sbit RST=P1^2; sbit CLO=P1^4; sbit ACC0=ACC^0; sbit ACC7=ACC^7; /************************************************************ ACC累加器=A ACC.0=E0H ACC.0就是ACC的第0位。Acc可以位寻址。 累加器ACC是一个8位的存储单元,是用来放数据的。但是,这个存储单元有其特殊的地位, 是单片机中一个非常关键的单元,很多运算都要通过ACC来进行。以后在学习指令时, 常用A来表示累加器。但有一些地方例外,比如在PUSH指令中,就必须用ACC这样的名字。 一般的说法,A代表了累加器中的内容、而ACC代表的是累加器的地址。 ***************************************************************/ //校时按键与C51的引脚连接定义 sbit key1=P1^5; //设置键 sbit key2=P1^6; //加键 sbit key3=P1^7; //减键 sbit buzzer=P1^3;//蜂鸣器,通过三极管9012驱动,端口低电平响 /**************************************************************/ uchar code tab1[]={"20 - - "}; //年显示的固定字符 uchar code tab2[]={" : : "};//时间显示的固定字符 uchar code tab3[]={" Congratulation!The New World is coming! "};//开机动画 //延时函数,后面经常调用 void delay(uint xms)//延时函数,有参函数 {uint x,y; for(x=xms;x>0;x--) for(y=110;y>0;y--);} void delay1ms(int i)//1毫秒延时 { int j,k; while(i--) for(j=76;j>1;j--); for(k=29;k>1;k--); } /********液晶写入指令函数与写入数据函数,以后可调用**************/ /*在这个程序中,液晶写入有关函数会在DS1302的函数中调用,所以液晶程序要放在前面*/ write_1602com(uchar com)//****液晶写入指令函数**** { rs=0;//数据/指令选择置为指令 rw=0; //读写选择置为写 P0=com;//送入数据 delay(1); en=1;//拉高使能端,为制造有效的下降沿做准备 delay(1); en=0;//en由高变低,产生下降沿,液晶执行命令} write_1602dat(uchar dat)//***液晶写入数据函数**** { rs=1;//数据/指令选择置为数据 rw=0; //读写选择置为写 P0=dat;//送入数据 delay(1); en=1; //en置高电平,为制造下降沿做准备 delay(1); en=0; //en由高变低,产生下降沿,液晶执行命令} lcd_init()//***液晶初始化函数**** { uchar j; write_1602com(0x0f|0x08); for(a=0;a<42;a++) write_1602dat(tab3[a]); j=42; while(j--) { write_1602com(0x1a);//循环左移 delay(700); } write_1602com(0x01); delay(10); write_1602com(0x38);//设置液晶工作模式,意思:16*2行显示,5*7点阵,8位数据 write_1602com(0x0c);//开显示不显示光标 write_1602com(0x06);//整屏不移动,光标自动右移 write_1602com(0x01);//清显示 /***开机动画显示hello welcome dianzizhong****/ write_1602com(yh+1);//日历显示固定符号从第一行第1个位置之后开始显示 for(a=0;a<14;a++) {write_1602dat(tab1[a]);//向液晶屏写日历显示的固定符号部分 //delay(3); }write_1602com(er+1);//时间显示固定符号写入位置,从第1个位置后开始显示 for(a=0;a<8;a++){write_1602dat(tab2[a]);//写显示时间固定符号,两个冒号 //delay(3); } write_1602com(er+0); write_1602dat(0x20); } /***************DS1302有关子函数********************/ void write_byte(uchar dat)//写一个字节 { ACC=dat; RST=1; for(a=8;a>0;a--) { IO=ACC0; SCLK=0; SCLK=1; ACC=ACC>>1; } } uchar read_byte()//读一个字节 {RST=1; for(a=8;a>0;a--) { ACC7=IO; SCLK=1; SCLK=0; ACC=ACC>>1; } return (ACC); } //---------------------------------------- void write_1302(uchar add,uchar dat)//向1302芯片写函数,指定写入地址,数据 { RST=0; SCLK=0; RST=1; write_byte(add); write_byte(dat); SCLK=1; RST=0; } uchar read_1302(uchar add)//从1302读数据函数,指定读取数据来源地址 { uchar temp; RST=0; SCLK=0; RST=1; write_byte(add); temp=read_byte(); SCLK=1; RST=0; return(temp); }uchar BCD_Decimal(uchar bcd)//BCD码转十进制函数,输入BCD,返回十进制 { uchar Decimal; Decimal=bcd>>4; return(Decimal=Decimal*10+(bcd&=0x0F));} //-------------------------------------- void ds1302_init() //1302芯片初始化子函数(2012-12-22,00:00:00,week6) {RST=0; SCLK=0; write_1302(0x8e,0x00); //允许写,禁止写保护 write_1302(0x80,0x00); //向DS1302内写秒寄存器80H写入初始秒数据00 write_1302(0x82,0x00);//向DS1302内写分寄存器82H写入初始分数据00 write_1302(0x84,0x00);//向DS1302内写小时寄存器84H写入初始小时数据00 write_1302(0x8a,0x06);//向DS1302内写周寄存器8aH写入初始周数据6 write_1302(0x86,0x22);//向DS1302内写日期寄存器86H写入初始日期数据22 write_1302(0x88,0x12);//向DS1302内写月份寄存器88H写入初始月份数据12 write_1302(0x8c,0x12);//向DS1302内写年份寄存器8cH写入初始年份数据12 write_1302(0x8e,0x80); //打开写保护 }//------------------------------------ //温度显示子函数 void write_temp(uchar add,uchar dat)//向LCD写温度数据,并指定显示位置 {uchar gw,sw,bw; if(dat>=0&&dat<=128) {gw=dat%10;//取得个位数字 sw=dat%100/10;//取得十位数字 bw=-5 ;//取得百位数字 } else { dat=256-dat; gw=dat%10;//取得个位数字 sw=dat%100/10;//取得十位数字 bw=-3; //0x30-3表示为负号 } write_1602com(er+add);//er是头文件规定的值0x80+0x40 write_1602dat(0x30+bw);//数字+30得到该数字的LCD1602显示码 write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码 write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码 write_1602dat(0xdf);//显示温度的小圆圈符号,0xdf是液晶屏字符库的该符号地址码 write_1602dat(0x43); //显示"C"符号,0x43是液晶屏字符库里大写C的地址码 }//------------------------------------ //时分秒显示子函数 void write_sfm(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数 { uchar gw,sw; gw=dat%10;//取得个位数字 sw=dat/10;//取得十位数字 write_1602com(er+add);//er是头文件规定的值0x80+0x40 write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码 write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码 } //------------------------------------- //年月日显示子函数 void write_nyr(uchar add,uchar dat)//向LCD写年月日,有显示位置加数、显示数据,两个参数 { uchar gw,sw; gw=dat%10;//取得个位数字 sw=dat/10;//取得十位数字 write_1602com(yh+add);//设定显示位置为第一个位置+add write_1602dat(0x30+sw);//数字+30得到该数字的LCD1602显示码 write_1602dat(0x30+gw);//数字+30得到该数字的LCD1602显示码 } //------------------------------------------- void write_week(uchar week)//写星期函数 { write_1602com(yh+0x0c);//星期字符的显示位置 switch(week) { case 1:write_1602dat('M');//星期数为1时,显示 write_1602dat('O'); write_1602dat('N'); break; case 2:write_1602dat('T');//星期数据为2时显示 write_1602dat('U'); write_1602dat('E'); break; case 3:write_1602dat('W');//星期数据为3时显示 write_1602dat('E'); write_1602dat('D'); break; case 4:write_1602dat('T');//星期数据为4是显示 write_1602dat('H'); write_1602dat('U'); break; case 5:write_1602dat('F');//星期数据为5时显示 write_1602dat('R'); write_1602dat('I'); break; case 6:write_1602dat('S');//星期数据为6时显示 write_1602dat('T'); write_1602dat('A'); break; case 7:write_1602dat('S');//星期数据为7时显示 write_1602dat('U'); write_1602dat('N'); break;}} //****************键盘扫描有关函数********************** void keyscan() { if(key1==0)//---------------key1为功能键(设置键)-------------------- { delay(9);//延时,用于消抖动 if(key1==0)//延时后再次确认按键按下 { buzzer=1;//蜂鸣器短响一次 delay(20); buzzer=0; while(!key1); key1n++; if(key1n==12) key1n=1;//设置按键共有秒、分、时、星期、日、月、年、返回,8个功能循环 switch(key1n) { case 1: TR0=0;//关闭定时器 //TR1=0; write_1602com(er+0x08);//设置按键按动一次,秒位置显示光标 write_1602com(0x0f);//设置光标为闪烁 temp=(miao)/10*16+(miao)%10;//秒数据写入DS1302 write_1302(0x8e,0x00); write_1302(0x80,0x80|temp);//miao write_1302(0x8e,0x80); break; case 2: write_1602com(er+5);//按2次fen位置显示光标 //write_1602com(0x0f); break; case 3: write_1602com(er+2);//按动3次,shi //write_1602com(0x0f); break; case 4: write_1602com(yh+0x0e);//按动4次,week //write_1602com(0x0f); break; case 5: write_1602com(yh+0x0a);//按动5次,ri //write_1602com(0x0f); break; case 6: write_1602com(yh+0x07);//按动6次,yue //write_1602com(0x0f); break; case 7: write_1602com(yh+0x04);//按动7次,nian //write_1602com(0x0f); break; case 8: write_1602com(er+0); write_1602dat(0x53); write_1602com(er+0); break; case 9: write_1602com(er+0); write_1602dat(0x4d); write_1602com(er+0); break; case 10:write_1602com(er+0); write_1602dat(0x48); write_1602com(er+0); break; case 11:write_1602com(er+0); write_1602dat(0x20); write_1602com(0x0c);//按动到第8次,设置光标不闪烁 TR0=1;//打开定时器 temp=(miao)/10*16+(miao)%10; write_1302(0x8e,0x00); write_1302(0x80,0x00|temp);//miao数据写入DS1302 write_1302(0x8e,0x80); break; }}} //------------------------------加键key2---------------------------- if(key1n!=0)//当key1按下以下。再按以下键才有效(按键次数不等于零) {if(key2==0) //上调键 { delay(10); if(key2==0) { buzzer=1;//蜂鸣器短响一次 delay(20); buzzer=0; while(!key2); switch(key1n) { case 1:miao++;//设置键按动1次,调秒 if(miao==60) miao=0;//秒超过59,再加1,就归零 write_sfm(0x07,miao);//令LCD在正确位置显示"加"设定好的秒数 temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00); //允许写,禁止写保护 write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码 write_1302(0x8e,0x80); //打开写保护 write_1602com(er+0x08);//因为设置液晶的模式是写入数据后,光标自动右移,所以要指定返回 //write_1602com(0x0b); break; case 2:fen++; if(fen==60) fen=0; write_sfm(0x04,fen);//令LCD在正确位置显示"加"设定好的分数据 temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 3:shi++; if(shi==24) shi=0; write_sfm(1,shi);//令LCD在正确的位置显示"加"设定好的小时数据 temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 4:week++; if(week==8) week=1; write_1602com(yh+0x0C);//指定'加'后的周数据显示位置 write_week(week);//指定周数据显示内容 temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 5:ri++; switch(yue) { case 1:case 3:case 5:case 7:case 8:case 10:case 12: if(ri>31) ri=1; break; case 2: if(nian%4==0||nian%400==0) { if(ri>29) ri=1; } else { if(ri>28) ri=1;}break; case 4:case 6:case 9:case 11: ri++; if(ri>30) ri=1; break; } write_nyr(9,ri);//令LCD在正确的位置显示"加"设定好的日期数据 temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 6:yue++; switch(ri) { case 31: if(yue==2|yue==4|yue==6|yue==9|yue==11) {yue++;} else {if(yue>=13) yue=1;} break; case 30: if(yue==2){yue++;} else {if(yue>=13) yue=1;} case 29: if(nian%4==0||nian%400==0) {if(yue>=13) {yue=1;}} else{if(yue==2){yue++;} else if(yue>=13) {yue=1;}} break; } if(yue>=13) yue=1; write_nyr(6,yue);//令LCD在正确的位置显示"加"设定好的月份数据 temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 7:nian++; if(nian==100) nian=0; write_nyr(3,nian);//令LCD在正确的位置显示"加"设定好的年份数据 temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 8: write_1602com(er+8); //设置闹钟的秒定时 miao1++; if(miao1==60) miao1=0; write_sfm(0x07,miao1);//令LCD在正确位置显示"加"设定好秒的数据 write_1602com(er+8);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 9: write_1602com(er+5); //设置闹钟的分钟定时 fen1++; if(fen1==60) fen1=0; write_sfm(0x04,fen1);//令LCD在正确位置显示"加"设定好的分数据 write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 10: write_1602com(er+2); //设置闹钟的小时定时 shi1++; if(shi1==24) shi1=0; write_sfm(0x01,shi1);//令LCD在正确的位置显示"加"设定好的小时数据 write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; } } } //------------------减键key3,各句功能参照'加键'注释--------------- if(key3==0) { delay(10);//调延时,消抖动 if(key3==0) { buzzer=1;//蜂鸣器短响一次 delay(20); buzzer=0; while(!key3); switch(key1n) { case 1:miao--; if(miao==-1) miao=59;//秒数据减到-1时自动变成59 write_sfm(0x07,miao);//在LCD的正确位置显示改变后新的秒数 temp=(miao)/10*16+(miao)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00); //允许写,禁止写保护 write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码 write_1302(0x8e,0x80); //打开写保护 write_1602com(er+0x08);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 //write_1602com(0x0b); break; case 2:fen--; if(fen==-1) fen=59; write_sfm(4,fen); temp=(fen)/10*16+(fen)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x82,temp);//向DS1302内写分寄存器82H写入调整后的分数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 3:shi--; if(shi==-1) shi=23; write_sfm(1,shi); temp=(shi)/10*16+(shi)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x84,temp);//向DS1302内写小时寄存器84H写入调整后的小时数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 4:week--; if(week==0) week=7; write_1602com(yh+0x0C);//指定'加'后的周数据显示位置 write_week(week);//指定周数据显示内容 temp=(week)/10*16+(week)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x8a,temp);//向DS1302内写周寄存器8aH写入调整后的周数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+0x0e);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 5:ri--; switch(yue) { case 1:case 3:case 5:case 7:case 8:case 10:case 12: if(ri==0) ri=31; break; case 2: if(nian%4==0||nian%400==0) { if(ri==0) ri=29; } else { if(ri==0) ri=28; }break; case 4:case 6:case 9:case 11: if(ri==0) ri=30; break;} write_nyr(9,ri); temp=(ri)/10*16+(ri)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x86,temp);//向DS1302内写日期寄存器86H写入调整后的日期数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+10);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 6:yue--; switch(ri) { case 31: if(yue==2|yue==4|yue==6|yue==9|yue==11) {yue--;} else {if(yue==0) yue=12;} break; case 30: if(yue==2){yue--;} else {if(yue==0) yue=12;} case 29: if(nian%4==0||nian%400==0) {if(yue==0) {yue=12;}} else{if(yue==2){yue--;} else if(yue==0) {yue=12;}} break; } if(yue==0) yue=12; write_nyr(6,yue); temp=(yue)/10*16+(yue)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x88,temp);//向DS1302内写月份寄存器88H写入调整后的月份数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+7);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 7:nian--; if(nian==-1) nian=99; write_nyr(3,nian); temp=(nian)/10*16+(nian)%10;//十进制转换成DS1302要求的DCB码 write_1302(0x8e,0x00);//允许写,禁止写保护 write_1302(0x8c,temp);//向DS1302内写年份寄存器8cH写入调整后的年份数据BCD码 write_1302(0x8e,0x80);//打开写保护 write_1602com(yh+4);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; case 8: write_1602com(er+8); //设置闹钟的秒定时 miao1--; if(miao1==-1) miao1=59; write_sfm(0x07,miao1);//令LCD在正确位置显示"加"设定好秒的数据 write_1602com(er+8);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 9: write_1602com(er+5); //设置闹钟的分钟定时 fen1--; if(fen1==-1) fen1=59; write_sfm(0x04,fen1);//令LCD在正确位置显示"加"设定好的分数据 write_1602com(er+5);//因为设置液晶的模式是写入数据后,指针自动加一,在这里是写回原来的位置 break; case 10: write_1602com(er+2); //设置闹钟的小时定时 shi1--; if(shi1==-1) shi1=23; write_sfm(0x01,shi1);//令LCD在正确的位置显示"加"设定好的小时数据 write_1602com(er+2);//因为设置液晶的模式是写入数据后,指针自动加一,所以需要光标回位 break; } } } } } //定时器0初始化程序 void init() //定时器、计数器设置函数 { TMOD=0x11; //指定定时/计数器的工作方式为1 TH0=0; //定时器T0的高四位=0 TL0=0; //定时器T0的低四位=0 EA=1; //系统允许有开放的中断 ET0=1; //允许T0中断 TR0=1; //开启中断,启动定时器 } //*******************主函数************************** //*************************************************** void main() { buzzer=0; lcd_init(); //调用液晶屏初始化子函数 ds1302_init(); //调用DS1302时钟的初始化子函数 init(); //调用定时计数器的设置子函数 led=0; //打开LCD的背光电源 buzzer=1;//蜂鸣器长响一次 delay(80); buzzer=0; while(1) //无限循环下面的语句: { keyscan(); //调用键盘扫描子函数 } } /*************通过定时中断实现定是独处并显示数据******************/ void timer0() interrupt 1 //取得并显示日历和时间 { //Init_DS18B20();//温度传感器DS18b2初始化子函数,在头文件中 flag=ReadTemperature();//将18b2头文件运行返回的函数结果送到变量FLAG中,用于显示 //读取秒时分周日月年七个数据(DS1302的读寄存器与写寄存器不一样): miao = BCD_Decimal(read_1302(0x81)); fen = BCD_Decimal(read_1302(0x83)); shi = BCD_Decimal(read_1302(0x85)); ri = BCD_Decimal(read_1302(0x87)); yue = BCD_Decimal(read_1302(0x)); nian=BCD_Decimal(read_1302(0x8d)); week=BCD_Decimal(read_1302(0x8b)); //显示温度、秒、时、分数据: write_temp(11,flag);//显示温度,从第二行第12个字符后开始显示 write_sfm(7,miao);//秒,从第二行第8个字后开始显示(调用时分秒显示子函数) write_sfm(4,fen);//分,从第二行第5个字符后开始显示 write_sfm(1,shi);//小时,从第二行第2个字符后开始显示 //显示日、月、年数据: write_nyr(9,ri);//日期,从第二行第9个字符后开始显示 write_nyr(6,yue);//月份,从第二行第6个字符后开始显示 write_nyr(3,nian);//年,从第二行第3个字符后开始显示 write_week(week); /***********整点报时程序************/ if(fen==0&&miao==0) if(shi<22&&shi>6 ) { buzzer=1;//蜂鸣器短响一次 delay(20); buzzer=0;} /**************闹钟程序: 将暂停键按下停止蜂鸣********************/ if(CLO==0) /*按下p1.3停止蜂鸣*/ { clock=0; return;} if(shi1==shi&&fen1==fen&&miao1==miao) { clock=1; } if(clock==1) {buzzer=1;//蜂鸣器短响一次 delay(20); buzzer=0; }} 5、测试结果分析与结论 Protel总电路图: 设计电路的PROTEU仿真图: 整体PCB版图如下: 六、调试所需的仪器设备 基本测试仪器:万用电表、秒表、电源、温度表 七、设计的难点和可能出现的问题 1、设计难点: (1):温度设计模块的设计, (2):LCD显示模块的设计 (3):闹铃设计模块的实现设计 (4):单片机个功能模块的综合设计 (5):电路总体设计,元器件选择及焊接,调试 2、可能出现的问题: (1):温度显示,与实际偏差大 (2):LCD显示模块的功能显示不正常 (3):闹铃设计与其他时间功能设计冲突 (4):软件程序设计功能不完善,系统无法正常运转 八、预期达到的性能指标 1、预期达到的性能指标: 能动态显示实时时钟的时、分、秒,数据显示(误差在30每天),对闹铃方式与温度调节模块进行了重点设计 实现SB0、SB1、SB2、SB3四个键实现时钟正常显示,调时,及闹钟时间设置。 2、多功能数字时钟使用说明: A:(调整切换键)进行时间、闹铃以及生日的设置,调整内容切换。 B: (调整加减/生日的设定)在时间显示界面中按A键后,进入时间调整。按B键对应闪烁内容加1;在时间显示界面中长按改建进入圣体信息设定,按A键选择调整内容,按B键加1,C键减1,D减退出设定,当设定生日到时,当日七点整第一次响铃提醒,以后每隔一小时整点提醒,直至当日晚上22点后最后一次提醒。 C:(调整减/日期提醒)在时间显示界面中按A键后,进入时间调整。按C键对应闪烁内同加1;在时间显示界面中常按A键后,进入日期提醒查看。 D (退出键/闹铃设定)在各种设定模式(时间、生日、闹铃)下,按该键退出设定;若在时间显示界面中短按键后,则进入闹铃、生日信息查看;长按则进行闹铃设定。 E:(屏幕背光键):按下该键不放则屏幕背光。调整左上方的蓝色变阻器课调整LCD显示灰度;调整右上方的蓝色编著器可调整LCD背光的亮度。(注:系统再不进行日和操作时,10秒后自动关闭屏幕,按任意键返回) F:(复位键)按下该键系统复位,系统从头开始执行程序,如果与长按可按下改嫁进行系统复位,复位不会造成时间、生日、和闹铃等细细的丢失。