当前位置:文档之家› 单片机仿真课程设计——基于51单片机的实时时钟

单片机仿真课程设计——基于51单片机的实时时钟

基于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.全静态工作:0——24Hz;3.128×8字节内部RAM;4.32个外部双向输入/输出(I/O)口;5.6个中断优先级; 2个16位可编程定时计数器;6.可编程串行通道;7.片内时钟振荡器。

DS1302是美国DALLAS公司推出的一种高性能、低功耗的实时时钟日历芯片,附加31字节静态RAM,采用SPI三线接口与CPU进行同步通信,并可采用突发方式一次传送多个字节的时钟信号和RAM数据。

实时时钟可提供秒、分、时、日、星期、月和年,一个月小于31天时可以自动调整,且具有闰年补偿功能。

工作电压宽达 2.5~5.5V。

采用双电源供电(主电源和备用电源),可设置备用电源充电方式,提供了对后备电源进行涓细电流充电的能力。

有主电源和备份电源双引脚,而且备份电源可由大容量电容(>1F)来替代。

需要强调的是,DS1302需要使用32.768KHz的晶振。

四、原理图五、程序源代码#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 = P1^6; //实时时钟时钟线引脚sbit DS1302_IO = P1^7; //实时时钟数据线引脚sbit DS1302_RST = P1^5; //实时时钟复位线引脚sbit ACC0 = ACC^0;sbit ACC7 = ACC^7;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 DateString[9];unsigned char TimeString[9];}SYSTEMTIME; //定义的时间类型#define AM(X) X#define PM(X) (X+12) // 转成24小时制#define DS1302_SECOND 0x80#define DS1302_MINUTE 0x82#define DS1302_HOUR 0x84#define DS1302_WEEK 0x8A#define DS1302_DAY 0x86#define DS1302_MONTH 0x88#define DS1302_YEAR 0x8C#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; //相当于汇编中的 RRCDS1302_CLK = 1;DS1302_CLK = 0;ACC = ACC >> 1;}}unsigned char DS1302OutputByte(void) //实时时钟读取一字节(内部函数){unsigned char i;for(i=8; i>0; i--){ACC = ACC >>1; //相当于汇编中的 RRCACC7 = 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->DateString[0] = Time->Year/10 + '0';Time->DateString[1] = Time->Year%10 + '0';Time->DateString[2] = '-';Time->DateString[3] = Time->Month/10 + '0';Time->DateString[4] = Time->Month%10 + '0';Time->DateString[5] = '-';Time->DateString[6] = Time->Day/10 + '0';Time->DateString[7] = Time->Day%10 + '0';Time->DateString[8] = '\0';}void TimeToStr(SYSTEMTIME *Time){Time->TimeString[0] = Time->Hour/10 + '0';Time->TimeString[1] = Time->Hour%10 + '0';Time->TimeString[2] = ':';Time->TimeString[3] = Time->Minute/10 + '0';Time->TimeString[4] = Time->Minute%10 + '0';Time->TimeString[5] = ':';Time->TimeString[6] = Time->Second/10 + '0';Time->TimeString[7] = Time->Second%10 + '0';Time->DateString[8] = '\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 = P2^0;sbit LcdRw = P2^1;sbit LcdEn = P2^2;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_COMMAND 0 // Command#define LCD_DATA 1 // Data#define LCD_CLEAR_SCREEN 0x01 // 清屏#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_SHOW 0x04 //显示开#define LCD_HIDE 0x00 //显示关#define LCD_CURSOR 0x02 //显示光标#define LCD_NO_CURSOR 0x00 //无光标#define LCD_FLASH 0x01 //光标闪动#define LCD_NO_FLASH 0x00 //光标不闪动void LCD_SetDisplay(unsigned char DisplayMode){LCD_Write(LCD_COMMAND, 0x08|DisplayMode);}//设置输入模式#define LCD_AC_UP 0x02#define LCD_AC_DOWN 0x00 // default#define LCD_MOVE 0x01 // 画面可平移#define LCD_NO_MOVE 0x00 //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六、仿真结果七、实验心得通过本次课程设计,我不仅加深了对单片机理论的理解,而且能够完成从硬件电路图设计,到软件编程及系统调试实现系统功能这一流程。

相关主题