8098单片机实现日历时钟的显示方法
基于单片机的电子万年历设计

基于单片机的电子万年历设计一、概述随着科技的快速发展和人们对生活品质的追求,电子设备在日常生活中扮演着越来越重要的角色。
电子万年历作为一种集日期、时间显示于一体的实用电子产品,已经深入到人们的日常生活和工作中。
传统的机械式日历已经无法满足现代人对时间精确性和功能多样性的需求,基于单片机的电子万年历设计应运而生,成为了当前研究的热点之一。
基于单片机的电子万年历设计,旨在利用单片机(如STC89CAT89C51等)的强大计算和控制能力,结合液晶显示屏(LCD)、按键输入等外设,实现时间的准确显示、日期的自动更新、闹钟提醒、温度显示等多样化功能。
该设计不仅具有高度的集成性和可靠性,而且能够通过编程实现各种定制化的功能,满足不同用户的需求。
本文将对基于单片机的电子万年历设计进行详细的介绍和分析,包括设计思路、硬件组成、软件编程等方面。
通过本文的阅读,读者可以了解电子万年历的基本原理和设计方法,掌握单片机在电子万年历设计中的应用技巧,为实际的开发工作提供有益的参考和借鉴。
1.1 研究背景与意义随着科技的不断进步,人们日常生活和工作中对于时间的精度和便捷性的要求日益提高。
传统的机械式日历和简单的电子时钟已经无法满足现代生活的需求。
电子万年历作为一种集时间显示、日历查询、定时提醒等多功能于一体的电子装置,在日常生活、工作乃至科研领域都具有广泛的应用价值。
基于单片机的电子万年历设计,不仅可以提供准确的时间显示,还能实现复杂的日期计算、农历显示、节假日提示等功能,极大地提高了时间管理的效率和便捷性。
单片机作为一种集成度高、功耗低、价格适中的微型计算机,非常适合用于小型化、智能化的电子产品设计,如电子万年历。
本研究的意义在于,通过对基于单片机的电子万年历的设计研究,可以推动微型计算机技术和电子时钟技术的融合发展,提升电子产品的智能化水平,满足人们日益增长的生活和工作需求。
同时,该研究还可以为相关领域的技术人员提供参考和借鉴,推动电子万年历产品的不断创新和优化。
单片机制作数字钟(含万年历、秒表功能)

