《万年历课程设计(完整版).pdf》由会员分享,可在线阅读,更多相关《万年历课程设计(完整版).pdf(28页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、 万年历课程设计(完整版)基于 51 单片机的万年历的设计 学生姓名 学 号 所在专业 电子信息工程 所在班级 电子 1111 小组成员 指导教师 完成日期 基于 51 单片机的万年历的设计 1 需求分析 1.1“需“的分析 万年历是一个可以显示当前时间、日历、温度信息以及设置闹钟报警的应用系统,其具有以下功能:(1)可以显示当前的时间信息;(2)可以显示当前的温度;(3)可以手动修改时间;(4)可以设置闹钟,并且达到设置的时间点发出音响信号。1.2“求“的分析 设计万年历,需要考虑以下几方面的内容:(1)如何获取当前的时间信息,这些时间信息包括时、分、秒、年、月、日、和星期;(2)如何获得当
2、前的温度信息,精确到 1C 即可;(3)提供必要的用户输入设置通道;(4)提供相应的显示和报警部件;(5)需要写出合适的软件应用代码。2 方案设计与论证 2.1 时间获取方法的方案设计与论证 方案一:使用单片机的内部定时器进行定时,使用软件算法来计算当前的时间信息。此种方案虽然可以不使用时钟芯片,节约成本,但是时间的精准度一般,软件代码复杂。方案二:采用外部扩展实时时钟芯片 DS12C887 的方式来获取相应的时间信息。DS12C887 是一种高性能的芯片,能自动产生年、月、日、时、分、秒等信息,并有闰年修正功能。采用双电源供电方式,可设置备用电源充电方式,提供了对后备电源进行涓细电流的充电的
3、能力,在能满足精度要求的同时价格也比较便宜,性价比较高。所以采用方案二。2.2 显示模块的方案设计与论证 3.2 万年历的电路 图 3-2 万年历的电路 3.3 硬件基础 DS1302 的硬件基础:(1)时钟芯片 DS1302 的工作原理:DS1302 在每次进行读、写程序前都必须初始化,先把 SCLK 端置“0”,接着把 RST端置“1”,最后才给予 SCLK 脉冲。图 5 为 DS1302 的控制字的位 7 必须置 1,若为 0 则不能把对 DS1302 进行读写数据。对于位 6,若对程序进行读/写时 RAM=1,对时间进行读/写时,CK=0。位 1 至位 5 指操作单元的地址。位 0 是
4、读/写操作位,进行读操作时,该位为 1;该位为 0 则表示进行的是写操作。控制字节总是从最低位开始输入/输出的。DS1302 的日历、时间寄存器内容:“CH”是时钟暂停标志位,当该位为 1 时,时钟振荡器停止,DS1302 处于低功耗状态;当该位为 0 时,时钟开始运行。“WP”是写保护位,在任何的对时钟和 RAM 的写操作之前,WP 必须为 0。当“WP”为 1 时,写保护位防止对任一寄存器的写操作。(2)DS1302 的控制字节 DS1302 的控制字如表-1 所示。控制字节的高有效位(位 7)必须是逻辑 1,如果它为 0,则不能把数据写入 DS1302 中,位 6 如果 0,则表示存取日
5、历时钟数据,为 1 表示存取 RAM 数据;位 5 至位 1 指示操作单元的地址;最低有效位(位 0)如为 0 表示要进行写操作,为 1 表示进行读操作,控制字节总是从最低位开始输出。表 3-1 DS1302 的控制字格式 1 RAM /CK A4 A3 A2 A1 A0 RD /WR (3)数据输入输出(I/O)在控制指令字输入后的下一个 SCLK 时钟的上升沿时,数据被写入 DS1302,数据输入从低位即位 0 开始。同样,在紧跟 8 位的控制指令字后的下一个 SCLK 脉冲的下降沿读出 DS1302 的数据,读出数据时从低位 0 位到高位 7。如下图 3-3 所示:图 3-3 DS130
6、2 读/写时序图 (4)DS1302 的寄存器 DS1302 有 12 个寄存器,其中有 7 个寄存器与日历、时钟相关,存放的数据位为 BCD码形式,其日历、时间寄存器及其控制字见表 3-2。表 3-2 DS1302 的日历、时间寄存器 此外,DS1302 还有年份寄存器、控制寄存器、充电寄存器、时钟突发寄存器及与RAM 相关的寄存器等。时钟突发寄存器可一次性顺序读写除充电寄存器外的所有寄存器内容。DS1302 与 RAM 相关的寄存器分为两类:一类是单个 RAM 单元,共 31 个,每个单元组态为一个 8 位的字节,其命令控制字为 C0HFDH,其中奇数为读操作,偶数为写操作;另一类为突发方
7、式下的 RAM 寄存器,此方式下可一次性读写所有的 RAM 的 31个字节,命令控制字为 FEH(写)、FFH(读)。1602 液晶的硬件基础:1602 通常有 14 条引脚线或 16 条引脚线的 LCD,多出来的 2 条线是背光电源线 VCC(15脚)和地线 GND(16 脚),其控制原理与 14 脚的 LCD 完全一样,引脚定义如表 3-3 所示:表 3-3 1602 的引脚及其对应的功能 1602 支持的一系列指令:(1)清屏指令:表 3-4 清屏指令 (2)归零指令:表 3-5 归零指令 (3)输入方式选择指令:表 3-6 输入方式选择指令 (4)显示开关控制指令:表 3-7 显示开关
8、控制指令 (5)光标和画面移动指令:表 3-8 光标和画面移动指令 (6)功能设定指令:表 3-9 功能设定指令 (7)CGRAM 设置指令:表 3-10 CGRAM 设置指令 (8)DDRAM 设置指令:表 3-11 DDRAM 设置指令 (9)读 BF 和 AC 指令:表 3-12 读 BF 和 AC 指令 (10)写数据指令:表 3-13 写数据指令 (11)读数据指令:表 3-14 读数据指令 3 软件设计 是 否 是 图 4-1 万年历的工作流程 4 总结 4.1 仿真结果及分析 经过多次调试、修改程序,万年历终于在 pruteus 正常运行起来了。前面由于初次写的代码有较多不完善的
9、地方,所以调试的时候出了很多大的问题,第一次发现大的问系统初始化 初始化时钟芯片和液晶显示 读取 DS1302 的时钟数据 读取 DS18B20 数据并且进行相应的处理 显示当前的数时钟按键驱动蜂鸣设置闹钟 题后,团队认真地讨论并修改了程序,后面调试遇到的都是些小问题,无非是打漏或者打错这类问题。4.2 心得 工程上的问题很多不是一个人的能力可以解决的,所以不能老是想着单干、蛮干,一定要发挥团队的力量,多讨论,讨论往往可以碰撞出思想的火花来。做事的时候一定要静下心来,保持专注,不能老想着快,不然会弄出很多本可以避免的问题,敲代码的时候,为了求快,结果调试的遇到小问题花了可不少时间才解决。本次课
10、程设计,让我知道了要想解决工程上的问题,必须现将问题具体化、模块化,然后再逐个击破,这样才能更有效率地解决整个工程设计再到实物的问题。附录一:仿真图 附录二:程序#include#include#define TIME(0X10000-50000)#define FLAG 0XEF /闹钟标志/引脚连接图 sbit rst=P35;sbit clk=P34;sbit dat=P33;sbit rs=P15;sbit rw=P16;sbit e=P17;sbit DQ=P14;/温度输入口 sbit ACC_7=ACC7;/全局变量及常量定义 unsigned char i=20,j,time1
11、16;unsigned char alarm2,time215,time3;unsigned char code Day=31,28,31,30,31,30,31,31,30,31,30,31;/12个月的最大日期(非闰年)/音律表 unsigned int code table1=64260,64400,64524,64580,64684,64777,64820,64898,64968,65030,65058,65110,65157,65178,65217;/发声部分的延时时间 unsigned char code table2=0 x82,1,0 x81,0 xf4,0 xd4,0 xb4
12、,0 xa4,0 x94,0 xe2,1,0 xe1,0 xd4,0 xb4,0 xc4,0 xb4,4,0;/LCD 自建字 unsigned char code tab=0 x18,0 x1b,5,4,4,5,3,0,0 x08,0 x0f,0 x12,0 x0f,0 x0a,0 x1f,0 x02,0 x02,/年 0 x0f,0 x09,0 x0f,0 x09,0 x0f,0 x09,0 x11,0 x00,/月 0 x0f,0 x09,0 x09,0 x0f,0 x09,0 x09,0 x0f,0 x00;/日 /*温度小数部分用查表法*/unsigned char code dit
13、ab16=0 x00,0 x01,0 x01,0 x02,0 x03,0 x03,0 x04,0 x04,0 x05,0 x06,0 x06,0 x07,0 x08,0 x08,0 x09,0 x09;/闹钟中用的全局变量 unsigned char th1,tl1;unsigned char temp_data2=0 x00,0 x00;/读出温度暂放 bit flag;/18b20 存在标志位/*11 微秒延时函数*/delay(unsigned int t)for(;t0;t-);/*18B20 复位函数*/RST18B20(void)unsigned char i;DQ=1;_nop_
14、();_nop_();DQ=0;/delay(50);/550us DQ=1;/delay(6);/66us for(i=0;i0;i-)/DQ=1;_nop_();_nop_();DQ=0;_nop_();_nop_();_nop_();_nop_();_nop_();/5us DQ=val&0 x01;/最低位移出 delay(6);/66us val=val/2;/右移一位 DQ=1;delay(1);/*18B20 读 1 个字节函数*/从总线上读取一个字节 unsigned char Read1Byte18B20(void)unsigned char i;unsigned char
15、value=0;for(i=8;i0;i-)DQ=1;_nop_();_nop_();value=1;DQ=0;/_nop_();_nop_();_nop_();_nop_();/4us DQ=1;_nop_();_nop_();_nop_();_nop_();/4us if(DQ)value|=0 x80;delay(6);/66us DQ=1;return(value);/*读出温度函数*/Read18B20()RST18B20();/总线复位 if(!flag)/判断 ds1820 是否存在?若 ds18b20 不存在则返回 return;/Write1Byte18B20(0 xCC);
16、/Skip ROM /Write1Byte18B20(0 x44);/发转换命令 /delay(70);Write1Byte18B20(0 xCC);/发 Skip ROM 命令 Write1Byte18B20(0 xBE);/发读命令 temp_data0=Read1Byte18B20();/温度低 8 位 temp_data1=Read1Byte18B20();/温度高 8 位 RST18B20();Write1Byte18B20(0 xCC);/Skip ROM Write1Byte18B20(0 x44);/发转换命令 /*温度数据处理函数*/DealTempData()unsigne
17、d char n=0,m;if(temp_data1127)/负温度求补码 temp_data1=(256-temp_data1);temp_data0=(256-temp_data0);n=1;time213=ditabtemp_data0&0 x0f+0;time212=.;m=(temp_data0&0 xf0)4)|(temp_data1&0 x0f)4);/if(n)m-=16;time29=m/100+0;time211=m%100;time210=time211/10+0;time211=time211%10+0;if(time29=0)/最高位为 0 时都不显示 time29=
18、0 x20;if(n)/负温度时最高位显示-time29=-;if(time210=0)if(n)time210=-;time29=0 x20;else time210=0 x20;if(time211=0&time213=0)time211=time212=0 x20;delay1ms(unsigned char time)/延时 1ms unsigned char i,j;for(i=0;itime;i+)for(j=0;j250;j+);/使能 1602 EnableLCD()rs=0;rw=0;e=0;delay1ms(3);e=1;/写 LCD 函数 WriteLCD(unsigne
19、d char i)P0=i;rs=1;rw=0;e=0;delay1ms(2);e=1;/写 LCD 内部地址函数 WriteLCDRAM(unsigned char data*address,m)unsigned char i,j;for(i=0;im;i+,address+)j=*address;WriteLCD(j);/LCD 显示 LCDSHOW()P0=0XC;/显示器开、光标关 EnableLCD();P0=0 x80;/写入显示起始地址 EnableLCD();WriteLCDRAM(time1,16);P0=0 xc1;/写入显示起始地址 EnableLCD();WriteLC
20、DRAM(time2,15);/自建字函数库 DesignHZ()unsigned char i;P0=0 x40;EnableLCD();for(i=0;i32;i+)WriteLCD(tabi);delay1ms(2);/写 DS1302 子程序 WriteDS1302(unsigned char address)unsigned char i;clk=0;_nop_();rst=1;_nop_();for(i=0;i=1;clk=0;/读 DS1302 子程序 unsigned char ReadDS1302()unsigned char i,j=0;for(i=0;i=1;_nop_(
21、);clk=0;_nop_();if(dat)j|=0 x80;_nop_();clk=1;return(j);/部分显示数据初始化 TimeInit()time11=time113=time28=time29=time210=0 x20,time214=0;time16=1,time19=2,time112=3,time12=2,time13=0;time114=W,time22=time25=:;WriteDS1302(0 xc1);alarm0=ReadDS1302();rst=0;WriteDS1302(0 xc3);alarm1=ReadDS1302();rst=0;WriteDS1
22、302(0 xc5);time10=ReadDS1302();rst=0;/读取时间 ReadTime()unsigned char i,m,n;WriteDS1302(0 x8d);/读取年份 m=ReadDS1302();rst=0;time14=m/16+0 x30;time15=m%16+0 x30;WriteDS1302(0 x8b);/读取星期 m=ReadDS1302();rst=0;time115=m+0 x30;for(i=7,n=0 x89;i11;i+=3,n-=2)/读取月份和日期 WriteDS1302(n);m=ReadDS1302();rst=0;time1i=m
23、/16+0 x30;time1i+1=m%16+0 x30;for(m=0,i=0,n=0 x85;i7;i+=3,n-=2,m+)/读取时,分,秒 WriteDS1302(n);timem=ReadDS1302();rst=0;time2i=timem/16+0 x30;time2i+1=timem%16+0 x30;time0()interrupt 1 using 1 i-;if(i=0)if(j!=0)j-;i=20;TH0=TIME/256,TL0=TIME%256;/闹钟部分 intime1()interrupt 3 TH1=th1,TL1=tl1;P3_2=!P3_2;/显示闹钟设
24、置数据 showalarm()unsigned char i,j,a,b,n;ET1=1;for(j=0;j=4;if(a=0)TR1=0;goto D1;a=(-a)1)/2;TH1=th1=table1a/256,TL1=tl1=table1a%256;TR1=1;D1:do b-;for(n=0;n2)i+;j=(time110&0 xf)*10+(time111&0 xf);i+=j;i%=7;if(i=0)i=7;return(i);/设置时间 settime()unsigned char i=0 x85,year,month,day,n;time26=time27=0 x30,ti
25、me114=time115=0 x20;LCDSHOW();while(1)P0=0 xe;/显示器开、光标开 EnableLCD();P0=i;/定光标 EnableLCD();P2=0 xf7;if(P2!=0XF7)delay1ms(100);/延时 0.1s 去抖动 if(P2!=0XF7)j=7;if(P2=0X77)i+=3;if(i=0 x8e)i=0 xc2;else if(i0 xc5)i=0 x85;else if(P2=0 xb7)year=(time14&0 xf)*10+(time15&0 xf);month=(time17&0 xf)*10+(time18&0 xf
26、);day=(time110&0 xf)*10+(time111&0 xf);if(i=0 x85)year+;if(year99)year=0;if(year%4)!=0)if(month=2&day=29)day=28;else if(i=0 x88)month+;if(month12)month=1;if(dayDaymonth-1)day=Daymonth-1;if(month=2&(year%4)=0)day=29;else if(i=0 x8b)day+;if(dayDaymonth-1)if(month=2&(year%4)=0)if(day29)day=1;if(month!=
27、2)day=1;else if(i=0 xc2)n=(time20&0 xf)*10+(time21&0 xf);n+;if(n23)n=0;time20=n/10+0 x30;time21=n%10+0 x30;else n=(time23&0 xf)*10+(time24&0 xf);n+;if(n59)n=0;time23=n/10+0 x30;time24=n%10+0 x30;time14=year/10+0 x30;time15=year%10+0 x30;time17=month/10+0 x30;time18=month%10+0 x30;time110=day/10+0 x3
28、0;time111=day%10+0 x30;LCDSHOW();else if(P2=0 xd7)WriteDS1302(0 x8c);WriteDS1302(time14&0 xf)*16+(time15&0 xf);rst=0;WriteDS1302(0 x8a);WriteDS1302(setweek();rst=0;for(i=7,n=0 x88;i11;i+=3,n-=2)WriteDS1302(n);WriteDS1302(time1i&0 xf)*16+(time1i+1&0 xf);rst=0;for(i=0;i7;i+=3,n-=2)WriteDS1302(n);Write
29、DS1302(time2i&0 xf)*16+(time2i+1&0 xf);rst=0;TR0=0;time114=W;return;else TR0=0;time114=W;return;if(j=0)TR0=0;time114=W;return;/设置闹钟 setalarm()unsigned char i,n;for(i=1;i0 xc5)i=0 xc2;else if(P2=0 xb7)if(i=0 xc2)n=(time20&0 xf)*10+(time21&0 xf);n+;if(n23)n=0;time20=n/10+0 x30;time21=n%10+0 x30;else n
30、=(time23&0 xf)*10+(time24&0 xf);n+;if(n59)n=0;time23=n/10+0 x30;time24=n%10+0 x30;LCDSHOW();else if(P2=0 xd7)WriteDS1302(0 xc0);WriteDS1302(time20&0 xf)*16+(time21&0 xf);rst=0;WriteDS1302(0 xc2);WriteDS1302(time23&0 xf)*16+(time24&0 xf);rst=0;time10=FLAG;WriteDS1302(0 xc4);WriteDS1302(time10);rst=0;
31、TR0=0;TimeInit();return;else TR0=0;TimeInit();return;if(j=0)TR0=0;TimeInit();return;main()IE=0X82;TMOD=0 x11;WriteDS1302(0 x8E);/禁止写保护 WriteDS1302(0);rst=0;P0=1;/清屏并光标复位 EnableLCD();P0=0X38;/设置显示模式:8 位 2 行 5x7 点阵 EnableLCD();P0=6;/文字不动,光标自动右移 EnableLCD();DesignHZ();/自建字 TimeInit();while(1)ReadTime()
32、;/读取时间 Read18B20();/读出 18B20 温度数据 DealTempData();/处理温度数据 LCDSHOW();/显示时间 if(time10!=0 x20)if(time0=alarm0)if(time1=alarm1)if(time2=0)showalarm();P2=0 xf7;if(P2&0XF0)!=0XF0)delay1ms(100);/延时 0.1s 去抖动 if(P2&0XF0)!=0XF0)j=7;TH0=TIME/256,TL0=TIME%256;TR0=1;if(P2=0 x77)settime();else if(P2=0XB7)setalarm();else if(P2=0XD7)TR0=0;if(time10=FLAG)time10=0 x20;else time10=FLAG;WriteDS1302(0 xc4);WriteDS1302(time10);rst=0;delay1ms(100);