基于单片机的电子时钟设计_第1页
基于单片机的电子时钟设计_第2页
基于单片机的电子时钟设计_第3页
基于单片机的电子时钟设计_第4页
基于单片机的电子时钟设计_第5页
已阅读5页,还剩20页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

-PAGE25-阳泉职业技术学院专科学校 -PAGE1- 程序概述DS1302时钟芯片具有通电自动计时的功能。向DS1302中写入一个初值,如写入2011-05-0100:00:00星期日,在通电时,时间就会自动走:过60秒分加1;过60分时加1;过24小时天加1,星期日变成星期一;一周有7天,芯片内的周信息每7天一循环;芯片能够自动判断每月有多少天,5月有31天,31天后,月加1。采用DS1302时钟芯片的单片机时钟,其实质就是读取时钟芯片内的时钟信息并把它显示出来。只要时间初值正确,时钟就能一直精准的走下去。调整时间日期,实质就是向DS1302时钟芯片重新写入初值。电子时钟的主程序框图如图10所示图4.1主程序框图4.2延时函数voiddelay(uintz){ uintx,y; for(x=z;x>0;x--) for(y=110;y>0;y--);}由for循环构成的延时函数,z的取值为这个函数的延时ms数,如delay(200);大约延时200ms.delay(500);大约延时500ms。因下文多次用到,固在此先作说明。4.3对DS1302读写操作函数在对DS1302时钟芯片操作前,应对其操作时序有所了解,参看前文DS1302介绍。DS1302采用串行方式与单片机进行通信,一个机器周期只能读写一个字节的一位,因此,在单片机与DS1302芯片间传输一字节(8位)数据,要分8次进行,且先从低位开始传输。4.3.1向DS1302写数据/***************************写数据字节子函数**************************/voidwrite_1302_byte(uchartemp)//用来发送8位数据信息{ uchari; for(i=0;i<8;i++)//循环8次写入数据 { sck=0; sda=temp&0x01;//每次传输低字节 temp>>=1;//右移一位 sck=1;//在SCK上升沿的时候字节写入DS1302 }}/*************************1302写数据子函数**************************/voidwrite_1302(ucharaddd,uchardat){ rst=0; _nop_(); sck=0; _nop_(); rst=1; _nop_(); write_1302_byte(addd);//发送地址 write_1302_byte(dat);//发送数据 rst=0;}4.3.2从DS1302读数据/***************************读DS1302数据函数*************************/ucharread_1302(ucharadd)//输入地址add,返回读取的数据{ uchari,temp=0x00; rst=0; sck=0; rst=1; write_1302_byte(add); for(i=0;i<8;i++)//循环8次读取 { if(sda) temp|=0x80;//每次传输低字节,等价于temp=temp|0x80 sck=0; temp>>=1;//右移一位 sck=1; //sck被置高,在其下一次变为0时,数据被写入 } rst=0;//以下为DS1302复位的稳定时间 sck=0; sck=1; sda=0; sda=1; return(temp);//将temp值返回}4.4显示函数对1602进行操作前要对其进行初使化,初使化完成后它才能正常显示。如果想在1602液晶的某一个位置显示一个内容,要先对其写入一个指令:在什么地方显示。然后再对其写入一个数据:要显示什么内容。对1602的液晶初使化,需要用写入指令的方式完成。4.4.1向1602液晶中写一个指令voidwrite_com(ucharcom){ lcdwr=0; //lcdwr为读写控制端,lcdwr=0,这里可不写 lcdrs=0;//液晶rs接口为0时,写指令,rs为1时写数据 P0=com;//将要写的指令赋给P0口, delay(5);//由1602读写操作时序图,先将指令赋给P0口,延时后将使能 lcden=1;端lcden置高,再延时一段时间,然后将lcden置低,这样指令 delay(5); 就写入到LCD了 lcden=0;}4.4.2向液晶写数据voidwrite_data(uchardate),与写指令类似,这里lcdrs设为1{ lcdrs=1; P0=date; delay(5); lcden=1; delay(5); lcden=0;}4.4.3初使化1602液晶此函数首先对液晶进行初使化,使其处于待命状态,然后将时钟框架显示出来:年月日之间的“-”,时分秒之间的“:”,还有世纪年的高两位。voidinit_1602(){ ucharnum; lcdwr=0; lcden=0; write_com(0x38);//设置LCD为16*2显示、5*7点阵、8位数据接口模式 write_com(0x0c);//开显示、不显示光标 write_com(0x06);//写一个字符后,地址指针加1 write_com(0x01);//显示清0 write_com(0x80);//将指针指向初始位置 for(num=0;num<14;num++)//循环函数,用于将"20--"写入液晶 write_data(table[num]); write_com(0x80+0x40+4);//将指针指向1602液晶的第二行,第四个字段 for(num=0;num<8;num++)//功能与上同,用于将"::"写入 write_data(table1[num]);}4.4.4如何在液晶上显示时间、日期及周DS1302中的时间、日期等信息是以BCD码的形式存放的,要先将从1302中读取的数据转化成10进制,然后显示在液晶上相应的位置。/***************************显示时间、日期子函数***********************/voidwrite_sfm(ucharadd,uchartime)//用于在1602上显示年、月、日、时、分、秒。{Add为显示位置,time为要显示的内容 ucharshi,ge; shi=time/16;//将从DS1302中读取的BCD码数据转化成10进制个位和10 ge=time%16;进制十位 write_com(add+0x80);//定义显示在液晶的什么位置 write_data(0x30+shi);//由1602液晶字库可知,0~9的数据码分别对应0x30~0x39 write_data(0x30+ge); //初使化中设定了写一个字符后,地址指针加1,因此这里}不用重新光标定位/**************************显示周子函数*******************************/voidwrite_zhou(uchartime1),用于在1602上显示周信息,与显示{时间日期子函数类似 ucharge; ge=time1%16;//一周七天,因此只需个位 write_com(0x80+13); write_data(0x30+ge);}4.5按键函数此电子时钟共有4个按键,S1、S2、S3与调时有关图按键程序如图11,S4为12小时切换键S1功能键:在24小时显示模式下,该键被第一次按下后进入秒调整,液晶显示器上的时间停止走动。模式,再次按下后进入分调整模式,接着是调时模式、调年模式、调月模式、调日模式、调周模式,当第八次按下该键后退出S2调整键:在调整模式下,该键每按一次,相应时间或日期加1 S3确定键:在调整模式下,该键被按下后,退出调整模式,并将调整后的时间、日期写入DS1302 S4显示模式调整键用于对时间的12/24小时模式的切换图4.2按键程序图4.5.112/24小时显示模式切换键时钟默认在24小时模式下运行,定义一个标志位flag1。第一次按下切换键,把瞬时时间转化成12小时制,令flag1=1。并写入DS1302芯片,此后DS1302芯片在12小时模式下运行。第二次按下切换键,即返回24小时显示模式,令flag1=0,把瞬时时间转化成24小时模式并写入DS1302芯片。有关DS1302的时间寄存器存放形式参阅前文。如24小时模式下的22:00,存放的形式为00100010。12小时模式下的22点,也就是10:00PM,存放形式为10110000,在切换的一瞬间,我们只需要把小时数据0xb0写入到DS1302,这样就能让时钟芯片在12小时模式下运行了。需要注意的是,12小时模式下,只用到后5位来表示时间,第6位用来表示AM或PM信息,如果真接用24小时模式下的显示方法是会出错的,这里我们需要对时进行转化。首先提取AM/PM信息,让其显示。然后提取其后5位时间,显示时间。在主函数部分如果检测到flag1=1,就进行转换。12转24小进模式与其类似,不再赘述。if(s1num==0&&s4==0)//设置键没被按下,且12/24小时模式切换键被按下后 { delay(5); if(s4==0) { s2num++; while(!s4); if(s2num==1)//24小时切换成12小时 { intge,shi; flag1=1; hour=read_1302(0x85); ge=hour%16; shi=hour/16; if(shi<=1&&ge<=1)//时小于12 { if(shi==0&&ge==0) hour=0x92; else { hour=hour&0x9f; hour=hour+0x80; } } if(shi==1&&ge==2)//12点 { hour=hour+0x80; hour=hour+0x20; } if(shi>=1&&ge>=3&&ge<=9)//13点~19点 { hour=hour-0x12; hour=hour+0x80; hour=hour+0x20; } if(shi==2&&ge==0)//20点 { hour=0xa8; } if(shi==2&&ge==1) hour=0xa9; if(shi==2&&ge==2) { hour=0xb0; } if(shi==2&&ge==3) hour=0xb1; write_1302(0x8e,0x00); write_1302(0x84,hour); write_1302(0x8e,0x80); } if(s2num==2)//12小时切换成24小时 { intap1,ap2; hour=read_1302(0x85); ap1=hour&0x20;//ap1=0时AM,ap1!=0时PM ap2=hour&0x1f; if(ap1==0&&ap2!=0x12)//AM,且小时不等于12 { hour=hour&0x1f; } if(ap1==0&&ap2==0x12)//AM,小时等于12,即0点 { hour=0x00; } if(ap1!=0&&ap2==0x08)//下午8点,转化成20点 hour=0x20; if(ap1!=0&&ap2==0x09)//下午9点,转化成21点 hour=0x21; if(ap1!=0&&ap2!=0x08&&ap2!=0x09)//PM,且不是8点或9点 { hour=hour-0xa0; hour=hour+0x12; } if(ap1!=0&&ap2==0x12)//12点PM,也就是24小时模式下的12点 hour=hour-0xa0; write_1302(0x8e,0x00);//把转化成24小进模式后的时写入DS1302 write_1302(0x84,hour); write_1302(0x8e,0x80); flag1=0; s2num=0;//按键次数清0 } } }4.5.2功能键函数if(s1==0&&s2num==0)//如果S1被按下当在12小时模式下显示时,按此键无效 { delay(5); if(s1==0)//以上用于键盘消抖,差小误差 { flag=1;//时钟暂停标志位,flag=1时液晶时钟计时暂停 s1num++;//s1num表示按键被按下的次数,按下次数加1 while(!s1);//松手后,继续执行后面指令 if(s1num==1)//如果S1按下一次,秒的个位位置闪烁光标 { write_com(0x80+0x40+11); write_com(0x0f); } if(s1num==2)//如果S1按下两次,先将设置后的秒写到DS1302,再在分的个位位置闪烁光标 { write_1302(0x8e,0x00);//禁止写保护 write_1302(0x80,miao);//写入秒信息 write_1302(0x8e,0x80);//开写保护 write_com(0x80+0x40+8);//指针指到分钟显示的位置 write_com(0x0f);//闪烁光标 } if(s1num==3)//以下同上面类似 { write_1302(0x8e,0x00); write_1302(0x82,fen); write_1302(0x8e,0x80); write_com(0x80+0x40+5); write_com(0x0f); } if(s1num==4) { write_1302(0x8e,0x00); write_1302(0x84,hour); write_1302(0x8e,0x80); write_com(0x80+4); write_com(0x0f); } if(s1num==5) { write_1302(0x8e,0x00); write_1302(0x8c,nian); write_1302(0x8e,0x80); write_com(0x80+7); write_com(0x0f); } if(s1num==6) { write_1302(0x8e,0x00); write_1302(0x88,yue); write_1302(0x8e,0x80); write_com(0x80+10); write_com(0x0f); } if(s1num==7) { write_1302(0x8e,0x00); write_1302(0x86,ri); write_1302(0x8e,0x80); write_com(0x80+13); write_com(0x0f); } if(s1num==8) { write_1302(0x8e,0x00); write_1302(0x8a,zhou); write_1302(0x8e,0x80); flag=0; //时钟暂停标志位flag=0,退出调整模式 write_com(0x0c);//不显示光标 s1num=0;//按键次数清0 } } }4.5.3调整键函数每按一次此键,相应的时间或日期加1,但是时间、日期是以BCD码的形式存放在DS1302时钟芯片的,如果直接对其进行操作,如秒,假如秒的初始时间是00,它在DS1302芯片中存放的值为00000000,它的后4位代表秒的个位,在时钟芯片自动计时时,10秒后它的后四位为10时,它能自动进位,变为00010000。但是在调秒状态下按10次S2键,我们希望秒变成00010000,实际上其值为00001010。因此我们引入X1和X2,分别代表秒的十位和个位,每按一次S2键,x2加1,每当X2为10时,X1加1,X2重新为0.这样便不会出现上面的错误了。 在调节日期的过程中,我们引入了防错机制,平年2月,你不可能调到29日,4月,你不可能调到31日。这一功能是如何实现的呢?首先判断是平年还是闰年,本时钟的年调节区间为2010年-2079年。在这一区间内,凡是能被4整除的年都是闰年,否则为平年。再判断是几月,4、6、9、11这四个月份有30天,平年2月28天,闰年2月29天,其他月份31天。正因为此,调节日期的顺序为年-月-日if(s2==0)//时间、日期调整键 { delay(5); if(s2==0) { while(!s2); if(s1num==1) { intx1,x2; x1=miao%16; x2=miao/16; x1++; if(x1==10) { x1=0; x2++; if(x2>=6) x2=0; } miao=x1+x2*16; write_sfm(10+0x40,miao); write_com(0x80+0x40+11); } if(s1num==2) { intx3,x4; x3=fen%16; x4=fen/16; x3++; if(x3==10) { x3=0; x4++; if(x4>=6) x4=0; } fen=x3+x4*16; write_sfm(7+0x40,fen); write_com(0x80+0x40+8); } if(s1num==3) { intx5,x6; x5=hour%16; x6=hour/16; x5++; if(x6>=2&&x5>=4) { x5=0; x6=0; hour=0; } if(x5==10) { x5=0; x6++; } hour=x5+x6*16; write_sfm(4+0x40,hour); write_com(0x80+0x40+5); } if(s1num==4) { intx7,x8; x7=nian%16; x8=nian/16; x7++; if(x7==10) { x7=0; x8++; if(x8>=8) x8=1; } nian=x7+x8*16; write_sfm(3,nian); write_com(0x80+4); } if(s1num==5) { intx5,x6; x5=yue%16; x6=yue/16; x5++; if(x6>=1&&x5>=3) { x5=1; x6=0; } if(x5==10) { x5=0; x6++; } yue=x5+x6*16; write_sfm(6,yue); write_com(0x80+7); } if(s1num==6)//此条判断每月天数,包括平年闰年 { intx5,x6,ge2,shi2,mon,ge1,shi1,year,leap; x5=ri%16; x6=ri/16; x5++; nian=read_1302(0x8d); //读取年数据 ge1=nian%16; shi1=nian/16; year=ge1+shi1*10; if(year%4==0) //判断是否为闰年 leap=1; elseleap=0; yue=read_1302(0x89);//读取月数据 ge2=yue%16; shi2=yue/16; mon=ge2+shi2*10; if(mon==2&&leap==0)//平年2月,28天 { if(x6>=2&&x5>=9) { x5=1; x6=0; } if(x5==10) { x5=0; x6++; } } if(mon==2&&leap==1) //闰年2月,29天 { if(x5==10) { x5=0; x6++; if(x6>=3) {x5=1;x6=0;} } } if(mon==4||mon==6||mon==9||mon==11) //4、6、9、11月30天 { if(x6>=3&&x5>=1) { x5=1; x6=0; } if(x5==10) { x5=0; x6++; } } else//1、3、5、7、8、10、12月31天 { if(x6>=3&&x5>=2) { x5=1; x6=0; } if(x5==10) { x5=0; x6++; } } ri=x5+x6*16; write_sfm(9,ri); write_com(0x80+10); } if(s1num==7) { zhou++; if(zhou>=8) zhou=1; write_zhou(zhou); write_com(0x80+13); } } } }}4.5.4确定键在调时模式下,按下确写键后,把调好的时间写入DS1302时钟芯片并退出调整模式,时钟显示暂停标志位清0,时钟继续计时。{ if(s3==0)//如果确定键按下 { delay(5); if(s3==0)//延时并重新检测S3是否按下,用于差小误差 {while(!s3);//在松手时将调整后的时间、日期及周信息写入 write_1302(0x8e,0x00);//DS1302,退出调整模式,按键数清0 write_1302(0x80,miao); write_1302(0x84,hour); write_1302(0x82,fen); write_1302(0x8a,zhou); write_1302(0x8c,nian);//年 write_1302(0x88,yue);//月 write_1302(0x86,ri); write_1302(0x8e,0x80) flag=0; write_com(0x0c); s1num=0; } }4.6主函数voidmain(){intap; init_1602(); //inital_1302(); while(1) { keyscan(); if(flag==0) { miao=read_1302(0x81); fen=read_1302(0x83); hour=read_1302(0x85); if(flag1==1)//flag1=1时,在12模式下显示 { ap=hour&0x20; //判断AM、PM位是0还是1 if(ap==0x20) { write_com(0x80+0x40+14); write_data('P'); write_data('M'); } if(ap==0) { write_com(0x80+0x40+14); write_data('A'); write_data('M'); } hour&=0x1f;/提取有效时间,用于显示 } if(flag1==0)//flag1=0,在24小时模式下显示 { write_com(0x80+0x40+14); write_data(0x20);//0x20是1602字库中的空字符,即不显示内 write_data(0x20);//容,用来清空AM或PM的显示位置 } ri=read_1302(0x87); yue=read_1302(0x89); zhou=read_1302(0x8b); nian=read_1302(0x8d); write_sfm(10+0x40,miao); write_sfm(7+0x40,fe

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论