数字钟、万年历制作(基于单片机)电路原理图:程序://********************20131206****数字钟程序#pragma SMALL#include <reg51.h>#include <absacc.h>#include <intrins.h>//********************************************************* *********编译预处理void display(unsigned char *p); //显示函数,P为显示数据首地址unsigned char keytest(); //按键检测函数unsigned char search(); //按键识别函数void alarm(); //闹钟判断启动函数void ftion0(); //始终修改函数void ftion1(); //闹钟修改函数void ftion3(); //日期修改函数void cum(); //加1修改函数void minus(); //减1修改函数void jinzhi(); //进制修改函数void riqi(); //日期void stopwatch(); //秒表函数//********************************************************* *******函数声明sbit P2_7=P2^7;//********************************************************* *******端口定义unsigned char clockbuf[3]={0,0,0};unsigned char bellbuf[3]={0,0,0};unsigned char date[3]={1,1,1}; //日期存放数组unsigned char stop[3]={0,0,0};unsigned char msec1,msec2;unsigned char timdata,rtimdata,dtimdata;unsigned char count;unsigned char *dis_p;unsigned char or; //12进制控制标志unsigned char ri; //日期显示控制标志位unsigned char mb; //秒表控制标志位bit arm,rtim,rhour,rmin,hour,min,sec,day,mon,year; //定义位变量//********************************************************* *****全局变量定义void main(){unsigned char a;or=0; //12进制修改标志清零ri=0;mb=0;P2_7=0;arm=0;msec1=0;msec2=0;timdata=0;rtimdata=0;count=0;TMOD=0x12;TL0=0x06;TH0=0x06;TH1=(65536-10000)/256;TL1=(65536-10000)%256;EA=1;ET0=1;ET1=1;TR0=1;TR1=0;dis_p=clockbuf;while(1){a=keytest();if(a==0x78) //判断是否有键按下{display(dis_p);if(arm==1) alarm();}else{display(dis_p);a=keytest();if(a!=0x78){a=search();switch(a){case 0x00:ftion0();break;case 0x01:ftion1();break;case 0x02:cum();break;case 0x06:jinzhi();break;case 0x03:riqi();break;case 0x04:ftion3();break;case 0x05:minus();break;case 0x07:stopwatch();break;case 0x09:TR1=1;break;case 0x0a:TR1=0;break;case 0x0b:stop[0]=0;stop[1]=0;stop[2]=0;break;default:break;}}}}}//********************************************主函数【完】void display(unsigned char *p){unsigned char buffer[]={0,0,0,0,0,0};unsigned char k,i,j,m,temp;unsigned char led[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};buffer[0]=p[0]/10;buffer[1]=p[0]%10;buffer[2]=p[1]/10;buffer[3]=p[1]%10;buffer[4]=p[2]/10;buffer[5]=p[2]%10;if((sec==0)&&(min==0)&&(hour==0)&&(rmin==0)&&(rhour==0)&&( day==0)&&(mon==0)&&(year==0)) //没有修改标志,正常显示{for(k=0;k<3;k++){temp=0x01;for(i=0;i<6;i++){P0=0x00; //段选端口j=buffer[i];P0=led[j];P1=~temp; //位选端口temp<<=1;for(m=0;m<200;m++);}}}else //若有修改标志,则按以下标志分别显示{if(sec==1||day==1){P1=0x1f;i=buffer[5];P0=led[i];for(m=0;m<200;m++);P1=0x2f;j=buffer[4];P0=led[j];for(m=0;m<200;m++);}if(min==1||rmin==1||mon==1){P1=0x3b;i=buffer[2];P0=led[i];for(m=0;m<200;m++);P1=0x37;j=buffer[3];P0=led[j];for(m=0;m<200;m++);}if(hour==1||rhour==1||year==1) {P1=0x3e;i=buffer[0];P0=led[i];for(m=0;m<200;m++);P1=0x3d;j=buffer[1];P0=led[j];for(m=0;m<200;m++);}}}//**********************************LED显示函数【完】unsigned char keytest(){unsigned char c;P2=0x78; //检测是否有键按下c=P2;c=c&0x78;return(c);}//******************************************键盘检测函数【完】unsigned char search(){unsigned char a,b,c,d,e;c=0x3f;a=0; //行号while(1){P2=c;d=P2;d=d&0x07;if(d==0x03){b=0;break;} //列号else if(d==0x05){b=1;break;}else if(d==0x06){b=2;break;}a++;c>>=1;if(a==5){a=0;c=0x3f;}}e=a*3+b;do{display(dis_p);}while((d=keytest())!=0x78);return(e);}//***********************************************查键值函数【完】void alarm(){if((clockbuf[0]==bellbuf[0])&&(clockbuf[1]==bellbuf[1])){P2_7=1;rtim=1;if(count==10){count=0;P2_7=0;arm=0;rtim=0;}}}//****************************************闹钟判断启动函数【完】void ftion0(){TR0=0;rhour=0;rmin=0;dis_p=clockbuf;rtimdata=0;timdata++;switch(timdata){case 0x01:sec=1;break;case 0x02:sec=0;min=1;break;case 0x03:min=0;hour=1;break;case 0x04:timdata=0;hour=0;TR0=1;break;default:break;}}//*********************************************时钟设置函数【完】void ftion1(){if(TR0==0) TR0=1;sec=0;min=0;hour=0;dis_p=bellbuf;timdata=0;rtimdata++;switch(rtimdata){case 0x01:rmin=1;break;case 0x02:rmin=0;rhour=1;break;case 0x03:rtimdata=0;rhour=0;arm=1;dis_p=clockbuf;break;default:break;}}//*********************************************闹钟设置函数【完】void ftion3(){if(TR0==0) TR0=1;day=0;mon=0;year=0;dis_p=date;timdata=0;rtimdata=0;dtimdata++;switch(dtimdata){case 0x01:day=1;break;case 0x02:day=0;mon=1;break;case 0x03:mon=0;year=1;break;case 0x04:dtimdata=0;year=0;dis_p=clockbuf;break;default:break;}}//*************************************************日期修改函数【完】void minus(){if(sec==1){if(0==clockbuf[2]) clockbuf[2]=59;else clockbuf[2]--;}else if(min==1){if(0==clockbuf[1]) clockbuf[1]=59;else clockbuf[1]--;}else if(hour==1){if(or==0) //判断进制{if(0==clockbuf[0]) clockbuf[0]=23;else clockbuf[0]--;}if(or==1){if(1==clockbuf[0]) clockbuf[0]=12;else clockbuf[0]--;}}else if(rmin==1){if(bellbuf[1]==0) bellbuf[1]=59;else bellbuf[1]--;}else if(rhour==1){if(or==0){if(bellbuf[0]==0) bellbuf[0]=23;else bellbuf[0]--;}if(or==1){if(bellbuf[0]==1) bellbuf[0]=12;else bellbuf[0]--;}}else if(day==1){if(date[2]==1) date[2]=31;else date[2]--;}else if(mon==1){if(date[1]==1) date[1]=12;else date[1]--;}else if(year==1){if(date[0]==1) date[0]=99;else date[0]--;}}//*************************************减1修改功能函数【完】void cum(){if(sec==1){if(59==clockbuf[2]) clockbuf[2]=0;else clockbuf[2]++;}else if(min==1){if(59==clockbuf[1]) clockbuf[1]=0;else clockbuf[1]++;}else if(hour==1){if(or==0) //判断进制{if(23==clockbuf[0]) clockbuf[0]=0;else clockbuf[0]++;}if(or==1){if(12==clockbuf[0]) clockbuf[0]=1;else clockbuf[0]++;}}else if(rmin==1){if(bellbuf[1]==59) bellbuf[1]=0;else bellbuf[1]++;}else if(rhour==1){if(or==0){if(bellbuf[0]==23) bellbuf[0]=0;else bellbuf[0]++;}if(or==1){if(bellbuf[0]==12) bellbuf[0]=1;else bellbuf[0]++;}}else if(day==1){if(date[2]==31) date[2]=1;else date[2]++;}else if(mon==1){if(date[1]==12) date[1]=1;else date[1]++;}else if(year==1){if(date[0]==99) date[0]=0;else date[0]++;}}//*************************************加1修改功能函数【完】void jinzhi(){if(or==0) or=1;else or=0;}//***********************************进制修改控制函数【完】void riqi(){if(ri==0){dis_p=date;}if(ri==1){dis_p=clockbuf;}ri++;if(ri==2) ri=0;}//********************************日期控显示函数【完】void stopwatch(){if(mb==0){dis_p=stop;mb=1;}else{mb=0;dis_p=clockbuf;}}//************秒表**********秒表**********秒表函数【完】void clock() interrupt 1{EA=0;if(msec1!=0x14) msec1++; //6MHz晶振定时10mselse{msec1=0;if(msec2!=100) msec2++; //定时1selse{if(rtim==1) count++; //闹钟启动标志计时10smsec2=0;if(clockbuf[2]!=59) clockbuf[2]++;else{clockbuf[2]=0;if(clockbuf[1]!=59) clockbuf[1]++;else{clockbuf[1]=0;if(or==0){if(clockbuf[0]!=23) clockbuf[0]++;else{clockbuf[0]=0;if((date[1]==1)||(date[1]==1)||(date[1]==1)||(date[1]==3)||(date[ 1]==5)||(date[1]==7)||(date[1]==8)||(date[1]==10)||(date[1]==12)){if(date[2]!=30) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}if((date[1]==4)||(date[1]==6)||(date[1]==9)||(date[1]==11)){if(date[2]!=29) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}if(date[1]==2){if((((date[0]%4==0)&&(date[0]%100!=0))||(date[0]%400==0))){if(date[2]!=28) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}else{if(date[2]!=27) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}}}}if(or==1){if(clockbuf[0]!=12) clockbuf[0]++;else{clockbuf[0]=0;if((date[1]==1)||(date[1]==1)||(date[1]==1)||(date[1]==3)||(date[ 1]==5)||(date[1]==7)||(date[1]==8)||(date[1]==10)||(date[1]==12)){if(date[2]!=30) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}if((date[1]==4)||(date[1]==6)||(date[1]==9)||(date[1]==11)){if(date[2]!=29) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}if(date[1]==2){if((((date[0]%4==0)&&(date[0]%100!=0))||(date[0]%400==0))){if(date[2]!=28) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}else{if(date[2]!=27) date[2]++;else{date[2]=1;if(date[1]!=11) date[1]++;else{date[1]=1;date[0]++;}}}}}}}}}}EA=1;}//*******************************定时器0中断函数【完】void miaobiao() interrupt 3{TH1=(65536-10000)/256;TL1=(65536-10000)%256;if(stop[2]!=99) stop[2]++;else{stop[2]=0;if(stop[1]!=59) stop[1]++;else{stop[1]=0;if(stop[0]!=59) stop[0]++;else stop[0]=0;}}}//***********************************定时器1中断函数【完】。
自己制作的单片机万年历程序+原理图

