基于51单片机电子时钟设计.doc
基于51单片机的电子时钟设计摘要本电子时钟以STC89C52单片机作为主控芯片,采用DS12C887时钟芯片,使用1602液晶作为显示输出。该时钟走时精确,具有闹钟设置,以及可同时显示时间、日期等多种功能。本文将详细介绍该电子时钟涉及到的一些基本原理,从硬件和软件两方面进行分析。【关键词】 STC89C52单片机 DS12C887时钟芯片 1602液晶 蜂鸣器目 录一、 绪论4 1.1 电子时钟功能4 1.2 设计方案4二、 硬件设计4 2.1 51单片机部分设计4 2.2 USB供电电路设计5 2.3 串行通信电路设计6 2.4 DS12C887时钟芯片电路的设计6 2.5 1602LCD液晶屏显示电路设计7 2.6 蜂鸣器电路设计8 2.7 按键调整电路设计8三、软件设计9 3.1 系统程序流程图设计9 3.2 程序设计11四、 心得体会22参考文献23一、 绪 论1.1电子时钟功能(1) 在1602液晶上显示年、月、日、星期、时、分、秒,并且按秒实时更新显示。(2) 具有闹铃设定即到时报警功能,报警响起时按任意键可取消报警。(3) 能够使用实验板上的按键随时调节各个参数,四个有效键分别为功能选择键、数值增大键、数值减小键和闹钟查看键。(4) 每次有键按下时,蜂鸣器都以短“滴”声报警。(5) 利用DS12C887自身掉电可继续走时的特性,该时钟可实现断电时间不停、再次上电时时间仍准确显示在液晶上的功能。1.2设计方案DS12C887时钟芯片+1602LCD液晶屏DS12C887时钟芯片功能丰富、价格适中,能够自动产生世纪、年、月、日、时、分、秒等时间信息,其内部含有世纪寄存器,从而利用硬件电路解决“千年”问题。DS12C887中自带锂电池,外部掉电时,其内部时间信息还能保持10年之久。1602LCD液晶屏可以输出2行,每行显示16个字符。1602LCD液晶屏显示清晰且不会闪烁,由于液晶屏是数字式的,因此和单片机系统的接口简单,操作方便。以STC89C52为主控芯片,DS12C887为时钟芯片,1602LCD液晶屏作为显示器。程序控制DS12C887时钟芯片实现小时、分、秒和年、月、日的计时,并在1602LCD液晶屏上显示出来。当时间走到程序所设定的时间时,蜂鸣器响起,起到闹钟功能。二、 硬 件 设 计 2.1 51单片机部分设计单片机部分如图21所示:以STC89C52单片机为核心,选用12MHZ的晶振,由于晶振的频率越高,单片机的运行速度就越快,考虑到单片机的运行速度快会导致对存储器的要求就会变高,因此12MHZ为最佳选择。外接电容的值虽然没有严格的要求,但是外接电容的大小会影响振荡器的频率高低、振荡器的稳定性和起振的快速性 ,因此选用30pF的电容作为起振电容。复位电路为按键高电平复位,当按键按下,RES端为高电平,当高电平持续4us的时间单片机即复位。2.2 USB供电电路设计USB供电电路如下图22所示:该电子时钟采用USB端口的方式为单片机供电,LPOW1为电源显示灯,当按键S5按下,电源显示灯LPOW1亮,表示给单片机供+5V电。2.3 串行通信电路设计串行通信电路如下图23所示:图中通过MAX232进行RS232电平与单片机TTL电平之间的转换,从而为单片机和上位机之间通信提供通道。通信电路的目的就是让通信双方的电平匹配,单片机用的是TTL电平,上位机的串口用的是RS232电平。TTL电平逻辑1的电压范围是+3.3V到+5V,逻辑0的电压范围是0到+3.3V;RS232电平的逻辑1的电压范围是15V到5V,逻辑0的电压范围是+5V到+15V。MAX232可以把输入的+5V电源电压变换成为RS232输出电平所需的+10V电压。所以采用此芯片接口的串行通信系统只需单一的+5V电源就可以了。对于没有+12V电源的场合,其适应性更强,2.4 DS12C887时钟芯片电路的设计时钟芯片电路如下图24所示:DS12C887时钟芯片共需要13条信号线,分别是并行数据地址复用线AD0AD7,AS,R/,DS和。MOT总线操作时序选择端。它有两种工作模式,当MOT接时,选用Motorola模式;当MOT接GND或悬空时,选用Intel模式。NC空引脚。AD0AD7复用地址数据总线。在总线周期的前半部分,出现在AD0AD7上的是地址信息,可用以选通DS12C887内的RAM,总线周期的后半部分出现在AD0AD7上的是数据信息。GND,系统电源接入端。当输入为+5V时,用户可以访问DS12C887内RAM中的数据,并可对其进行读/写操作;当的输入小于+4.25V时,禁止用户对内部RAM进行读/写操作,此时用户不能正确芯片内的时间信息;当的输入小于+3V时,DS12C887会自动的将电源切换到内部自带的锂电池上,以保证内部的电路能正常工作。芯片片选端。AS地址选通输入端。在进行读/写操作时,AS的上升沿将AD0AD7上出现的地址信息锁存到DS12C887上,而下一个下降沿清除AD0AD7上的地址信息,不论CS是否有效,DS12C887都将执行该操作。R/读/写输入端。该引接脚有两种工作模式,当MOT接时,R/工作在Motorola模式。此时该引脚的作用是区分读操作还是写操作,R/高电平时为读操作,R/为低电平时为写操作;当MOT接GND时,该引脚工作在Intel模式,此时该引脚为写允许输入,此信号的上升沿锁存数据。DS数据选择或读输入脚。该引脚有两种工作模式,当MOT接时,选用Motorola模式,此时,每个总线周期后一部分的DS为高电平,称为数据选通。在读操作中,DS的上升沿使DS12C887将内部数据送往总线AD0AD7上,以供外部读取。在写操作中,DS的下降沿将使总线AD0AD7上的数据锁存在DS12C887中。当MOT接GND时,选用Intel模式,此时该引脚是读允许输入引脚。芯片复位引脚。中断请求输出。用作处理器的中断申请输入。只要引起中断的状态位置位,并且相应中断使能位也置位,将一直保持低电平,处理器程序通常读取C存储器来清除引脚输出,引脚也会清除未处理的中断。没有中断发生时,为高阻状态,可将多个中断器件接到一条总线上,只要它们均为漏极开路输出即可。引脚为漏极开路输出,需要使用一个外接上拉电阻与相连。SQW方波输出引脚。当供电电压大于4.25V时,SQW引脚可输出方波。2.5 1602LCD液晶屏显示电路设计1602LCD液晶屏显示电路如下图25所示:1602液晶为5V电压驱动,带背光,可显示2行,每行16个字符,不能显示汉字,内置含128个字符的ASCII字符集字库,只有并行接口,无串行接口。接口说明如下:(1) 液晶1,2端为电源;15,16为背光电源;为防止直接加5V电压烧坏背光灯,在15脚串接一个1K电阻用于限流。(2) 液晶3端为液晶对比度调节端,通过一个10K电位器接地来调节液晶显示对比度。首次使用时,在液晶上电状态下,调节至液晶上面一行显示出黑色小格为止。(3) 液晶4端为向液晶控制器写数据/写命令选择端,接单片机的P3.5口。(4) 液晶5端为读/写选择端,因为我们不从液晶读取任何数据,只向其写入命令和显示数据,因此此端始终选择为写状态,即低电平接地。(5) 液晶6端为使能信号,是操作时必须的信号,接单片机的P3.4口。2.6 蜂鸣器电路设计蜂鸣器电路如下图26所示:蜂鸣器电路接在单片机的P2.3引脚上,当该引脚一个低电平,三极管导通,蜂鸣器发出声音作为闹铃。2.7 按键调整电路设计按键调整电路如下图27所示:四个独立键盘均采用查询方式,将按键的一端接地,另一端各接一根输入线直接与STC89C52的I/O口相连。当按键闭合时,相当于该I/O口通过按键与地相连,变成低电平,单片机通过检测I/O口的电平状态,即可识别出按下的键。通过四个键实现参数的调节,S1为功能选择键,S2为数值增大键,S3为数值减小键,S4为闹钟查看键。三、 软 件 设 计3.1 系统程序流程图设计流程图1:实验主程序流程图流程图2:定时中断程序流程图流程图3:调时功能流程图3.2 程序设计#include<reg52.h>#include<define.h>void delay(uint z) /延时函数 uint x,y; for(x=z;x>0;x-) for(y=110;y>0;y-);void di( ) /蜂鸣器报警声音 beep=0; delay(100); beep=1;void write_com(uchar com) /写液晶命令函数 rs=0; lcden=0; P0=com; delay(3); lcden=1; delay(3); lcden=0;void write_data(uchar data) /写液晶数据函数 rs=1; lcden=0; P0=data; delay(3); lcden=1; delay(3); lcden=0;void init( ) /初始化函数 uchar num; EA=1; /打开总中断 EX1=1; /开外部中断1 IT1=1; /设置负跳变沿触发中断 flag1=0; /变量初始化 t0_num=0; s1num=0; week=1; dula=0; /关闭数码管显示 wela=0; lcden=0; rd=0;/*以下几行在首次设置DS12C887时使用,以后不必再写入 write_ds(0x0A,0x20); /打开振荡器 write_ds(0x0B,0x26); /设置24小时模式,数据二进制格式,开启闹铃中断 set_time( ); /设置上电默认时间-*/ write_com(0x38); /1602液晶初始化 write_com(0x0c); write_com(0x06); write_com(0x01); write_com(0x80); for(num=0;num<15;num+) /写入液晶固定部分显示 write_data(tablenum); delay(1); write_com(0x80+0x40); for(num=0;num<11;num+) write_data(table1num); delay(1); void write_sfm(uchar add,char data) /1602液晶刷新时分秒函数,4为时,7为分,10为秒 char shi,ge; shi=data/10; ge=data%10; write_com(0x80+0x40+add); write_data(0x30+shi); write_data(0x30+ge);void write_nyr(uchar add,char data) /1602液晶刷新年月日函数,3为年,6为月,9为日 char shi,ge; shi=data/10; ge=data%10; write_com(0x80+add); write_data(0x30+shi); write_data(0x30+ge);void write_week(char we) /写液晶星期显示函数 write_com(0x80+12); switch(we) case 1: write_data('M');delay(5); write_data('O');delay(5); write_data('N'); break; case 2: write_data('T');delay(5); write_data('U');delay(5); write_data('E'); break; case 3: write_data('W');delay(5); write_data('E');delay(5); write_data('D'); break; case 4: write_data('T');delay(5); write_data('H');delay(5); write_data('U'); break; case 5: write_data('F');delay(5); write_data('R');delay(5); write_data('I'); break; case 6: write_data('S');delay(5); write_data('A');delay(5); write_data('T'); break; case 7: write_data('S');delay(5); write_data('U');delay(5); write_data('N'); break; void keyscan( )if(flag_ri=1) /这里用来取消闹钟报警,按任意键取消报警 if(s1=0)|(s2=0)|(s3=0)|(s4=0) delay(5); if(s1=0)|(s2=0)|(s3=0)|(s4=0) while(!(s1&&s2&&s3&s&s4);di( ); flag_ri=0; /清除报警标志 if(s1=0) /检测s1 delay(5); if(s1=0) s1num+; /记录按下次数 if(flag1=1) if(s1num=4) s1num=1; flag=1; while(!s1);di( ); switch(s1num) /光标闪烁点定位 case 1: write_com(0x80+0x40+10); write_com(0x0f); break; case 2: write_com(0x80+0x40+7); break; case 3: write_com(0x80+0x40+4); break; case 4: write_com(0x80+12); break; case 5: write_com(0x80+9); break; case 6: write_com(0x80+6); break; case 7: write_com(0x80+3); break; case 8: s1num=0; write_com(0x0c); flag=0; write_ds(0,miao); write_de(2,fen); write_ds(4,shi); write_ds(6,week); write_ds(7,day); write_ds(8,month); write_ds(9,year); break; if(s1num!=0) /只有当s1按下后,才检测s2和s3 if(s2=0) delay(1); if(s2=0) while(!s2);di( ); switch(s1num) /根据功能键次数调节相应数值 case 1: miao+; if(miao=60) miao=0; write_sfm(10,miao); write_com(0x80+0x40+10); break; case 2: fen+; if(fen=60) fen=0; write_sfm(7,fen); write_com(0x80+0x40+7); break; case 3: shi+; if(shi=24) shi=0; write_sfm(4,shi); write_com(0x80+0x40+4); break; case 4: week+; if(week=8) week=1; write_week(week); write_com(0x80+12); break; case 5: day+; if(day=32) day=1; write_nyr(9,day); write_com(0x80+9); break; case 6: month+; if(month=13) month=1; write_nyr(6,month); write_com(0x80+6); break; case 7: year+; if(year=100) year=0; write_nyr(3,year); write_com(0x80+3); break; if(s3=0) delay(1); if(s3=0) while(!s3);di( ); switch(s1num) /根据功能键次数调节相应数值 case 1: miao-; if(miao=-1) miao=59; write_sfm(10,miao); write_com(0x80+0x40+10); break; case 2: fen-; if(fen=-1) fen=59; write_sfm(7,fen); write_com(0x80+0x40+7); break; case 3: shi-; if(shi=-1) shi=23; write_sfm(4,shi); write_com(0x80+0x40+4); break; case 4: week-; if(week=0) week=7; write_week(week); write_com(0x80+12); break; case 5: day-; if(day=0) day=31; write_nyr(9,day); write_com(0x80+9); break; case 6: month-; if(month=0) month=12; write_nyr(6,month); write_com(0x80+6); break; case 7: year-; if(year=-1) year=99; write_nyr(3,year); write_com(0x80+3); break; if(s4=0) /检测s4 delay(5); if(s4=0) flag1=flag1; while(!s4);di( ); if(flag1=0) /退出闹钟设置时保存数值 flag=0; write_com(0x80+0x40); write_data(' '); write_data(' '); write_com(0x0c); write_ds(1,miao); write_ds(3,fen); write_ds(5,shi); else /进入闹钟设置 read_alarm( ); /读取原始数据 miao=amiao; /重新赋值用以按键调节 fen=afen; shi=ashi; write_com(0x80+0x40); write_data('R'); /显示标志 write_data('i'); write_com(0x80+0x40+3); write_sfm(4,ashi); /送液晶显示闹钟时间 write_sfm(7,afen); write_sfm(10,amiao); void write_ds(uchar add,uchar data) /写12C887函数 dscs=0; dsas=1; dsds=1; dsrw=1; P0=add; /先写地址 dsas=0; d