单片机仿真课程设计基于单片机的实时时钟.docx
基于51系列单片机及DS1302时钟芯片的实时时钟仿真设计一、 课程设计目的意义通过本次课程设计可以灵活运用单片机的基础知识,依据课程设计内容,能够完成从硬件电路图设计,到软件编程及系统调试实现系统功能,完成课程设计,加深对单片机基础知识的理解并灵活运用。二、 实现目标本设计主要为实现一款可正常显示时钟/日历的实时电子时钟。对当前电子钟开发手段进行了比较和分析,最终确定了采用单片机技术实现电子时钟。本设计应用AT89C52芯片作为核心,LCD显示屏,使用DS1302实时时钟日历芯片完成时钟/日历的基本功能。这种实现方法的优点是电路简单,性能可靠,实时性好,时间精确,操作简单,编程容易。三、 硬件设计 本设计采用具有32根I/O引脚的AT89C52单片机。AT89C52单片机是一款低功耗,低电压,高性能CMOS 8位单片机,片内含4KB(可经受1000次擦写周期)的FLASH可编程可反复擦写的只读程序存储器(EPROM),器件采用CMOS工艺和ATMEI公司的高密度、非易失性存储器(NURAM)技术制造,其输出引脚和指令系统都与MCS-52兼容。片内的FLASH存储器允许在系统内可改编程序或用常规的非易失性存储器编程器来编程。因此,AT89C52是一种功能强,灵活性高且价格合理的单片机,可方便的应用在各个控制领域。 AT89C52具有以下主要性能:1. 4KB可改编程序Flash存储器;2. 全静态工作:024Hz;3. 128×8字节内部RAM;4. 32个外部双向输入/输出(I/O)口;5. 6个中断优先级; 2个16位可编程定时计数器;6. 可编程串行通道;7. 片内时钟振荡器。DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟日历芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。实时时钟可提供秒、分、时、日、星期、月和年,一个月小于31天时可以自动调整,且具有闰年补偿功能。工作电压宽达2.55.5V。采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。有主电源和备份电源双引脚,而且备份电源可由大容量电容(1F)来替代。需要强调的是,DS1302需要使用32.768KHz的晶振。四、 原理图五、 程序源代码第 13 页#include <REGX52.H>#include "LCD1602.h"#include "DS1302.h"void Delay1ms(unsigned int count)unsigned int i,j;for(i=0;i<count;i+)for(j=0;j<120;j+);main()SYSTEMTIME CurrentTime;LCD_Initial();Initial_DS1302();GotoXY(0,0);Print("Date: ");GotoXY(0,1);Print("Time: ");while(1)DS1302_GetTime(&CurrentTime);DateToStr(&CurrentTime);TimeToStr(&CurrentTime);GotoXY(6,0);Print(CurrentTime.DateString);GotoXY(6,1);Print(CurrentTime.TimeString);Delay1ms(300);#ifndef _REAL_TIMER_DS1302#define _REAL_TIMER_DS1302sbit DS1302_CLK = P16; /实时时钟时钟线引脚 sbit DS1302_IO = P17; /实时时钟数据线引脚 sbit DS1302_RST = P15; /实时时钟复位线引脚sbit ACC0 = ACC0;sbit ACC7 = ACC7;typedef struct _SYSTEMTIME_unsigned char Second;unsigned char Minute;unsigned char Hour;unsigned char Week;unsigned char Day;unsigned char Month;unsigned char Year;unsigned char DateString9;unsigned char TimeString9;SYSTEMTIME;/定义的时间类型#define AM(X)X#define PM(X)(X+12) / 转成24小时制#define DS1302_SECOND0x80#define DS1302_MINUTE0x82#define DS1302_HOUR0x84 #define DS1302_WEEK0x8A#define DS1302_DAY0x86#define DS1302_MONTH0x88#define DS1302_YEAR0x8C#define DS1302_RAM(X)(0xC0+(X)*2) /用于计算 DS1302_RAM 地址的宏 void DS1302InputByte(unsigned char d) /实时时钟写入一字节(内部函数) unsigned char i; ACC = d; for(i=8; i>0; i-) DS1302_IO = ACC0; /相当于汇编中的 RRC DS1302_CLK = 1; DS1302_CLK = 0; ACC = ACC >> 1; unsigned char DS1302OutputByte(void) /实时时钟读取一字节(内部函数) unsigned char i; for(i=8; i>0; i-) ACC = ACC >>1; /相当于汇编中的 RRC ACC7 = DS1302_IO; DS1302_CLK = 1; DS1302_CLK = 0; return(ACC); void Write1302(unsigned char ucAddr, unsigned char ucDa)/ucAddr: DS1302地址, ucData: 要写的数据 DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302InputByte(ucAddr); / 地址,命令 DS1302InputByte(ucDa); / 写1Byte数据 DS1302_CLK = 1; DS1302_RST = 0;unsigned char Read1302(unsigned char ucAddr)/读取DS1302某地址的数据 unsigned char ucData; DS1302_RST = 0; DS1302_CLK = 0; DS1302_RST = 1; DS1302InputByte(ucAddr|0x01); / 地址,命令 ucData = DS1302OutputByte(); / 读1Byte数据 DS1302_CLK = 1; DS1302_RST = 0; return(ucData);void DS1302_SetProtect(bit flag) /是否写保护if(flag)Write1302(0x8E,0x10);elseWrite1302(0x8E,0x00);void DS1302_SetTime(unsigned char Address, unsigned char Value) / 设置时间函数DS1302_SetProtect(0);Write1302(Address, (Value/10)<<4 | (Value%10); void DS1302_GetTime(SYSTEMTIME *Time)unsigned char ReadValue;ReadValue = Read1302(DS1302_SECOND);Time->Second = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_MINUTE);Time->Minute = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_HOUR);Time->Hour = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_DAY);Time->Day = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_WEEK);Time->Week = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_MONTH);Time->Month = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);ReadValue = Read1302(DS1302_YEAR);Time->Year = (ReadValue&0x70)>>4)*10 + (ReadValue&0x0F);void DateToStr(SYSTEMTIME *Time)Time->DateString0 = Time->Year/10 + '0'Time->DateString1 = Time->Year%10 + '0'Time->DateString2 = '-'Time->DateString3 = Time->Month/10 + '0'Time->DateString4 = Time->Month%10 + '0'Time->DateString5 = '-'Time->DateString6 = Time->Day/10 + '0'Time->DateString7 = Time->Day%10 + '0'Time->DateString8 = '0'void TimeToStr(SYSTEMTIME *Time)Time->TimeString0 = Time->Hour/10 + '0'Time->TimeString1 = Time->Hour%10 + '0'Time->TimeString2 = ':'Time->TimeString3 = Time->Minute/10 + '0'Time->TimeString4 = Time->Minute%10 + '0'Time->TimeString5 = ':'Time->TimeString6 = Time->Second/10 + '0'Time->TimeString7 = Time->Second%10 + '0'Time->DateString8 = '0'void Initial_DS1302(void)unsigned char Second=Read1302(DS1302_SECOND);if(Second&0x80) DS1302_SetTime(DS1302_SECOND,0);#endif#ifndef LCD_CHAR_1602#define LCD_CHAR_1602#include <intrins.h>/Port Definitionssbit LcdRs= P20;sbit LcdRw= P21;sbit LcdEn = P22;sfr DBPort = 0x80;/P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口/内部等待函数unsigned char LCD_Wait(void)LcdRs=0;LcdRw=1;_nop_();LcdEn=1;_nop_();/while(DBPort&0x80);/在用Proteus仿真时,注意用屏蔽此语句,在调用GotoXY()时,会进入死循环, /可能在写该控制字时,该模块没有返回写入完备命令,即DBPort&0x80=0x80 /实际硬件时打开此语句LcdEn=0;return DBPort;/向LCD写入命令或数据#define LCD_COMMAND0 / Command#define LCD_DATA1 / Data#define LCD_CLEAR_SCREEN0x01 / 清屏#define LCD_HOMING 0x02 / 光标返回原点void LCD_Write(bit style, unsigned char input)LcdEn=0;LcdRs=style;LcdRw=0;_nop_();DBPort=input;_nop_();/注意顺序LcdEn=1;_nop_();/注意顺序LcdEn=0;_nop_();LCD_Wait();/设置显示模式#define LCD_SHOW0x04 /显示开#define LCD_HIDE0x00 /显示关 #define LCD_CURSOR0x02 /显示光标#define LCD_NO_CURSOR0x00 /无光标 #define LCD_FLASH0x01 /光标闪动#define LCD_NO_FLASH0x00 /光标不闪动void LCD_SetDisplay(unsigned char DisplayMode)LCD_Write(LCD_COMMAND, 0x08|DisplayMode);/设置输入模式#define LCD_AC_UP0x02#define LCD_AC_DOWN0x00 / default#define LCD_MOVE0x01 / 画面可平移#define LCD_NO_MOVE0x00 /defaultvoid LCD_SetInput(unsigned char InputMode)LCD_Write(LCD_COMMAND, 0x04|InputMode);/初始化LCDvoid LCD_Initial()LcdEn=0;LCD_Write(LCD_COMMAND,0x38); /8位数据端口,2行显示,5*7点阵LCD_Write(LCD_COMMAND,0x38);LCD_SetDisplay(LCD_SHOW|LCD_NO_CURSOR); /开启显示, 无光标LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); /清屏LCD_SetInput(LCD_AC_UP|LCD_NO_MOVE); /AC递增, 画面不动void GotoXY(unsigned char x, unsigned char y)if(y=0)LCD_Write(LCD_COMMAND,0x80|x);if(y=1)LCD_Write(LCD_COMMAND,0x80|(x-0x40);void Print(unsigned char *str)while(*str!='0')LCD_Write(LCD_DATA,*str);str+;#endif六、 仿真结果七、 实验心得通过本次课程设计,我不仅加深了对单片机理论的理解,而且能够完成从硬件电路图设计,到软件编程及系统调试实现系统功能这一流程。还学会了如何应用proteus进行仿真,应用keil软件进行编程,加深了对51系列单片机的理解。在本次课程设计中遇到了一连串的问题,经过同学们的帮助,最终完成了本次课程设计,我不仅学会了如何将所学的理论知识和实际结合起来,也学会了和他人的合作,而且锻炼了我的实践能力。