自己制作的单片机万年历程序+原理图单片机万年历仿真原理图如下仿真Altium Designer画的万年历原理图和PCB图如下:PCB原理图基于51单片机,可以完成时钟显示、公历显示、农历显示、温度显示、闹钟报警定时的LCD时钟PPT内容预览:本设计使用AT89C51来做主控芯片,其强大的功能足够实现我们设计的所有功能。
使用LCD1602的液晶显示器来进行显示。
使用Keil uVision5进行编程。
通过Proteus8.6来进行仿真。
点击一次K1进入时钟设置页面,通过点击K2切换时、秒、分、星期、年、月、日,通过K3与K4实现加减来完成时钟的设置点击两次K1进入闹钟设置页面,通过点击K2切换开关、时、秒、分,通过K3与K4实现加减完成闹钟的设置。
单片机源程序如下注释是很全的#include //调用单片机头文件#define uchar unsigned char //无符号字符型宏定义变量范围0~255#define uint unsigned int //无符号整型宏定义变量范围0~65535#include "eeprom52.h"#include "nongli.h"#include "intrins.h"bit flag_200ms ;bit flag_100ms ;sbit beep = P3^7; //蜂鸣器定义bit flag_beep_en;uint clock_value; //用作闹钟用的sbit dq = P3^1; //18b20 IO口的定义uint temperature ; //温度变量uchar flag_nl; //农历阳历显示标志位uchar menu_1,menu_2;uchar key_time,flag_value; //用做连加的中间变量bit key_500ms ;uchar n_nian,n_yue,n_ri; //农历显示的函数#include "ds1302.h"#include "lcd1602.h"/******************把数据保存到单片机内部eeprom中******************/void write_eeprom(){SectorErase(0x2000);byte_write(0x2000, fen1);byte_write(0x2001, shi1);byte_write(0x2002, open1);byte_write(0x2058, a_a);}/******************把数据从单片机内部eeprom中读出来*****************/void read_eeprom(){fen1 = byte_read(0x2000);shi1 = byte_read(0x2001);open1 = byte_read(0x2002);a_a = byte_read(0x2058);}/**************开机自检eeprom初始化*****************/void init_eeprom(){read_eeprom(); //先读if(a_a != 1) //新的单片机初始单片机内问eeprom{fen1 = 3;shi1 = 8;a_a = 1;write_eeprom(); //保存数据}}/***********************18b20初始化函数*****************************/void init_18b20(){bit q;dq = 1; //把总线拿高delay_uint(1); //15usdq = 0; //给复位脉冲delay_uint(80); //750usdq = 1; //把总线拿高等待delay_uint(10); //110usq = dq; //读取18b20初始化信号delay_uint(20); //200usdq = 1; //把总线拿高释放总线}/*************写18b20内的数据***************/void write_18b20(uchar dat){uchar i;for(i=0;i<8;i++){ //写数据是低位开始dq = 0; //把总线拿低写时间隙开始dq = dat & 0x01; //向18b20总线写数据了delay_uint(5); // 60usdq = 1; //释放总线}}/*************读取18b20内的数据***************/uchar read_18b20(){uchar i,value;for(i=0;i<8;i++){dq = 0; //把总线拿低读时间隙开始value >>= 1; //读数据是低位开始dq = 1; //释放总线if(dq == 1) //开始读写数据value |= 0x80;delay_uint(5); //60us 读一个时间隙最少要保持60us 的时间}return value; //返回数据}/*************读取温度的值读出来的是小数***************/uint read_temp(){uint value;uchar low; //在读取温度的时候如果中断的太频繁了,就应该把中断给关了,否则会影响到18b20的时序init_18b20(); //初始化18b20write_18b20(0xcc); //跳过64位ROMwrite_18b20(0x44); //启动一次温度转换命令delay_uint(50); //500usinit_18b20(); //初始化18b20write_18b20(0xcc); //跳过64位ROMwrite_18b20(0xbe); //发出读取暂存器命令EA = 0;low = read_18b20(); //读温度低字节value = read_18b20(); //读温度高字节EA = 1;value <<= 8; //把温度的高位左移8位value |= low; //把读出的温度低位放到value的低八位中value *= 0.625; //转换到温度值小数return value; //返回读出的温度带小数}/******************1ms 延时函数*******************/void delay_1ms(uint q){uint i,j;for(i=0;i<>< p=""><>for(j=0;j<120;j++);}/******************写星期函数*******************/void write_week(uchar hang,uchar add,uchar week)//写星期函数{if(hang==1)write_com(0x80+add);elsewrite_com(0x80+0x40+add);。
单片机C语言电子钟显示年月日星期及时间(带注释)

