基于DS1302和51单片机的电子时钟设计.doc
Four short words sum up what has lifted most successful individuals above the crowd: a little bit more.-author-date基于DS1302和51单片机的电子时钟设计基于DS1302和51单片机的电子时钟设计目 录第一章 系统介绍21.1 电子钟介绍21.2 单片机AT89C51介绍21.3 时钟芯片DS1302简介51.3.1 主要功能51.3.2 内部结构及引脚功能51.3.3 工作原理61.3.4 控制字节及寄存器61.3.5 时钟/日历存储区(时分秒)71.3.6 数据的传送7第二章 硬件设计82.1 系统结构及总流程图82.2 系统硬件电路设计图92.2.1整体电路设计92.2.2 DS1302电路设计92.2.3 按键电路102.2.4 显示电路10第三章、程序源代码113.1程序流程图113.1源程序12第四章 PROTEUS软件仿真18总 结19谢 辞20参考文献21第一章 系统介绍1.1 电子钟介绍电子钟已成为人们日常生活中的必需品,广泛应用于家庭、车站、办公室等场所。钟表数字化给人们生产生活带来了极大地方便而且大大地扩展了钟表原先的报时功能诸如定时自动报警、按时自动打铃、时间程序自动控制、通断动力设备、甚至各种定时电气的自动启用,因此研究数字钟及扩大其应用有着非常现实的意义。数字电子钟设计与制作可采用数字电路实现,也可以采用单片机来完成。若用数字电路完成,所设计的电路相当复杂,大概需要十几片数字集成块,其功能也主要依赖于数字电路的各功能模块的组合来实现,焊接的过程比较复杂,成本也非常高。若用单片机来设计制作完成,由于其功能的实现主要通过软件编程来完成,那么就降低了硬件电路的复杂性,而且其成本也有所降低。截止今日,单片机应用技术飞速发展,纵观现在各个领域,从导弹的导航装置,到飞机上各种仪表的控制,从计算机的网络通讯与数据传输,到工业自动化过程的实时控制和数据处理,以及我们生活中广泛使用的各种智能IC卡、电子宠物等,这些都离不开单片机。单片机即单片微型计算机(Single-Chip Microcomputer ),是集CPU ,RAM ,ROM ,定时,计数和多种接口于一体的微控制器。它体积小,成本低,功能强,广泛应用于智能产业和工业自动化上。同时,若采用单片机计时,一方面需要采用计数器,占用硬件资源,另一方面需要设置中断、查询等,同样耗费单片机的资源,而且,某些测控系统可能不允许。但是,如果在系统中采用时钟芯片,则能很好地解决这个问题。现在流行的串行时钟电路很多,如DS1302、DS1307、PCF8485等。这些电路的接口简单、价格低廉、使用方便,被广泛地采用。美国Dallas公司生产的串行实时时钟芯片 DS1302是一种具有涓细电流充电能力的实时时钟芯片,采用普通32.768KHZ晶振,具有实时时钟和 31 字节的静态RAM。主要特点是采用串行数据传输,可方便地与单片机接口,可为掉电保护电源提供可编程的充电功能,并且可以关闭充电功能。本设计要求利用51单片机和DS1302设计制作一个LED电子钟,用8个LED显示时间,当按下相应按键时,修改当前时间或闹铃时间,若当前时间与闹铃时间相同,蜂鸣器发音1分钟。1.2 单片机AT89C51介绍AT89C51是一个低功耗,高性能CMOS 8位单片机,片内含8k Bytes ISP(In-system programmable)的可反复擦写1000次的Flash只读程序存储器,器件采用ATMEL公司的高密度、非易失性存储技术制造,兼容标准 MCS-51指令系统及80C51引脚结构,芯片内集成了通用8位中央处理器和ISP Flash存储单元,功能强大的微型计算机的AT89C51可为许多嵌入式控制应用系统提供高性价比的解决方案。AT89C51具有如下特点:40个引脚,8k Bytes Flash片内程序存储器,256 bytes的随机存取数据存储器(RAM),32个外部双向输入/输出(I/O)口,5个中断优先级2层中断嵌套中断,2个16位可编程定时计数器,2个全双工串行通信口,看门狗(WDT)电路,片内时钟振荡器。另外,AT89C51 可降至0Hz 静态逻辑操作,支持2种软件可选择节电模式。空闲模式下,CPU 停止工作,允许RAM、定时器/计数器、串口、中断继续工作。掉电保护方式下,RAM内容被保存,振荡器被冻结, 单片机一切工作停止,直到下一个中断或硬件复位为止。芯片引脚功能AT89C51芯片DIP双列直插式封装引脚如图1-1所示。图1-1 AT89C51引脚排列P0 口:P0口是一个8位漏极开路的双向I/O口。作为输出口,每位能驱动8个TTL逻辑电平。对P0端口写“1”时,引脚用作高阻抗输入。当访问外部程序和数据存储器时,P0口也被作为低8位地址/数据复用。在这种模式下,P0具有内部上拉电阻。 P1 口:P1 口是一个具有内部上拉电阻的8 位双向I/O 口,P1 输出缓冲器能驱动4 个 TTL 逻辑电平。对P1 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。 引脚口第二功能如下: P1.0/T2(定时器/计数器T2的外部计数输入),时钟输出 P1.1/T2EX(定时器/计数器T2的捕捉/重载触发信号和方向控制) P1.5/MOSI(在系统编程用) P1.6/MISO(在系统编程用) P1.7/SCK(在系统编程用) P2 口:P2口是一个具有内部上拉电阻的8 位双向I/O 口,P2 输出缓冲器能驱动4 个TTL逻辑电平。对P2 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。 在访问外部程序存储器或用16位地址读取外部数据存储器(例如执行MOVX DPTR)时,P2 口送出高八位地址。在这种应用中,P2 口使用很强的内部上拉发送1。在使用8位地址(如MOVX RI)访问外部数据存储器时,P2口输出P2锁存器的内容。 P3 口:P3 口是一个具有内部上拉电阻的8 位双向I/O 口,P2 输出缓冲器能驱动4 个TTL逻辑电平。对P3 端口写“1”时,内部上拉电阻把端口拉高,此时可以作为输入口使用。作为输入使用时,被外部拉低的引脚由于内部电阻的原因,将输出电流(IIL)。 P3口亦作为AT89C51特殊功能(第二功能)使用。 引脚口第二功能如下:P3.0/RXD(串行输入口)P3.1/TXD(串行输出口)P3.2/INTO(外中断0)P3.3/INT1(外中断1)P3.4/TO(定时/计数器0)P3.5/T1(定时/计数器1)P3.6/WR(外部数据存储器写选通)P3.7/RD(外部数据存储器读选通)此外,P3口还接收一些用于FLASH闪存编程和程序校验的控制信号。RST复位输入。当振荡器工作时,RST引脚出现两个机器周期以上高电平将是单片机复位。ALE/PROG当访问外部程序存储器或数据存储器时,ALE(地址锁存允许)输出脉冲用于锁存地址的低8位字节。一般情况下,ALE仍以时钟振荡频率的1/6输出固定的脉冲信号,因此它可对外输出时钟或用于定时目的。要注意的是:每当访问外部数据存储器时将跳过一个ALE脉冲。如有必要,可通过对特殊功能寄存器(SFR)区中的8EH单元的D0位置位,可禁止ALE操作。该位置位后,只有一条MOVX和MOVC指令才能将ALE激活。此外,该引脚会被微弱拉高,单片机执行外部程序时,应设置ALE禁止位无效。PSEN程序储存允许(PSEN)输出是外部程序存储器的读选通信号,当AT89C52由外部程序存储器取指令(或数据)时,每个机器周期两次PSEN有效,即输出两个脉冲,在此期间,当访问外部数据存储器,将跳过两次PSEN信号。EA/VPP外部访问允许,欲使CPU仅访问外部程序存储器(地址为0000H-FFFFH),EA端必须保持低电平(接地)。需注意的是:如果加密位LB1被编程,复位时内部会锁存EA端状态。如EA端为高电平(接Vcc端),CPU则执行内部程序存储器的指令。1.3 时钟芯片DS1302简介1.3.1 主要功能DS1302 是美国DALLAS公司推出的一种高性能、低功耗、带RAM的实时时钟电路,它可以对年、月、日、周日、时、分、秒进行计时,具有闰年补偿功能,工作电压为2.5V5.5V。采用三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号或RAM数据。DS1302内部有一个31×8的用于临时性存放数据的RAM寄存器。DS1302是DS1202的升级产品,与DS1202兼容,但增加了主电源/后备电源双电源引脚,同时提供了对后备电源进行涓细电流充电的能力。特性: 实时时钟,可对秒、分、时、日、周、月以及带闰年补偿的年进行计数 用于高速数据暂存的 31×8 RAM 2引脚的串行 I/O 2.5-5.5V满度工作范围 用于时钟或RAM数据读写的单字节或 多字节数据传送 双电源引脚 可选慢速充电至VCC11.3.2 内部结构及引脚功能DS1302内部主要包括实时时钟(real time clock)、输入移位寄存器(input shift registers)、31字节静态RAM、电源控制部分(power control)、命令控制逻辑(command and control logic)、振荡器和分频器(oscillator and divider)等部分。DS1302内部结构如图1-2所示。图1-2 DS1302内部结构图1-3 DS1302引脚排列DS1302具有8脚DIP引脚排列如图1-3所示。Vcc1:后备电源,在主电源关闭的情况下,也能保持时钟的连续运行;当Vcc2大于Vcc10.2V时,Vcc2给DS1302供电。Vcc2:主电源,当Vcc2小于Vcc1时,DS1302由Vcc1供电。X1、X2:振荡源,外接32.768kHz晶振。GND:接地端SCLK:串行时钟输入端I/O:串行数据输入输出端(双向)。RST:复位/片选线,通过把RST输入驱动置高电平来启动所有的数据传送。RST输入有两种功能:首先,RST接通控制逻辑,允许地址/命令序列送入移位寄存器;其次,RST提供终止单字节或多字节数据的传送手段。1.3.3 工作原理DS1302工作时为了对任何数据传送进行初始化,需要将复位脚(RST)置为高电平且将8位地址和命令信息装入移位寄存器。数据在时钟(SCLK)的上升沿串行输入,前8位指定访问地址。命令字装入移位寄存器后,在之后的时钟周期,读操作时输出数据,写操作时输入数据。时钟脉冲的个数在单字节方式下为8+8(8位地址+8位数据),在多字节方式下最多可达8+248。1.3.4 控制字节及寄存器DS1302的一次数据传送是从发送控制字节开始的。控制字节的最高有效位(位7)必须是逻辑1,如果该位为0,则无法把数据写入到DS1302中;位6表示要读写的数据类型,为0表示存取日历时钟数据,为1表示存取 RAM数据;位5至位1指示要操作单元的地址;最低有效位(位 0)表示命令类型,为0表示要进行写操作,为 1 表示要进行读操作。控制字节总是从最低位开始输出。其控制字节格式如图1-4所示。图1-4 控制字节格式1.3.5 时钟/日历存储区(时分秒)1.3.6 数据的传送向 DS1302 写入数据时,数据在控制字节输入后的下一个 SCLK周期的上升沿被写入,多余的 SCLK将被忽略。数据写入时从低位(位0)开始;同样,从DS1302 读取数据时,数据在紧跟控制字节后的下一个 SCLK的下降沿读出,读出数据时也是从低位(0位)到高位(7 位),只要RST保持高电平,额外的 SCLK将导致数据字节的持续读出,这个特性用于实现该芯片的突发读模式。对DS1302 的每一次读写需 16个时钟脉冲,前 8 个脉冲输入操作地址和读写命令,后8个脉冲写入或读出数据。数据传送时序如图1-5。图1-5 数据读写时序图第二章 硬件设计2.1 系统结构及总流程图单片机AT89S52数码管显示实时时钟芯片DS1302电源电路蜂鸣电 路时 间闹 钟设 置电 路图3-1 系统总体结构根据软件功能要求,将系统软件划分为若干个相对独立的部分,设计出合理的总体结构:时钟显示是一个循环过程,系统以单片机AT89S52为主控制器,不断读取实时时钟芯片DS1302提供的时间送LED显示,时间采用24小时模式;当达到闹钟所设定时间时,控制蜂鸣器发声一分钟;当需要调整时间或闹钟时,按下相应按键进入中断处理。整个系统的电源可由电池提供或者用USB电源线由电脑提供。系统总体结构如图3-1所示。2.2 系统硬件电路设计图2.2.1整体电路设计将DS1302时钟芯片与单片机的P1口连接构成系统的实时时钟电路的部分;将键盘与单片机的P1.4-P1.7接口连接组成系统的按键电路,用来对显示器的控制;LED的段选端与单片机的P0口连接构成系统的显示段选控制系统;将位选与P2口连接使系统的位选较好。2.2.2 DS1302电路设计DS1302部分电路设计图将DS1302的X1、X2引脚分别与晶振相连,并通过两个分立电容相连然后接地;将VCC1、VCC2相连然后接地,此部分构成了DS1302芯片的供电电路。将DS1302的RST引脚接单片机P1.1引脚作为DS1302的复位引脚电路;将DS1302的SLK引脚单片机的P1.2引脚相连组成DS1302的时钟端电路;将DS1302的I/O引脚与单片机的P1.3相连构成DS1302的I/O端口的电路连接图。2.2.3 按键电路按键部分电路设计图将四个按键分别与单片机的P1.4-P.7相连组成系统的按键电路部分,P1.4用来接key1,此按键作为切换键,P1.5接口接key2,此键作为加,P1.6接口接key3,此键最为系统减位,2.2.4 显示电路系统显示部分电路设计图将LED数码显示管的段选端与单片机的P0口相连,由于数码管采用的是动态显示,所以通过对P0的8位二进制数据的不断改变使数码显示管不断的动态显示;将LED的位选端与单片机的P2口连接使LED数码显示管不断的到位选信号在与数选信号不断的配合下显示数时间数据。第三章、程序源代码3.1程序流程图图为系统软件系统流程图,通过不断的切换与循环实现系统循环计时此图为按键扫描软件流程图,通过软件的不断扫描信号,确认按键的扫描情况。3.1源程序-#include<reg52.h>#define uchar unsigned char#define uint unsigned intsbit beep = P33 ;sbit RST = P11 ;sbit SCK = P12 ;sbit SDA = P13 ; sbit key1 = P14 ;sbit key2 = P15 ;sbit key3 = P16 ;sbit key4 = P17 ;uchar i,j,mod,flag,flag1=1 ;/uchar code write_addr7=0x80,0x82,0x84,0x86,0x88,0x8a,0x8c; /uchar code read_addr7=0x81,0x83,0x85,0x87,0x89,0x8b,0x8d; /共阳数码管码表,表示0-9和-。-表示时分秒的间隔.uchar code LED11 = 0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xF8,0x80,0x90,;uchar disbuf8 = 0x62,0xa2,0xf7,0x7a,0x28,0xf7,0x7e,0x7e ;/定义时间日期的结构类型struct time uchar second ; uchar minute ; uchar hour ; uchar week ; uchar day ; uchar month ; uchar year ;current_time ;#define ctime current_time/定义闹钟的时分秒变量uchar asec,amin,ahour ;void delay(uint ms) uchar i ; while(ms-) for(i=135;i>0;i-);/滴一声,用来按键伴音void di() uchar i ; for(i=0;i<40;i+) beep = 0 ; delay(1) ; beep = 1 ; delay(1) ; /滴滴,连续响两下。用来闹钟提醒。void didi() uchar i ; for(i = 2; i>0; i-) di(); delay(140); /DS1302的写驱动,传入参数为一个地址和数据void write(uchar addr,uchar dat) uchar i ; RST = 0 ; SCK = 0 ; RST = 1 ; for(i=0;i<8;i+) SCK = 0 ; SDA = (bit)(addr&0x01) ; addr >>=1 ; SCK = 1 ; for(i=0;i<8;i+) SCK = 0 ; SDA = (bit)(dat&0x01); dat >>= 1 ; SCK = 1 ; RST = 0 ;/DS1302的读驱动,传入参数为一个地址,返回参数为读到的数据uchar read(uchar addr) uchar i,dat ; RST = 0 ; SCK = 0 ; RST = 1 ; for(i=0;i<8;i+) SCK = 0 ; SDA =(bit)(addr&0x01); addr>>=1 ; SCK = 1 ; for(i=0;i<8;i+) SCK=0 ; dat>>=1 ; if(SDA) dat|=0x80 ; SCK=1 ; RST=0 ; dat=(dat/16*10)+(dat&0x0f); return dat ;/读取DS1302的时间和日期void read_time() ctime.second = read(0x81); ctime.minute = read(0x83); ctime.hour = read(0x85); ctime.day = read(0x87); ctime.month = read(0x89); ctime.week = read(0x8b); ctime.year = read(0x8d);/设定时间和日期void set_time() if(!flag) write(0x8e,0x00); write(0x80,(ctime.second/10)<<4|(ctime.second%10); write(0x82,(ctime.minute/10)<<4|(ctime.minute%10); write(0x84,(ctime.hour/10)<<4|(ctime.hour%10); write(0x8e,0x80); else write(0x8e,0x00); write(0x86,(ctime.day/10)<<4|(ctime.day%10); write(0x88,(ctime.month/10)<<4|(ctime.month%10); write(0x8a,(ctime.week/10)<<4|(ctime.week%10); write(0x8c,(ctime.year/10)<<4|(ctime.year%10); write(0x8e,0x80); flag1=1 ;/刷新显示缓冲区void refbuf() switch(flag) case 1 : disbuf0 = LEDctime.day%10; disbuf1 = LEDctime.day/10; disbuf2 = 0xbf ; disbuf3 = LEDctime.month%10; disbuf4 = LEDctime.month/10; disbuf5 = 0xbf ; disbuf6 = LEDctime.year%10; disbuf7 = LEDctime.year/10; break ; case 2 : disbuf0 = 0xbf ; disbuf1 = 0xbf ; disbuf2 = 0xbf ; disbuf3 = 0xbf ; disbuf4 = LEDctime.week ; disbuf5 = 0xbf ; disbuf6 = 0xbf ; disbuf7 = 0xbf ; break ; case 3 : disbuf0 = LEDasec%10; disbuf1 = LEDasec/10; disbuf2 = 0xbf ; disbuf3 = LEDamin%10; disbuf4 = LEDamin/10; disbuf5 = 0xbf ; disbuf6 = LEDahour%10; disbuf7 = LEDahour/10; break ; default : disbuf0 = LEDctime.second%10; disbuf1 = LEDctime.second/10; disbuf2 = 0xbf ; disbuf3 = LEDctime.minute%10; disbuf4 = LEDctime.minute/10; disbuf5 = 0xbf ; disbuf6 = LEDctime.hour%10; disbuf7 = LEDctime.hour/10; /键盘扫描void keyscan() if(key1 = 0) delay(5); if(key1 = 0) while(!key1); /key1用来调节时间和日期、闹钟 mod+;/mod标示调节时间的时候对应的闪烁位,mod=1秒位或天或闹钟的秒,mod=2是分位,mod=3是小时位 di();/退出调节功能,返回正常显示 if(mod = 4) set_time(); mod = 0 ; /调节加1,因为涉及到时分秒,年月日周和闹钟的调节,所以写的比较复杂,应该有更精简的代码。 if(key2=0) delay(5); if(key2=0) while(!key2); di(); switch(mod) case 1 : ctime.second+; if(ctime.second=60) ctime.second=0 ; break ; case 2 : ctime.minute+; if(ctime.minute=60) ctime.minute=0 ; break ; case 3 : ctime.hour+; if(ctime.hour=24) ctime.hour=0 ; break ; /flag=0时默认调节时分秒,=1时调节年月日,=2时调节周,=3时调节闹钟,=4时就返回正常显示,flag又key4来控制。 if(flag=1) switch(mod) case 1 : ctime.day+; if(ctime.day=32) ctime.day=0 ; break ; case 2 : ctime.month+; if(ctime.month=13) ctime.month=0 ; break ; case 3 : ctime.year+; if(ctime.year=100) ctime.year=0 ; break ; if(flag=2) if(mod=1) ctime.week+; if(ctime.week=8) ctime.week=1 ; if(flag=3) switch(mod) case 1 : asec+; if(asec=60) asec=0 ; break ; case 2 : amin+; if(amin=60) amin=0 ; break ; case 3 : ahour+; if(ahour=24) ahour=0 ; break ; /调节减1,功能与key2相似。 if(key3=0) delay(5); if(key3=0) while(!key3); di(); switch(mod) case 1 : if(ctime.second=0) ctime.second=60 ; ctime.second-; break ; case 2 : if(ctime.minute=0) ctime.minute=60 ; ctime.minute-; break ; case 3 : if(ctime.hour=0) ctime.hour=24 ; ctime.hour-; break ; if(flag=1) switch(mod) case 1 : if(ctime.day=0) ctime.day=32 ; ctime.day-; break ; case 2 : if(ctime.month=0) ctime.month=13 ; ctime.month-; break ; case 3 : if(ctime.year=0) ctime.year=100 ; ctime.year-; break ; if(flag=2) if(mod=1) if(ctime.week=1) ctime.week=8 ; ctime.week-; if(flag=3)