【精美排版】基于ATS单片机的多功能电子万年历的设计.doc
个人收集整理 仅供参考学习单片机应用系统设计课 题:基于AT89S51单片机的多功能 电子万年历的设计 姓 名:班 级:学 号:指导老师:日 期:一、绪论:现在是一个知识爆炸的新时代,新产品、新技术层出不穷,电子技术的发展更是日新月异。可以毫不夸张的说,电子技术的应用无处不在,电子技术正在不断地改变我们的生活,改变着我们的世界。近些年,随着科技的发展和社会的进步,人们对电子万年历的要求也越来越高,传统的电子万年历已不能满足人们的需求。人们对多功能电子万年历的功能及工作顺序都非常熟悉,但是却很少知道它的内部结构以及工作原理。由于单片机具有灵活性强、成本低、功耗低、保密性好等特点,还有,单片机是微型计算机的一个重要分枝,单片机是把中央处理器、随机存储器、只读存储器、定时器/计数器、并行接口接口、串行接口、A/D转换器等主要计算机部件,集中在一块集成电路芯片上。虽然只是一个芯片,但从功能上它就相当于一台完整的微机。随着科学技术的迅猛发展,单片计集成度高、体积小、运算速度快、功耗低、运行可靠、价格低廉等诸多优点,越来越显现出来。在日常生活方面,我们使用彩电、录放机、VCD、照相机、手机等等可以说举不胜举。所以多功能电子万年历一般都以单片机为核心,外加一些外围设备来实现。由单片机作为多功能电子万年历的核心控制器,通过按键可以进行定时、校时功能。输出设备显示器可以用液晶显示技术和数码管显示技术。个人收集整理 勿做商业用途意义:电子万年历作为电子类小设计不仅是市场上的宠儿,也是是单片机实验中一个很常用的题目。因为它的有很好的开放性和可发挥性,因此对作者的要求比较高,不仅考察了对单片机的掌握能力更加强调了对单片机扩展的应用。而且在操作的设计上要力求简洁,功能上尽量齐全,显示界面也要出色。所以,电子万年历无论作为比赛题目还是练习题目都是很有价值。个人收集整理 勿做商业用途二、系统总体方案设计:1、共具备两个功能:(1)显示年月日及分秒信息(2)具有可调整日期和时间功能。2、硬件电路的总体框图: 辅助电路power开关电路显示CPU按键及按钮电路图1总体硬件框图3、所用芯片图2 三、硬件系统设计:1、硬件原理图:图3 硬件原理2、硬件原理:在硬件电路中采用P0口作为6位液晶显示电路的驱动接口,这是由于P0口输出驱动电路工作处于开漏状态,它的驱动能力强,故只需外接上拉电阻便可以把LED数码管点亮。因为共阴的LED数码管它的驱动电流是分开的,在单片机进行动态扫描的时候不会影响彼此的电流,故该电路中的8位LED数码管均用共阳阴极的数码管。8位LED数码管的位选线分别由相应的P2. 0P2. 5控制,而将其相应的段选线并联在一起,由一个8位的I/O口控制,即P0口。P3口与八个校时按键相连,以成功实现万年历校时的功能。个人收集整理 勿做商业用途四、系统软件设计: 1、 程序流程框图:BEGIN初始化设置数字刷新取数据转换为字符显示RETURN图4 程序流程框图2、年调整程序流程图,月、日调整程序流程图类似。开始控制键有效,进入年调整程序等待按键程序加键有效减键有效年加1年减1图5 年调整程序流程图3、源程序代码:17 / 17#include <REG51.H>#include <intrins.h>/#include "LCD1602.h"/#include "DS1302.h"#define uint unsigned int#define uchar unsigned charchar hide_sec,hide_min,hide_hour,hide_day,hide_week,hide_month,hide_year; /秒,分,时到日,月,年位闪的计数个人收集整理 勿做商业用途char done,count,temp,flag,Up_Key_flag,down_flag;uchar temp_value; /温度值uchar TempBuffer5,week_value2;sbit DS1302_CLK = P17; /实时时钟时钟线引脚 sbit DS1302_IO = P16; /实时时钟数据线引脚 sbit DS1302_RST = P15; /实时时钟复位线引脚sbit port01 = P30;sbit port02 = P31;sbit port03 = P32;sbit port04 = P33;sbit ACC0 = ACC0;sbit ACC7 = ACC7;sbit Set_Key = P20; /模式切换键sbit Up_Key = P21; /加法按钮sbit Down_Key = P22; /减法按钮sbit out_Key_Key= P23; /立刻跳出调整模式按钮sbit DQ = P10; /温度传送数据IO口void show_time(); /液晶显示程序/*1602液晶显示部分子程序*/Port Definitions*个人收集整理 勿做商业用途sbit LcdRs= P25;sbit LcdRw= P26;sbit LcdEn = P27;sfr DBPort = 0x80;/P0=0x80,P1=0x90,P2=0xA0,P3=0xB0.数据端口个人收集整理 勿做商业用途内部等待函数*个人收集整理 勿做商业用途unsigned char LCD_Wait(void)LcdRs=0;LcdRw=1;_nop_();/延时一个时钟周期LcdEn=1;_nop_();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_Set_KeyDisplay(unsigned char DisplayMode)LCD_Write(LCD_COMMAND, 0x08|DisplayMode);/设置输入模式*个人收集整理 勿做商业用途#define LCD_AC_UP_KEY0x02#define LCD_AC_DOWN0x00 / default#define LCD_MOVE0x01 / 画面可平移#define LCD_NO_MOVE0x00 /defaultvoid LCD_Set_KeyInput(unsigned char InputMode)LCD_Write(LCD_COMMAND, 0x04|InputMode);/初始化LCD*个人收集整理 勿做商业用途void LCD_Initial()LcdEn=0;LCD_Write(LCD_COMMAND,0x38); /8位数据端口,2行显示,5*7点阵个人收集整理 勿做商业用途LCD_Write(LCD_COMMAND,0x38);LCD_Set_KeyDisplay(LCD_SHOW|LCD_NO_CURSOR); /开启显示, 无光标个人收集整理 勿做商业用途LCD_Write(LCD_COMMAND,LCD_CLEAR_SCREEN); /清屏LCD_Set_KeyInput(LCD_AC_UP_KEY|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+;/*DS1302时钟部分子程序*/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 DateString11;unsigned char TimeString9;SYSTEMTIME;/定义的时间类型SYSTEMTIME CurrentTime;#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 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_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) /将时间年,月,日,星期数据转换成液晶显示字符串,放到数组里DateString个人收集整理 勿做商业用途 if(hide_year<2) /这里的if,else语句都是判断位闪烁,<2显示数据,>2就不显示,输出字符串为 2007/07/22个人收集整理 勿做商业用途 Time->DateString0 = '2' Time->DateString1 = '0' Time->DateString2 = Time->Year/10 + '0' Time->DateString3 = Time->Year%10 + '0' else Time->DateString0 = ' ' Time->DateString1 = ' ' Time->DateString2 = ' ' Time->DateString3 = ' 'Time->DateString4 = '-'if(hide_month<2) Time->DateString5 = Time->Month/10 + '0' Time->DateString6 = Time->Month%10 + '0' else Time->DateString5 = ' ' Time->DateString6 = ' ' Time->DateString7 = '-'if(hide_day<2) Time->DateString8 = Time->Day/10 + '0' Time->DateString9 = Time->Day%10 + '0' else Time->DateString8 = ' ' Time->DateString9 = ' ' if(hide_week<2) week_value0 = Time->Week%10 + '0' /星期的数据另外放到 week_value数组里,跟年,月,日的分开存放,因为等一下要在最后显示个人收集整理 勿做商业用途 else week_value0 = ' ' week_value1 = '0'Time->DateString10 = '0' /字符串末尾加 '0' ,判断结束字符void TimeToStr(SYSTEMTIME *Time) /将时,分,秒数据转换成液晶显示字符放到数组 TimeString;个人收集整理 勿做商业用途 if(hide_hour<2) Time->TimeString0 = Time->Hour/10 + '0' Time->TimeString1 = Time->Hour%10 + '0' else Time->TimeString0 = ' ' Time->TimeString1 = ' 'Time->TimeString2 = ':' if(hide_min<2) Time->TimeString3 = Time->Minute/10 + '0' Time->TimeString4 = Time->Minute%10 + '0' else Time->TimeString3 = ' ' Time->TimeString4 = ' ' Time->TimeString5 = ':' if(hide_sec<2) Time->TimeString6 = Time->Second/10 + '0' Time->TimeString7 = Time->Second%10 + '0' else Time->TimeString6 = ' ' Time->TimeString7 = ' ' Time->DateString8 = '0'void Initial_DS1302(void) /时钟芯片初始化 unsigned char Second=Read1302(DS1302_SECOND);if(Second&0x80) /判断时钟芯片是否关闭 Write1302(0x8e,0x00); /写入允许Write1302(0x8c,0x07); /以下写入初始化时间 日期:07/07/25.星期: 3. 时间: 23:59:55个人收集整理 勿做商业用途Write1302(0x88,0x07);Write1302(0x86,0x25);Write1302(0x8a,0x07);Write1302(0x84,0x23);Write1302(0x82,0x59);Write1302(0x80,0x55);Write1302(0x8e,0x80); /禁止写入/*ds18b20子程序*/*ds18b20延迟子函数(晶振12MHz )*/ void delay_18B20(unsigned int i)while(i-);/*ds18b20初始化函数*/void Init_DS18B20(void) unsigned char x=0; DQ = 1; /DQ复位 delay_18B20(8); /稍做延时 DQ = 0; /单片机将DQ拉低 delay_18B20(80); /精确延时 大于 480us DQ = 1; /拉高总线 delay_18B20(14); x=DQ; /稍做延时后 如果x=0则初始化成功 x=1则初始化失败 delay_18B20(20);/*ds18b20读一个字节*/ unsigned char ReadOneChar(void)uchar i=0;uchar dat = 0;for (i=8;i>0;i-) DQ = 0; / 给脉冲信号 dat>>=1; DQ = 1; / 给脉冲信号 if(DQ) dat|=0x80; delay_18B20(4); return(dat);/*ds18b20写一个字节*/ void WriteOneChar(uchar dat) unsigned char i=0; for (i=8; i>0; i-) DQ = 0; DQ = dat&0x01; delay_18B20(5); DQ = 1; dat>>=1; /*读取ds18b20当前温度*/void ReadTemp(void)unsigned char a=0;unsigned char b=0;unsigned char t=0;Init_DS18B20();WriteOneChar(0xCC); / 跳过读序号列号的操作WriteOneChar(0x44); / 启动温度转换delay_18B20(100); / this message is wery important个人收集整理 勿做商业用途Init_DS18B20();WriteOneChar(0xCC); /跳过读序号列号的操作WriteOneChar(0xBE); /读取温度寄存器等(共可读9个寄存器) 前两个就是温度delay_18B20(100);a=ReadOneChar(); /读取温度值低位b=ReadOneChar(); /读取温度值高位temp_value=b<<4;temp_value+=(a&0xf0)>>4; void temp_to_str() /温度数据转换成液晶字符显示 TempBuffer0=temp_value/10+'0' /十位 TempBuffer1=temp_value%10+'0' /个位 TempBuffer2=0xdf; /温度符号 TempBuffer3='C' TempBuffer4='0'void Delay1ms(unsigned int count)unsigned int i,j;for(i=0;i<count;i+)for(j=0;j<120;j+);/*延时子程序*/void mdelay(uint delay)uint i; for(;delay>0;delay-) for(i=0;i<62;i+) /1ms延时. ; void outkey() /跳出调整模式,返回默认显示 uchar Second; if(out=0|port01=1) mdelay(8); count=0;hide_sec=0,hide_min=0,hide_hour=0,hide_day=0,hide_week=0,hide_month=0,hide_year=0;个人收集整理 勿做商业用途Second=Read1302(DS1302_SECOND); Write1302(0x8e,0x00); /写入允许Write1302(0x80,Second&0x7f);Write1302(0x8E,0x80); /禁止写入done=0; while(out=0);while(port01=1); /个人收集整理 勿做商业用途void Up_Keykey()/升序按键 Up_Key=1;if(Up_Key=0|port02=1) mdelay(8); switch(count) case 1: temp=Read1302(DS1302_SECOND); /读取秒数 temp=temp+1; /秒数加1 Up_Key_flag=1; /数据调整后更新标志 if(temp&0x7f)>0x59) /超过59秒,清零 temp=0; break; case 2: temp=Read1302(DS1302_MINUTE); /读取分数 temp=temp+1; /分数加1 Up_Key_flag=1; if(temp>0x59) /超过59分,清零 temp=0; break; case 3: temp=Read1302(DS1302_HOUR); /读取小时数 temp=temp+1; /小时数加1 Up_Key_flag=1; if(temp>0x23) /超过23小时,清零 temp=0; break; case 4: temp=Read1302(DS1302_WEEK); /读取星期数 temp=temp+1; /星期数加1 Up_Key_flag=1; if(temp>0x7) temp=1; break; case 5: temp=Read1302(DS1302_DAY); /读取日数 temp=temp+1; /日数加1 Up_Key_flag=1; if(temp>0x31) temp=1; break; case 6: temp=Read1302(DS1302_MONTH); /读取月数 temp=temp+1; /月数加1 Up_Key_flag=1; if(temp>0x12) temp=1; break; case 7: temp=Read1302(DS1302_YEAR); /读取年数 temp=temp+1; /年数加1 Up_Key_flag=1; if(temp>0x85) temp=0; break; default:break; while(Up_Key=0); while(port02=1); /个人收集整理 勿做商业用途void Down_Keykey()/降序按键 Down=1; if(Down=0|port03=1) mdelay(8); switch(count) case 1: temp=Read1302(DS1302_S