#include<reg52.h>#define uchar unsigned char#define uint unsigned intuchar code table[]=" 2011-12-30 SA T";uchar code table1[]=" 23:59:55 ";uchar code week1[][3]={"MON","TUE","WEN","THU","FRI","SA T","SUN"}; /*uchar code week2[]="TUE";uchar code week3[]="WEN";uchar code week4[]="THU";uchar code week5[]="FRI";uchar code week6[]="SA T";uchar code week7[]="SUN"; */uchar count,shi,fen,miao,dnum,year,month,day,week,a=5;sbit rs=P2^0; //数据命令sbit rw=P2^1; //读写sbit en=P2^2; //使能sbit d1=P3^1; //独立键盘sbit d2=P3^2;sbit d3=P3^3;sbit d4=P3^4;void delay(uchar z) //延时{uint x,y;for(x=110;x>0;x--)for(y=z;z>0;z--);}void write_com(uchar com) //写指令{rs=0;rw=0;en=0;P0=com;delay(5);en=1;delay(5);en=0;}void write_date(uchar date) //写数据{rs=1;rw=0;en=0;P0=date;delay(5);en=1;delay(5);en=0;}void write_weekday (uchar a){uchar x;write_com(0x80+13);for(x=0;x<3;x++){write_date(week1[a][x]);}}void weekday(uint i){write_weekday(i%7);}void write_sfm(uchar add,uchar date){uchar shi,ge;shi=date/10;ge=date%10;write_com(0x80+0x40+add); //指向要写的地址write_date(0x30+shi); //0x30代表的数字是0,+shi表示shi的数字write_date(0x30+ge); //ge的数字}void write_ymd(uchar add,uchar date){uchar shi,ge;shi=date/10;ge=date%10;write_com(0x80+add); //指向要写的地址write_date(0x30+shi); //0x30代表的数字是0,+shi表示shi的数字write_date(0x30+ge); //ge的数字}void unit(){uchar num;en=0;write_com(0x38); //显示write_com(0x0c); //光标不显示write_com(0x06); //指针加1,整屏不移动write_com(0x01); //清屏write_com(0x80); //指针指向0x80for(num=0;num<16;num++) //第一行显示table数组{write_date(table[num]);delay(20);}write_com(0x80+0x40);for(num=0;num<16;num++) //第二行显示table1数组{write_date(table1[num]);delay(20);}TMOD=0X01;TH0=(65536-50000)/256; //开启中断TL0=(65536-50000)%256;EA=1;ET0=1;TR0=1;shi=23;fen=59;miao=55;year=11;month=12;day=30;}void jishu() //计数{if(count==20){count=0;miao++; //秒加if(miao==60){miao=0;fen++; //分加if(fen==60){fen=0;shi++;if(shi==24){shi=0;day++;a++;weekday(a);//时加}write_sfm(3,shi);write_ymd(9,day);if(day==31){day=0x01;write_ymd(9,day);month++;write_ymd(6,month);if(month==13){month=0x01;write_ymd(6,month);year++;write_ymd(3,year);}}}write_sfm(6,fen);}write_sfm(9,miao);}}void change(){d4=0;if(d1==0) //移光标{delay(10);if(d1==0){while(!d1);dnum++;if(dnum==1){TR0=0;write_com(0x0f);write_com(0x80+0x40+10);}if(dnum==2){write_com(0x80+0x40+7);}if(dnum==3){write_com(0x80+0x40+4);}if(dnum==4){write_com(0x80+10);}if(dnum==5){write_com(0x80+7);}if(dnum==6){write_com(0x80+4);}if(dnum==7){dnum=0;TR0=1;write_com(0x0c);}}}if(d2==0) //加数据{delay(10);if(d2==0){while(!d2);if(dnum==1){miao++;if(miao==60)miao=0;write_sfm(9,miao); //顺序不能换write_com(0x80+0x40+10); //顺序不能换}if(dnum==2){fen++;if(fen==60)fen=0;write_sfm(6,fen);write_com(0x80+0x40+7);}if(dnum==3){shi++;if(shi==24){shi=0;}write_sfm(3,shi);write_com(0x80+0x40+4);}if(dnum==4){day++;a++;weekday(a);if(day==31)day=1;write_ymd(9,day);write_com(0x80+10);}if(dnum==5){month++;if(month==13)month=1;write_ymd(6,month);write_com(0x80+7);}if(dnum==6){year++;write_ymd(3,year%100);write_com(0x80+4);}}}if(d3==0) //减数据{delay(10);if(d3==0){while(!d3);if(dnum==1){miao--;if(miao==-1)miao=59;write_sfm(9,miao);write_com(0x80+0x40+10);}if(dnum==2){fen--;if(fen==-1)fen=59;write_sfm(6,fen);write_com(0x80+0x40+7);}if(dnum==3){shi--;if(shi==-1)shi=23;write_sfm(3,shi);write_com(0x80+0x40+4);}if(dnum==4){day--;a--;weekday(a);if(day==0)day=30;write_ymd(9,day);write_com(0x80+10);}if(dnum==5){month--;if(month==0)month=12;write_ymd(6,month);write_com(0x80+7);}if(dnum==6){year--;write_ymd(3,year%100);write_com(0x80+4);}}}}void main(){unit();while(1){jishu();change();}}void timer0()interrupt 1{TH0=(65536-50000)/256;TL0=(65536-50000)%256;count++;}#include<reg52.h>#define uchar unsigned char //宏定义#define uint unsigned int //宏定义sbit rs=P3^5; //液晶数据/指令选择端:1-数据,0-指令sbit lcden=P3^4; //液晶使能控制端:1-有效,0-无效sbit shift_key=P3^2;//位置移动键sbit up_key=P3^3;//增加键uchar temp=0;//定义定时器溢出计数变量,每隔50ms产生1次溢出,temp加1uint year=2011;//定义年变量并赋初值2011年uchar month=06,day=01,week;//定义月、日、星期变量,并赋初值5月23日uchar hour=23,minute=59,second=58;//定义时、分、秒变量,并赋初值12时00分00秒uchar code week_string[7][4]={"MON","TUE","WED","THU","FRI","SA T","SUN"};//定义星期英文缩写表uchar data month_day[12]={31,0,31,30,31,30,31,31,30,31,30,31};//定义每月天数表/*--定时计数器T0及中断初始化函数--*/void init(void){TMOD=0x01;//设置定时器0为工作方式1TH0=(65536-50000)/256;//16位计数初值除以256得到高8位初值TL0=(65536-50000)%256;//16位计数初值除以256的余数得到低8位初值EA=1;//开总中断ET0=1;//开启定时器0中断EX0=1;//开启外部中断,外部中断用于调整时间PT0=1;//将定时器0中断设置高优先级,调整时间期不停止计时TR0=1;//启动定时器0}/*----------1ms延时函数----------*/void delay(uint n){uint i,j;for(i=n;i>0;i--)for(j=114;j>0;j--);}/*-------LCD1602写指令函数-----*/void LCD1602_write_com(uchar com){rs=0;//rs=0,置指令输入状态P0=com;//输出指令码delay(1);//延时1mslcden=1;//lcden=1,使能端有效delay(1);//延时1mslcden=0;//lcden=0,使能端无效}/*-------LCD1602写数据函数-----*/void LCD1602_write_dat(uchar dat){rs=1;//rs=1,置数据输入状态P0=dat; //输出待显示字符的字符码(ASCII码)delay(1);//延时1mslcden=1;//lcden=1,使能端有效delay(1);//延时1mslcden=0;//lcden=0,使能端无效}/*********************公历平年的2月只有28天,公历闰年的2月有29天。
智能单片机万年历时钟电路讲解

