
摘 要
数字钟自从人类发明的那刻起,就伴随在我们左右,我们每天都要看时间,因此数字钟已经成为了我们生活中部可缺少的一部分。数字钟是一种用数字电路技术实现时、分、秒计时的钟表。与机械钟相比具有更高的准确性和直观性,具有更长的使用寿命,已得到广泛的使用。制作数字钟的方法有很多种,这次设计主要是基本ATC51单片机作为控制器,以LCD1602作为显示的数字钟;实现时间、日期、和星期的显示,整点报时,时间、日期、星期可调至任意值等功能。
关键词:数字钟;ATC51单片机;LCD1602;
目 录
1 任务提出与方案论证 6
1.1 设计要求 6
1.2 方案论证 6
2 总体设计 8
2.1总体硬件设计 8
2.2 软件设计 8
3 详细设计及仿真 9
3.1硬件系统设计 9
3.2软件系统设计 12
3.2系统仿真 12
4 总结 15
参考文献 16
1 任务提出与方案论证
数字钟是一种用数字电路技术实现时、分、秒计时的钟表。与机械钟相比具有更高的准确性和直观性,具有更长的使用寿命。数字钟的设计方法有许多种,例如可用中小规模集成电路组成电子钟,也可以利用专用的电子钟芯片配以显示电路及其所需要的外围电路组成电子钟,还可以利用单片机来实现电子钟等等。这些方法都各有其特点,其中利用单片机实现的电子钟具有编程灵活,以便于功能的扩展。因此设计数字钟是很有必要的。
1.1 设计要求
1、可以准确显示时间,年、月、日,时、分、秒
2、增加星期的显示功能;
3、时间和星期可以手动调节至任意值;
4、添加整点报时和闹铃的功能。
1.2 方案论证
方案一:用555振荡电路, 时间计数电路,校时电路和译码驱动电路组成。而分频器采用3片集成电路计数器74LS90,每片为1/10分频,3片级联则可获得所需的频率信号。而时间计数电路由74LS90组成,分为一个24进制电路和两个60进制电路。校时电路则由开关组成。原理图如图1-1所示:
图1-1 方案一的原理图
方案二:由ATC51单片机为控制器,用LCD1602作为显示器,显示时间日期用单片机的P0口作为数据口,P3口作为单片机的控制口,在P0口需加上上拉电阻;加上按键调节时间。方案图如图1-2所示:
图1-2 由ATC51控制的数字钟
在数字电路设计中,两种方案采用了不同的元器件,但都达到了数字时钟功能。通过比较方案二硬件电路少,实现起来简单,精度比较高,主要是写程序,经济效益比较好;因此,选择方案二。
2 总体设计
这次设计以51系列单片为核心,加上简单外围电路,配上软件就可以实现数字钟的功能。
2.1 硬件设计
总体硬件设计电路如图2-1所示:
图2-1 总体硬件图
上图就是所有硬件电路:它由ATC51单片机、晶振电路、复位电路、显示电路等构成。
2.2 软件件设计
总体软件设计的流程图2-2所示:
图2-2 软件流程图
3 详细设计及仿真
这次设计的主控制芯片为ATMEL的ATC51,它是一种高效微控制器。是一种带4K字节FLASH存储器的低电压、高性能CMOS 8位微处理器,俗称单片机。ATC2051是一种带2K字节闪存可编程可擦除只读存储器的单片机。该器件采用ATMEL高密度非易失存储器制造技术制造,与工业标准的MCS-51指令集和输出管脚相兼容。采用Proteus软件仿真。Proteus软件是英国Labcenter electronics公司出版的EDA工具软件。它不仅具有其它EDA工具软件的仿真功能,还能仿真单片机及外围器件。它是目前最好的仿真单片机及外围器件的工具。虽然目前国内推广刚起步,但已受到单片机爱好者、从事单片机教学的教师、致力于单片机开发应用的科技工作者的青睐。Proteus是世界上著名的EDA工具,从原理图布图、代码调试到单片机与外围电路协同仿真,一键切换到PCB设计,真正实现了从概念到产品的完整设计。是目前世界上唯一将电路仿真软件、PCB设计软件和虚拟模型仿真软件三合一的设计平台,其处理器模型支持8051、HC11、PIC10/12/16/18/24/30/DsPIC33、AVR、ARM、8086和MSP430等,2010年即将增加Cortex和DSP系列处理器,并持续增加其他系列处理器模型。在编译方面,它也支持IAR、Keil和MPLAB等多种编译器。
3.1硬件电路设计
3.1.1 晶振电路的设计
单片机晶振可以采用6MHz或者11.0592MHz,在正常工作的情况下可以采用更高频率的晶振,51单片机最小系统晶振的振荡频率直接影响单片机的处理速度,频率越大处理速度越快。单片机最小系统起振电容C2、C3一般采用15~33pF,并且电容离晶振越近越好,晶振离单片机越近越好4.P0口为开漏输出,作为输出口时需加上拉电阻,阻值一般为10k。设置为定时器模式时,加1计数器是对内部机器周期计数(1个机器周期等于12个振荡周期,即计数频率为晶振频率的1/12)。计数值N乘以机器周期Tcy就是定时时间,设置为计数器模式时,外部事件计数脉冲由T0或T1引脚输入到计数器。在每个机器周期的S5P2期间采样T0、T1引脚电平。当某周期采样到一高电平输入,而下一周期又采样到一低电平时,则计数器加1,更新的计数值在下一个机器周期的S3P1期间装入计数器。由于检测一个从1到0的下降沿需要2个机器周期,因此要求被采样的电平至少要维持一个机器周期。当晶振频率为12MHz时,最高计数频率不超过1/2MHz,即计数脉冲的周期要大于2 ms。晶振电路如图3-1所示:
图3-1 晶振电路
3.1.2 复位电路的设计
单片机复位电路的基本功能是:系统上电时提供复位信号,直至系统电源稳定后,撤销复位信号。为可靠起见,电源稳定后还要经一定的延时才撤销复位信号,以防电源开关或电源插头分-合过程中引起的抖动而影响复位。RST引脚是复位信号的输入端。复位信号是高电平有效,其有效时间应持续24个振荡周期(即二个机器周期)以上。本设计使用频率为12MHz的晶振,则复位信号持续时间应超过2us,才能完成复位操作。复位电路如图3-2所示:
图3-2 复位电路
3.1.3按键电路的设计
这次设计一共用了4个按键,一个是用控制的,按下一次的时刻时间停止计时,液晶上的光标在秒的位置上闪烁,此时按一下加键的话,秒会加一,按下减键的话,秒减一;当按下控制键第二次的时刻,光标停留在分得位置,此时按加减键可以调节分钟;第三次按下控制键会,光标停留在时的位置,按下加减键分别可以调节时的加减;同理,可以调节星期、年、月、日等;当按下退出设置键时,就会退出当前的设值,时间开始走;按键电路如图3-3所示:
图3-3 按键电路
3.1.4 液晶显示电路的设计
这次试验采用LCD1602显示 ,它是不带背光的14脚封装;1602液晶里面存储了所有的ASCALL码,可以直接调用来显示,它的引脚功能介绍如下:
| 引脚 | 符号 | 功能说明 |
| 1 | VSS | 一般接地 |
| 2 | VDD | 接电源(+5V) |
| 3 | V0 | 液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高(对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度)。 |
| 4 | RS | RS为寄存器选择,高电平1时选择数据寄存器、低电平0时选择指令寄存器。 |
| 5 | R/W | R/W为读写信号线,高电平(1)时进行读操作,低电平(0)时进行写操作。 |
| 6 | E | E(或EN)端为使能(enable)端,下降沿使能。 |
| 7 | DB0 | 低4位三态、 双向数据总线 0位(最低位) |
| 8 | DB1 | 低4位三态、 双向数据总线 1位 |
| 9 | DB2 | 低4位三态、 双向数据总线 2位 |
| 10 | DB3 | 低4位三态、 双向数据总线 3位 |
| 11 | DB4 | 高4位三态、 双向数据总线 4位 |
| 12 | DB5 | 高4位三态、 双向数据总线 5位 |
| 13 | DB6 | 高4位三态、 双向数据总线 6位 |
| 14 | DB7 | 高4位三态、 双向数据总线 7位(最高位)(也是busy flag) |
| RS | R/W | 操作说明 |
| 0 | 0 | 写入指令寄存器(清除屏等) |
| 0 | 1 | 读busy flag(DB7),以及读取位址计数器(DB0~DB6)值 |
| 1 | 0 | 写入数据寄存器(显示各字型等) |
| 1 | 1 | 从数据寄存器读取数据 |
图3-4 LCD1602硬件连接图
3.1.5蜂鸣器电路的设计
蜂鸣器是一种一体化结构的电子讯响器,采用直流电压供电,广泛应用于计算机、打印机、复印机、报警器、电子玩具、汽车电子设备、电话机、定时器等电子产品中作发声器件。这次试验用蜂蜜器来整点闹铃;每当时间到达整点时,蜂蜜器就会嘟嘟响几声。蜂鸣器的连接图如图3-5所示:
图3-5 蜂鸣器
3.2软件系统设计
软件设计主要包括四个模块的程序,LCD1602的驱动程序,时间显示程序,按键扫描程序,整点报时程序等。详细软件实现、程序清单见附录一。
3.3 系统的仿真
这次设计硬件采用Proteus软件仿真,软件程序用keil软件编译,生存HEX文件。Keil C51是美国Keil Software公司出品的51系列兼容单片机C语言软件开发系统,与汇编相比,C语言在功能上、结构性、可读性、可维护性上有明显的优势,因而易学易用。Keil提供了包括C编译器、宏汇编、连接器、库管理和一个功能强大的仿真调试器等在内的完整开发方案,通过一个集成开发环境(uVision)将这些部分组合在一起。Keil C51生成的目标代码效率非常之高,多数语句生成的汇编代码很紧凑,容易理解。在开发大型软件时更能体现高级语言的优势。用过汇编语言后再使用C来开发,体会更加深刻。 Keil C51软件提供丰富的库函数和功能强大的集成开发调试工具,全Windows界面。
3.3.1 软件仿真
程序通过keil软件编译无误,生成机器语言供单片机用,编译完成后系统提示如图3-6所示:
图3-6 软件编译后系统提示图
3.3.1 硬件件仿真
这次硬件仿真用Proteus仿真,把用keil编译器生成的HEX文件后,系统就可以运行,硬件仿真结果如图3-7所示:
图3-7 硬件仿真图
4 总结
基于单片机的数字钟设计精度比较高,因为在程序的执行过程中,指令不会影响定时器的正常计时,从而使数字钟的精度取决于单片机的机器周期电路,和定时器T0的硬件电路的精度。但是这次程序的设计方面还是有些地方有待改进的地方,定时器T0的中断服务子程序写得过于复杂,而且在里面有些延时的指令,这样会对数字钟的精度有一定的影响;这次设计由于时间有限,没有把定时闹铃的功能加上去,在以后的学习当中我会进一步的完善数字钟的功能。通过这次设计,对我来说收获颇大,让我掌握了LCD1602的操作方法,进一步熟练了51单片机C语言编程的方法。在软件的调试过程中,出现过很多错误,通过查找资料一步一步的解决了。
参考文献
【1】康华光,陈大钦. 电子技术基础—数字部分(第五版)[M]. 北京:高等教育出版社, 2005
【2】郭天祥.新概念51单片机C语言教程(第一版)[M].北京:电子工业出版社,2011
【3】张毅刚.新编MCS-51单片机应用设计(第二版) [M]哈尔滨: 哈尔滨工业大学出版社,2003
【4】陆应华,王理.电子系统设计教程(第二版)[M]北京:国防工业出版社,2008
附录1
程序清单:
#include #define uchar unsigned char #define uint unsigned int sbit rs=P3^5; sbit rd=P3^6; sbit en=P3^7; sbit key1=P3^0; sbit key2=P3^1; sbit key3=P3^2; sbit key4=P3^3; sbit Speak =P1^0; uchar code table1[]=" 2011-12-25 TUS"; uchar code table2[]=" W Z X 00:00:00"; uchar code table3[]="MONTUSWEDTHUFRISTASON"; uchar shi,fen,miao,key2num,keynum,num,count,nian,yue,ri,count; void delay(uint z) //延迟函数 { uint i,j; for(i=z;i>0;i--) for(j=110;j>0;j--); } void write_com(uchar com) //写命令函数 { rs=0; rd=0; P0=com; delay(5); en=0; delay(5); en=1; delay(5); en=0; } void write_date(uchar date) //写数据函数 { rs=1; rd=0; P0=date; delay(5); en=0; delay(5); en=1; delay(5); en=0; } void inti() //初始化函数 { TMOD=0x01; //装初值 TH0=(65536-50000)/256; TL0=(65536-50000)%256; EA=1; //开总中断 ET0=1; //开定时器0中断 TR0=1; //启动定时器 shi=0; fen=59; miao=57; Speak=0; key2num=5; ri=25; yue=12; nian=11; en=0; write_com(0x38); write_com(0x0c); write_com(0x06); write_com(0xd0); write_com(0x01); write_com(0x80); for(num=0;num<16;num++) { write_date(table1[num]); delay(5); } write_com(0x80+0x40); for(num=0;num<16;num++) { write_date(table2[num]); delay(5); } } void write_sfm(uchar add,uchar date) //时间显示函数 { uchar shi,ge; shi=date/10; ge=date%10; write_com(0xc0+add); write_date(0x30+shi); write_date(0x30+ge); } void write_nyr(uchar add,uchar date) //月日显示函数 { uchar shi,ge; shi=date/10; ge=date%10; write_com(0x80+add); write_date(0x30+shi); write_date(0x30+ge); } void write_n(uchar add,uchar date) //年显示函数 { uchar shi,ge; shi=date/10; ge=date%10; write_com(0x80+add); write_date(0x30+shi); write_date(0x30+ge); } void keyscan() //键盘扫描函数 { if(key1==0) { delay(10); if(key1==0) { while(!key1); TR0=0; keynum++; switch(keynum) { case 1: write_com(0xc0+14); write_com(0x0f); break; case 2: write_com(0xc0+11); break; case 3: write_com(0xc0+8); break; case 4: write_com(0x80+13); break; case 5: write_com(0x80+9); break; case 6: write_com(0x80+6); break; case 7: write_com(0x80+1); break; case 8: TR0=1; keynum=0; write_com(0x0c); break; default: break; } } } if(key4==0) { delay(10); if(key4==0) { while(!key4); TR0=1; keynum=0; write_com(0x0c); } } if(keynum==1) { if(key2==0) { delay(5); if(key2==0) { while(!key2); miao++; if(miao==60) //秒加1 { miao=0; } write_sfm(14,miao); } } write_com(0xc0+14); write_com(0x0f); } if(keynum==2) { if(key2==0) { delay(5); if(key2==0) { //分加1 while(!key2); fen++; if(fen==60) { fen=0; } write_sfm(11,fen); write_com(0xc0+11); } } } if(keynum==3) { if(key2==0) { delay(2); if(key2==0) { while(!key2); //时加1 shi++; if(shi==24) { shi=0; } write_sfm(8,shi); write_com(0xc0+8); } } } if(keynum==1) { if(key3==0) { delay(5); if(key3==0) { while(!key3); //秒减1 miao--; if(miao==-1) { miao=59; } write_sfm(14,miao); write_com(0xc0+14); } } } if(keynum==2) { if(key3==0) { delay(5); if(key3==0) { while(!key3); //分减1 fen--; if(fen==-1) { fen=59; } write_sfm(11,fen); write_com(0xc0+11); } } } if(keynum==3) { if(key3==0) { delay(5); if(key3==0) { while(!key3); //时减1 shi--; if(shi==-1) { shi=23; } write_sfm(8,shi); write_com(0xc0+8); } } } if(keynum==4) { if(key2==0) { delay(5); if(key2==0) { while(!key2); write_com(0x80+13); key2num++; write_date(table3[key2num]); //星期 加1 if(key2num==20) key2num=-1; write_com(0x80+14); key2num++; write_date(table3[key2num]); if(key2num==20) key2num=-1; write_com(0x80+15); key2num++; write_date(table3[key2num]); if(key2num==20) key2num=-1; write_com(0x80+13); } } } if(keynum==4) { if(key3==0) { delay(5); if(key3==0) { //星期减1 while(!key3); write_com(0x80+13); key2num=key2num-5; if(key2num<0) key2num=18; write_date(table3[key2num]); key2num++; if(key2num<0) key2num=18; write_com(0x80+14); write_date(table3[key2num]); key2num++; if(key2num<0) key2num=18; write_com(0x80+15); write_date(table3[key2num]); write_com(0x80+13); } } } if(keynum==5) { if(key2==0) { delay(5); if(key2==0) //日加1 { while(!key2); if(ri==30) ri=0; ri++; write_nyr(9,ri); write_com(0x80+9); } } } if(keynum==6) { if(key2==0) { delay(5); if(key2==0) //月加1 { while(!key2); if(yue==12) yue=0; yue++; write_nyr(6,yue); write_com(0x80+6); } } } if(keynum==7) { if(key2==0) { delay(5); if(key2==0) //年加1 { while(!key2); nian++; write_n(3,nian); write_com(0x80+1); } } } if(keynum==5) { if(key3==0) { delay(5); if(key3==0) //日减1 { while(!key3); ri--; if(ri==0) ri=30; write_nyr(9,ri); write_com(0x80+9); } } } if(keynum==6) { if(key3==0) { delay(5); if(key3==0) //月减1 { while(!key3); yue--; if(yue==0) ri=12; write_nyr(6,yue); write_com(0x80+6); } } } if(keynum==7) { if(key3==0) { delay(5); if(key3==0) //年减1 { while(!key3); nian--; write_nyr(3,nian); write_com(0x80+1); } } } } void main() //主函数 { inti(); write_sfm(8,shi); write_sfm(11,fen); write_sfm(14,miao); while(1) { keyscan(); } } void timer0() interrupt 1 //中断函数 { uint i; TH0=(65536-50000)/256; TL0=(65536-50000)%256; count++; if(count==20) { count=0; miao++; if(miao==60) { miao=0; fen++; if(fen==60) { fen=0; shi++; if(shi==24) { shi=0; //key2num=key2num+3; ri++; if(ri==30) { yue++; { if(yue==12) nian++; write_n(3,nian); } write_nyr(6,yue); } write_nyr(9,ri); write_com(0x80+13); key2num++; write_date(table3[key2num]); //星期 加1 if(key2num==20) key2num=-1; write_com(0x80+14); key2num++; write_date(table3[key2num]); if(key2num==20) key2num=-1; write_com(0x80+15); key2num++; write_date(table3[key2num]); if(key2num==20) key2num=-1; write_com(0x80+13); } write_sfm(8,shi); } write_sfm(11,fen); } write_sfm(14,miao); if(fen==0&miao<4) { for(i=6;i>0;i--) { Speak=1; delay(50); Speak=0; delay(50); } } } }
