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