一、设计任务与要求:本设计准备实现的功能:(1) 显示公历日期功能(年、月、日、星期)。
(2) 可通过按键切换年、月、日、星期的显示状态。
(3) 可随时调校年、月、日及星期。
(4) 可每次增减一进行时间调节。
(5) 可动态完整显示年份,实现真正的万年历显示。
二、方案设计与论证:1.方案一:通过一段时间对专业书籍及多种设计方案的研究及分析,在计数电路芯片的选择上可以采用74LS160或74LS90,在实现的电路中有两种方案来实现清零(异步置数和同步清零);对于实现年、月、日、星期的显示,可采用LED液晶显示屏、点阵式数码管、LED数码管中的一种;在实现年、月、日、星期的校时方面,可设置K3 、K2、K1三个开关分别作为年、月、日的校时控制开关,由于“日”与“星期”同步,因而控制“日”的同时也控制了“星期”。
另外通过按钮开关可以在日期与时间间切换和对时钟进行调整。
该方案的系统原理框图如下:图中各单元电路的工作原理如下:(1)计数器电路:包括年计数器、月计数器、日计数器、星期计数器四部分。
各部分分别完成对“年”、“月”、“日”、“星期”的计数。
(2)译码显示电路:译码显示电路的功能是将年、月、日、星期计数器输出的4位二进制码进行翻译后显示出相应的十进制数字。
(3)校时电路:当数字钟计时出现误差时,必须对时间进行校正,通常称为“校时”,校时是数字钟应该具备的基本功能,一般要求能对年、月、日分别进行校正。
2.方案二:对于本题目的设计,我们不仅可以运用以前学过的课程——《数字电路逻辑设计》里边的知识来完成,也可以运用我们所学过的单片机知识来完成本设计,我们可以直接用叫简单的单片机芯片AT89C51再加上其周围的外设电路结构来完成。
该方案的系统原理框图如下:图中各单元电路的工作原理如下:(1)晶体电路:晶体振荡器电路给数字钟提供一个频率稳定准确的32768Hz的方波信号,可保证数字钟的走时准确及稳定。
不管是指针式的电子钟还是数字显示的电子钟都使用了晶体荡器电路。
单片机万年历时钟

#include<reg52.h> //用定时器实现电子时钟#include<delay.h>#define uint unsigned int#define uchar unsigned charsbit wela = P2^7;sbit dula = P2^6;sbit lcdrs = P1^0;sbit lcdrw = P1^1;sbit lcden = P2^5;sbit beep = P2^3;uchar lcd1[] = "2014-07-22 TUE";uchar lcd2[] = "00-00-00";uchar num;uchar hour = 23,min = 59,sec = 50;uchar i;//循环时用到i计数uchar month = 7,day = 22;uint year = 2014, week = 3; //时间初始化,可自己定义void write_com(uchar com) //写命令{lcdrs = 0;lcden = 0;P0 = com;delayms(3);lcden = 1;delayms(3);lcden = 0;}void write_data(uchar dat) //写数据{lcdrs = 1;lcden = 0;P0 = dat;delayms(5);lcden = 1;delayms(5);lcden = 0;}void init()//液晶初始化,包括第一次的显示{dula = 0;wela = 0;lcden = 0;lcdrw = 0;write_com(0x38);//设置16*2显示,5*7点阵,8位数据口//delayms(5); //确保程序安全写入,程序小的情况下write_com(0x0c); //设置开显示,不显示光标//delayms(5);write_com(0x06);//写一个字符后地址指针加一//delayms(5);write_com(0x01);//显示清零,数据指针清零//delayms(5);write_com(0x80); //从第一行第一个光标开始//delayms(5);for(i=0;i<14;i++){write_data(lcd1[i]);delayms(1);}write_com(0x80+0x43); //从第二行第三个开始显示for(i=0;i<8;i++){write_data(lcd2[i]) ;delayms(1);}TMOD = 0x01; //定时器中断0初始化TH0 = (65536-45872)/256;TL0 = (65536-45872)%256;EA = 1;TR0 = 1;ET0 = 1;}void display_sfm(uchar add,uchar date){uchar shi,ge;shi = date/10;ge = date%10;write_com(0x80+0x40+add); //找到位write_data(0x30+shi);//写数据,0x30是16进制,在液晶中代表0,加几代表写几write_data(0x30+ge ) ;// 写数据依次来的}void display_week(uchar a )//显示星期几{switch(a){case 1: write_com(0x80+11);//星期一write_data(0x40);write_data(0x4f);write_data(0x4e);break;case 2: write_com(0x80+11);write_data(0x54);write_data(0x55);write_data(0x45);break;case 3: write_com(0x80+11);write_data(0x57);write_data(0x45);write_data(0x44);break;case 4: write_com(0x80+11);write_data(0x54);write_data(0x48);write_data(0x55);break;case 5: write_com(0x80+11);write_data(0x46);write_data(0x52);write_data(0x49);break;case 6: write_com(0x80+11);write_data(0x54);write_data(0x48);write_data(0x55);break;case 7: write_com(0x80+11);write_data(0x53);write_data(0x55);write_data(0x4e);break;}}void display_yue_ri(uchar add,uchar date) {uchar shi,ge;shi = date/10;ge = date%10;write_com(0x80+add); //找到位write_data(0x30+shi);//写数据,0x30是16进制,在液晶中代表0,加几代表写几write_data(0x30+ge);// 写数据依次来的}void display_year(uint year)//注意数据之间的传输不要出现错误{uchar a,b,c,d;a = year/1000;b = year/100%10;c = year/10%10;d = year%10;write_com(0x80);//控制指针write_data(0x30+a);//控制显示什么数字write_data(0x30+b);write_data(0x30+c);write_data(0x30+d);}void main()//主程序内越精简越好{init();while(1){if(num==20){num = 0;sec++;if(sec==60){sec = 0;min++;if(min==60){min = 0;hour++;if(hour==24){hour = 0;day++;display_week(week);//显示星期几week++;if(week==8)week = 0;if(((year/4==0&&year/100!=0)||(year/400==0)&&(month==2))&&(day==29))//闰年2月判断{day = 0;month++;}if(((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))&&(day==31))//1.3.5.7.8.10.12月份{day = 0;month++;}if(((month==4)||(month==6)||(month==9)||(month==11))&&(day==30))//4.6.9.11月份{day = 0;month++;}if((!(year/4==0&&year/100!=0)||(year/400==0))&&(month==2)&&(day==28))//非闰年2月判断{day = 0;month++;}if(month==12){month = 1;year++;}}}}display_sfm(9,sec);//显示display_sfm(6,min);display_sfm(3,hour);display_year(year);display_yue_ri(5,month);display_yue_ri(8,day);}}}void T0_timer() interrupt 1{TH0 = (65536-45872)/256;TL0 = (65536-45872)%256;num++;}。
基于单片机设计的万年历显示电路
基于单片机设计的万年历显示电路电路原理图:程序:#include<reg51.h>#include"lcd1602.h"#include"ds1302.h"#include"ds18b20.h"#define uint unsigned int#define uchar unsigned charuint b[6];//年、月、日、时、分、秒uchar code row1[]={"2009-01-01"};uchar code row2[]={"00:00:00"};uchar year1[12]={31,28,31,30,31,30,31,31,30,31,30,31};//平年uchar year2[12]={31,29,31,30,31,30,31,31,30,31,30,31};//润年uchar j[6]={0x85,0x88,0x8b,0x84+0x40,0x87+0x40,0x8a+0x40};//LCD地址uchar i,k,jj=0,w,clock=0,bigclock=0,c=0,num;//i循环数,k温度缓存,jj地址位uint temp;sbit b1=P3^0;//设置sbit b2=P3^1;//上调sbit b3=P3^2;//下调sbit b4=P3^3;//转换sbit b5=P3^4;//闹钟sbit speaker=P3^5;lcdscan(){for(i=0;i<6;i++){ lcdwrite(j[i]);lcdshuju(a[11-i*2]+0x30);lcdwrite(j[i]+0x01);lcdshuju(a[10-i*2]+0x30);}}void key(){ if(b1==0){ dsaddshuju(0x80,(a[1]<<4)+a[0]+0x80);while(b1==0){for(i=0;i<6;i++){b[i]=a[11-i*2+clock]*10+a[10-i*2+clock];}if(b4==0){ delay(3);jj++;if(jj==6){jj=0;}}if(b2==0){ delay(3);b[jj]++;if(b[5]==60)b[5]=0;if(b[4]==60)b[4]=0;if(b[3]==24)b[3]=0;if((b[0]%4==0&&b[0]%100!=0)||b[0]%400==0){if(b[2]>year2[b[1]-1])b[2]=1;} else {if(b[2]>year1[b[1]-1])b[2]=1;}if(b[1]==13)b[1]=1;if(b[0]==100)b[0]=0;}if(b3==0){ delay(3);b[jj]--;if(b[5]==-1)b[5]=59;if(b[4]==-1)b[4]=59;if(b[3]==-1)b[3]=23;if((b[0]%4==0&&b[0]%100!=0) || b[0]%400==0){if(b[2]<=0)b[2]=year2[b[1]-1];}else {if(b[2]<=0)b[2]=year1[b[1]-1];}if(b[1]==0)b[1]=12;if(b[0]==-1)b[0]=99;}for(i=0;i<6;i++){ a[11-i*2+clock]=b[i]/10;a[10-i*2+clock]=b[i]%10;}lcdwrite(j[jj]);lcdshuju(0);lcdwrite(j[jj]+1);lcdshuju(0);delay(200);lcdwrite(j[jj]);lcdshuju(a[11-2*jj+clock]+0x30);lcdwrite(j[jj]+1);lcdshuju(a[10-2*jj+clock]+0x30);delay(200);if(b1==1){ dsaddshuju(0x80,(a[1]<<4)+a[0]);dson();}if(b5==0)clock=12;} }}void main(){ speaker=0;lcdrw=0;dson();lcdon();lcdwrite(0x83);for(i=0;i<10;i++){ lcdshuju(row1[i]);delay(1);}lcdwrite(0x84+0x40);for(i=0;i<8;i++){lcdshuju(row2[i]);delay(1);}dsrst=0;dssclk=0;while(1){for(i=0;i<5;i++){ dsrst=1;dswrite(0x81+i*2);k=dsread();dsrst=0;a[i*2]=k&0x0f;a[i*2+1]=(k>>4)&0x0f;}dsrst=1;dswrite(0x8d);k=dsread();dsrst=0;a[10]=k&0x0f;a[11]=(k>>4)&0x0f;delay(100);lcdscan();key();if(b5==0){ for(i=0;i<12;i++){if(a[i+12]==a[i])num++; }if(num==12)bigclock=1;else num=0;lcdwrite(0x8e);lcdshuju('&');}else{lcdwrite(0x8e);lcdshuju(0x00);}if(bigclock==1){ speaker=1;c++;if(c==100){bigclock=0;speaker=0;}}temp=readtemperature();//temp是uint型的。
基于单片机的电子日历时钟
保护 psw、A 寄存器
带显示延时
N
Key0 按下?
Y
秒表模式?
N
设置闹钟?
N
闪烁切换模式
Y
暂停/继续计时
Y 打开/关闭闹钟
滴一声提示
闪烁循环切换
恢复 psw、A 寄存器
退出中断
八、性能测试与故障排查
1、硬件部分
问题:断路。 现象:程序下载完之后可以工作但是 6 个 LED 显示器有 2 个没亮。 分析:因为其他几个显示器完全正常,这两个则完全没亮,所以推断是位选
修改二:程序默认设置的外部中断延时过短,导致操作过于“灵敏”,也即 按键 K0、K1 按下时,跳变过快。这里所说的延时是中断子程序 EX0_INT 和 EX1_INT 中的语句 LCALL DIS_DELAY。解决这个情况只需修改 DELAY 次数,则 DIS_DELAY: MOV D_CNT_DIS,#10H。
; 学生姓名:陈晓伟、冯劲增 ; 学生学号:200830240144,200830240229 ; 分组编号:004
;变量定义
TICK_CNT EQU 20H
;------------------
SECOND_BCD EQU 21H
MINUTE_BCD EQU 22H
HOUR_BCD EQU 23H
ALAM_OFF1 EQU 39H
ALAM_OFF2 EQU 3AH
ALAM_OFF3 EQU 3BH
;------------------
;中断向量表
ORG 0000H
LJMP MAIN
ORG 0003H
LJMP EX0_INT
; port INT0
ORG 000BH
基于单片机的电子时钟万年历设计
(2)31 8 位暂存数据存储RAM(3)串行 I/O 口方式使得管脚数量最少(4)宽范围工作电压2.0 5.5V(5)工作电流 2.0V 时,小于300nA(6)读/写时钟或RAM 数据时有两种传送方式单字节传送和多字节传送字符组方式(7)8 脚DIP 封装或可选的8 脚SOIC 封装根据表面装配(8)简单 3 线接口(9)与 TTL 兼容Vcc=5V(10)可选工业级温度范围-40~+85优点:串行接口的日历时钟芯片,使用简单,接口容易,与微型计算机连线较少等特点,在单片机系统尤其是手持式信息设备中己得到了广泛的应用。
所以,最终选择串行时钟芯片DS1302,DS1302的管脚图如图2所示。
图2 DS1302管脚图1.2显示模块选择方案一:LED数码管显示数码管显示比较常用的是采用CD4511和74LS138实现数码转换,数码显示分动态显示和静态显示,静态显示具有锁存功能,可以使数据显示得很清楚,但浪费了一些资源。
目前单片机数码管普通采用动态显示。
编程简单,但只能显示数字,不能显示中文。
方案二:LCD1602能够显示英文和数字。
1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。
所以最终选择LCD1602。
2.项目功能模块2.1 89C51模块Mcs-51单片机管脚图图如图3所示:单片机管脚图2.2 1602液晶显示模块1602液晶模块内部的字符发生存储器(CGROM)已经存储了160个不同的点阵字符图形,这些字符有:阿拉伯数字、英文字母的大小写、常用的符号、和日文假名等,每一个字符都有一个固定的代码,比如大写的英文字母“A”的代码是01000001B(41H),显示时模块把地址41H中的点阵字符图形显示出来,我们就能看到字母“A”。
单片机万年历程序(带闹钟)
第一章设计方案1.1.硬件设计1.1.1设计要求实现年月日、时分秒、星期的显示功能,用两个按键来实现日期和时间的调整功能,调整要求星期能自动更新,且能实现位调。
显示格式:四位年,两位月,两位日,星期字母前三位,两位时,两位分,两位秒。
比如:YYYY-MM-DD WEEKHH:MM:SS1.1.2硬件框图图1-1-硬件框图日期时间由硬件软件结合产生;按键之间相互配合完成当前日期时间的调整功能。
过程与结果由控制芯片分别输出到显示器上。
本设计附加了播放音乐的功能。
音乐从蜂鸣器产生。
1.1.3硬件选择1).显示模块本设计具体选用与实际应用与消耗资源相关。
万年历用到的地方大多都在室内。
现市场上与许多显示器,在日常生活中最常见的有数码管,led点阵屏,lcd液晶显示器等。
八段数码管的优点:亮度高,显示大。
驱动部份的软件简单;缺点:与液晶相比,耗电及体积大。
数码管只能单纯的显示数字,不能生动的表达各个参数的信息,消耗电力也比led液晶显示器大。
LCD液晶显示器它是一种专门用来显示字母、数字、符号等的点阵型液晶模块,在单片机系统中应用晶液显示器作为输出器件有以下几个优点:显示质量高,由于液晶显示器每一个点在收到信号后就一直保持那种色彩和亮度,恒定发光,而不像阴极射线管显示器(CRT)那样需要不断刷新新亮点。
因此,液晶显示器画质高且不会闪烁。
数字式接口,液晶显示器都是数字式的,和单片机系统的接口更加简单可靠,操作更加方便。
体积小、重量轻,液晶显示器通过显示屏上的电极控制液晶分子状态来达到显示的目的,在重量上比相同显示面积的传统显示器要轻得多。
功耗低,相对而言,液晶显示器的功耗主要消耗在其内部的电极和驱动IC上,因而耗电量比其它显示器要少得多。
Lcd液晶能一屏把本设计所要显示的信息表达完整。
而且lcd耗电小,体积小很适合与室内使用。
本设计的要求日期时间以数字表示、星期用3位英文字符表示共21个字符,lcd1602能显示32个字表 1-1 lcd1602引脚及功能2).按键与蜂鸣器本设计选用轻触按钮,型号为XDJT1102S。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
8098单片机实现日历时钟的显示方法徐创文(兰州工业高等专科学校 兰州 730050) M C146818是M OTOROLA公司生产的CMOS型实时时钟集成电路,内含M OT OROLA 和INT EL总线时序选择,通过总线可以很方便地和各种微处理器直接接口。
它具有日历、时钟、计时,可编程定时中断、方波发生器,并能提供50字节低功耗静态RAM。
MC146818用在自动化柴油发电机组微机控制系统中主要用来记载市电投入时间、供电时间、机组启动、机组运行时间以及所有系统中出现故障时的时间。
1 MC146818功能简介图2表2 排样方案的文字描述序号板长板宽张数 带数带宽坯号坯数 带数带宽坯号坯数1234200020007502000100010001500100037620246394 2 3 4 23012601853013431353426351026 2 1 2 2188188188185323232351515518余坯量(坯号*余坯数):31*100,32*40,34*40,35*24所需板材总面积(平方米):1857 下料利用率:92.63% 表2中下料利用率指条料总面积和所需板材总面积的比值。
方式1的含义是按这种方式切割长2000mm、宽1000m m的板材376张,每张板材上含宽为301m m和宽为188m m的条料各2根。
在每根宽301m m的条料上含34号毛坯26个,在每根宽188m m的条料上含32号毛坯15个。
其它排样方式的含义可类似理解。
按上述方案下料后所有毛坯的需求均得到满足。
例如方式3和4中含35号毛坯各40和36块,其它方式中不含该种毛坯,按上述方案下料后切出该种毛坯的数量可计算如下:246×40+394×36=24024(块)该种毛坯共需要24000块,满足需求外尚余24块。
4 结论国内尚有相当一部分企业采用单一下料的方式下料,即在1张板材上只排列1种条料,用于制作同一种毛坯。
并且所有条料在板材上的取向一致。
当对条料方向无约束时,按单张板材下料利用率最高的原则选取条料方向。
假定使用长2000mm、宽1000m m的板材,不难算出按上述单一下料方式切出4种毛坯分别需板材20张、187张、513张和267张,板材总面积为1974平方米。
虽然产生的部分余料尚可使用,但余料利用率往往较低。
计算机排样一次就可达到较高的下料利用率,节材潜力较大。
作者同多家企业有关车间领导讨论过不规则形冲裁件排样问题,这些领导往往认为他们现在的边料宽度已经较小,优化排样节材潜力不大。
岂不知板材宽度只有100cm左右,边料宽8~12cm就占8%~12%。
而通过计算机排样可以将边料宽度降到最小(本文中例题沿板材长边方向边料的加权平均宽度为2.3cm),节材效果还是相当可观的。
参考文献1 Hinsm an A I.The trim loss and assotmentproblems:A surv ey.Eur opean Journal o f Op-eratio nal Research,1980,5:8~182 Dy ckhoff H,et al.Essay s o n Productio n T heo-ry and Planning,Springer,Berlein,1988.3 Dagli C H,et al.An appro ach to tw o dimen-sional cutting stock problem s.Inter nationalJournal of Productio n Resear ch,1987,25:175~190.4 Hahn S G.On the optimal cutting of defectivesheets.Operations Resear ch,1968,16:1100~1114.5 崔耀东等.背包问题的两阶段动态规划算法.高校应用数学学报,1994,8(4):430~435・15・1997No.1《机械研究与应用》计算机应用1.1 内部带时基和振荡电路,振荡频率可选择4.194303M HZ、1.048576M HZ或32.768M HZ。
1.2 低功耗,电源电压范围为3V~6V。
在32.768 M HZ频率时,电流小于50L A,在4.194304M HZ 频率时,电流小于3m A。
1.3 可计数每天的秒、分、时和日期、星期几、月份、年份。
1.4 具有夏时制转换、月份自动递进和闰年自动补偿功能。
1.5 具有三种可由软件屏蔽、测试的中断。
1.6 日历、时钟、闹钟数据格式可选择二进制或BCD码表示。
1.7 通过软件可访问64字节RAM存贮器,其中14字节为时钟和控制寄存器,50字节为用户自定义通用RAM。
1.8 能输出频率为1或1/4时基频率的时钟。
1.9 可选择24小时或带AM/PM的12小时计时功能。
2 MC146818与8098接口设计接口设计应实现:¹8098能对MC146818进行数据读写。
º主电源下降时,主电源和备用电源能自动切换,并能对内部RAM数据进行写保护。
»读取的时间值能显示在液晶显示屏上(见图1)。
图1图中M C146818引脚OCS1、OCS2时间基准输入线,CKOUT时钟输出端,CKFS时钟输出频率选择,SQW方波输出线,AD0-AD7双向地址/数据总线,AS地址选通信号输入线,R/W读写控制信号输入线,CE片选信号输入线,IRQ中断请求输出线,REST复位信号输入线,Ps电源检测信号输入线。
M GL(s)10032B引脚V0液晶显示对比度调节电压,A0数据/指令通道选择信号输入线,H:选择数据通道,L:选择指令通道,R/W读写信号, E1、E2片选信号,D0-7数据总线。
M AX691A微处理监控器完成电源检测,主电源和备用电池自动切换,RAM写保护,系统复位等功能。
3 MC146818使用方法3.1 地址映象(图2)由地址映象图可知,10个字节用于寄存时间,日历和闹钟的数据,4个字节用作寄存器A、B、C、D,用来寄存状态信息和控制信息,其中除了寄存器C、D、A中的最高位和秒字节的最高位仅能读不能写以外,其余均可直接由程序直接读写。
图2CPU可通过初始化写来实现时间、日历、闹钟的初值,也可通过读将当前的时间、日历、闹钟值显示在液晶显示屏上。
字节的代码可以是二进制,也可以是BCD码。
时间、日历和闹钟单元初值的设置应通过初始化内部寄存器B,将B中的SET位置“1”,从而禁止时间/日历更新发生,使程序按指定的格式对这10个字节进行初始化,然后可清零STE位以重新计时更新。
50个通用RAM字节由程序任意读写。
3.2 中断M C146818提供三个独立的中断源:闹时中断、定时中断、更新结束中断,具体选择由CPU对寄存器B中的PIE、AIE、UIE设置来决定,中断的标志由寄存器C中的IRQF提供,其状态由下式决定:IRQF=PF・PIE+AF・AIE+UF・UIE其中PF、AF、UF是设置在寄存器C中的三个中断源的标志位。
3.3 分频器3.3.1 分频器控制分频器控制由寄存器A中的DV2、DV1、DV0设置,利用它们可选择三种时基:4.194303M HZ、1.048576M HZ、32.768KHZ。
3.3.2时钟输出选择通过时钟频率选择脚的两种状态和时基频率・16・计算机应用《机械研究与应用》1997No.1的组合,能输出8种时钟频率以供其它集成电路之用。
3.3.3 方波输出选择由寄存器A 中的RS 3-RS 0四位决定,其输出的方波可用作外部频率标准等。
3.4 更新周期更新周期的基本功能是时钟日历计数,使秒字节内容加1,若有溢出则分字节内容加1,若分字节有溢出,则时字节内容加1,……,直到年字节。
更新周期时间为248us (对 4.19MHZ 或1.048576M HZ),或1984L S(对32.768KHZ)。
在更新周期,寄存器A 中的U IP 置位“0”,此时CPU 若进行访问时间、日历和闹钟字节,读这些字节得到的数值是不准确的。
为了能正确地访问时间和日历信息,有三种方法可供选择:¹利用更新结束中断。
因每次更新周期结束产生中断,利用中断服务程序,CPU 读取时间和日历的值,在退出中断服务程序之前,CPU 应对寄存器C 中的IRQF 、PF 位清零。
º查询法:查询寄存器A 中的U IP 位判断M C146818是否进入更新周期。
U IP 位每秒置一次,若读到U IP 位为高电平,记时至少在244us 时间内不会发生更新,因此CPU 可在这段时间内读取要读的信息。
»定时中断。
利用定时中断来判定M C146818是否进入更新周期。
4 软件设计8098单片机与M C146818实时时钟及液晶显示器的接口软件包括实时时钟初始化,液晶显示器初始化,日历时钟的读取和显示三部分。
4.1 M C146818实时时钟初始化计时写寄存器A允许更新初始化星期几、年、月、日初始化时、分、秒写寄存器B,设置为24小时制,数据格式为二进制,不许更新,禁止夏时制转换写寄存器A 选择时间基准频率开始4.2 液晶显示器初始化CPU 每次对液晶显示器进行访问,都要先读状态寄存器,判断是否处于“准备好状态”,只有处于“准备好状态”才能进行读写操作。
图4输出顺序选择结束调清RAM 子程序显示起始行、列、页设置开显示占空比设置软件复位开始4.3 日历时钟数据的读取和显示图5读MC146818寄存器A结束显示年、月、日,星期几、时、分、秒调用显示字符代码到液晶显示器显示RAM 区转换为液晶显示器显示字符代码依次读取年、月、日星期几、时、分、秒UIP=“1”?开始・17・1997No.1《机械研究与应用》计算机应用。