摘要在寒假期间我用一周时间完成了这个液晶万年历,它可以显示年月日、时分秒、以及温度(可上下限报警),可以对时间进行加一或减一调整,并加入了闰、平年时间调整,方便可行,已经调试成功。
考虑到成本和方便,本作品采用了STC89c52和DS18B20,计时用的是51单片机自带的十六位定时器/计数器T0,尽管对时间进行了误差调整但是还是有一定的误差,考虑到学校后改用DS1302时钟芯片,进一步减小误差。
温度显示精度达到0.1摄氏度。
关键词:单片机 DS18B20 万年历温度1.硬件工作介绍(1)上电自动复位及手动复位电路STC89c52单片机的RST端外部复位有两种操作方式:上电自动复位和按键手动复位。
本设计用上电自动复位以及手动复位下结合的方式外接电路(见附图)。
(2)时钟振荡电路作品中采用12M晶振,其连接方法如图所示,其中电容的值都为22pF。
(3)1602液晶显示接口1602采用标准的16脚接口(见附图),其中:第1脚:GND为地电源第2脚:VCC接5V正电源第3脚:V0为液晶显示器对比度调整端,接正电源时对比度最弱,接地电源时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。
第5脚:RW为读写信号线,高电平时进行读操作,低电平时进行写操作。
当RS 和RW共同为低电平时可以写入指令或者显示地址,当RS为低电平RW为高电平时可以读忙信号,当RS为高电平RW为低电平时可以写入数据。
第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。
第7~14脚:D0~D7为8位双向数据线。
第15脚:VCC第16:GND(4)键盘键盘是通过S3(P3.2),S4(p3.3),S5(p3.4)对时钟进行调整,其S3选择要调整的对象如时,分,日,月等等,并在液晶屏上显示所选的对象。
S4对所选中的对象进行加一调整,而S5对所选对象进行减一调整。
(5)蜂鸣器对温度进行测量,高低温超限报警,正常温度在15.0-36.0摄氏度,超过最高或低于最低温度就会鸣叫报警。
2.软件工作介绍由于C语言功能丰富,表达能力和可移植能力强,而且操作方便,简单易懂,因此软件部分由C语言编写,程序的重点和难点是闰月的时间调整以及键盘对时间的调整,恳请老师指正。
主程序流程图设计总结在制作的过程中,因为板子是在学校自己动手做的最小系统板,接插线还是比较简单的,难的是硬件的调试,因为理论与实践总是存在着差距,想出来的东西没有经过实践的检验是不可靠的,任何一个小小的疏忽大意都能导致失败。
比如说对hour, min , sec,day, moth的定义,一开始我用的unsigned char ,但是在调试减一时,减到零时再按下键盘就会出现乱码,几经调试,终于发现问题地所在并及时地解决了问题,之后再一次对硬件进行调试,终于成功了。
从这一方面来说,我认识到做要比说难得多,做的过程中必定有很多的阻碍,但是一定要慢慢来,一点一点地找出问题,再一点一点地解决它,这样的话,到达成功的彼岸也就仅仅是时间的问题了。
我通过这次设计,特别是通过对程序的编写,深层了解了单片机内部结构和外围器件的应用。
我学到的不仅仅是电子方面的知识,更重要的是,我学到了作为一名电子设计者所必需的心理素质,那就是——决心加上耐心!附录1:程序代码:#include <reg51.h>#define Lcd_Data P0 //定义数据端口#define _TH0_TL0_ (65536 - 50000)#define HI (_TH0_TL0_ / 256) //给高8位赋值#define LO (_TH0_TL0_ % 256) //给低8位赋值#define M 20 //(1000/50)1秒要20个中断的累计char hour = 0, min = 0, sec = 0,N=0,day=1,moth=1,week=7;//初始化要显示的值unsigned char SEG_TAB[] = {0x30,0x31,0x32, 0x33, 0x34, 0x35, 0x36, 0x37,0x38, 0x39};//0-9数字unsigned int year=8;sbit RS = P2^ 5; //定义和LCD的连接端口sbit RW = P2^ 4;sbit E = P2^ 3;sbit Busy = P0^ 7;sbit S3 = P3^2;//定义开关s3选择键sbit S4 = P3^3;//s4加一键sbit S5 = P3^4;//s5减一键sbit DQ = P2^0;//DS18B20sbit speak = P2^1; //蜂鸣器警报/******************************************************************** *******/void delay1(unsigned int num)//短延时函数{while( num-- );}/******************************************************************** **********/void Delay(unsigned char t)//长延时函数,延时1MS*t S{unsigned char a;while(--t != 0){for(a = 0; a < 125; a++);}}/*******************************************************************/ void Timer0Init(void) //初始化定时器0程序{TMOD = 0x01; //设置模式1 定时器0 工作方式0TH0 = HI; //给TH0,TL0赋初始值TL0 = LO;TR0 = 1; //启动定时器0ET0 = 1; //定时器中断开EA = 1; //总中断开}/******************************************************************** **********/void Read_Busy(void)//读忙信号判断{do{Lcd_Data = 0xff;RS = 0;RW = 1;E = 0;Delay(2);E = 1;}while(Busy);}/******************************************************************** **********/void Write_Comm(unsigned char lcdcomm) //写指令函数{Lcd_Data = lcdcomm;RS = 0;RW = 0;E = 0;Read_Busy();E = 1;}/******************************************************************** **********/void Write_Data(unsigned char lcddata)//写数据函数{Lcd_Data = lcddata;RS = 1;RW = 0;E = 0;Read_Busy();E = 1;}/******************************************************************** **********/void Init_LCD(void)//初始化LCD{Delay(400); //稍微延时,等待LCD进入工作状态Write_Comm(0x01);//清显示Write_Comm(0x02);//光标归位Write_Comm(0x38);//8位2行5*8Write_Comm(0x06);//文字不动,光标右移Write_Comm(0x0c);//显示开/关,光标开闪烁开Write_Comm(0x18);//左移}/******************************************************************** ***********/Init_DS18B20(void)//初始化ds1820{unsigned char x = 0;DQ = 1; //DQ复位delay1(8); //稍做延时DQ = 0; //单片机将DQ拉低delay1(80); //精确延时大于480usDQ = 1; //拉高总线delay1(14);x = DQ; //稍做延时后如果x=0则初始化成功x=1则初始化失败delay1(20);}/**********************************************************************************/ReadOneChar(void)//读一个字节{unsigned char i = 0;unsigned char dat = 0;for (i = 8; i > 0; i--){DQ = 0; // 给脉冲信号dat >>= 1;DQ = 1; // 给脉冲信号if(DQ)dat |= 0x80;delay1(4);}return (dat);}/******************************************************************** **************/WriteOneChar(unsigned char dat)//写一个字节{unsigned char i = 0;for (i = 8; i > 0; i--){DQ = 0;DQ = dat&0x01;delay1(5);DQ = 1;dat>>=1;}}/******************************************************************** **************/Read_Disp_Temperature(void)//读取并显示温度{unsigned int a = 0, b = 0, c = 0, t = 0;float tt = 0;Init_DS18B20();WriteOneChar(0xCC); // 跳过读序号列号的操作WriteOneChar(0x44); // 启动温度转换Init_DS18B20();WriteOneChar(0xCC); //跳过读序号列号的操作WriteOneChar(0xBE); //读取温度寄存器a = ReadOneChar();b = ReadOneChar();t = b;t <<= 8;t = t | a;tt = t * 0.0625;t = tt * 10 + 0.5; //放大10倍输出并四舍五入a = t / 100; //十位b = t / 10 - a * 10; //个位c = t - a * 100 - b * 10; //小数位if((t>=360)||(t<=150)) //温度报警范围{ speak=0;}else speak=1;Write_Comm(0x8d); //lcd显示温度Write_Data( SEG_TAB[a]);Write_Data( SEG_TAB[b]);Write_Data( '.');Write_Data( SEG_TAB[c]);}/******************************************************************** **********/void main(void)//主函数--数字读取和计算{Init_LCD(); //初始化LCDTimer0Init();//初始化定时器0Write_Comm(0x83); //写入":"字符Write_Data(':');Write_Comm(0x86);Write_Data(':');Write_Comm(0x89);//温度提示字符Write_Data('T');Write_Data('e');Write_Data('m');Write_Data('p');Write_Comm(0xc1);//第二行首地址//写入"20 - -"字符Write_Data('2');Write_Data('0');Write_Comm(0xc5);Write_Data('-');Write_Comm(0xc8);Write_Data('-');Write_Comm(0xcb);Write_Data('-');while(1){Read_Disp_Temperature(); //温度if(S3==0){delay1(10);if(S3==0)N++;if(N>6) {N=0;}while(S3==0);Write_Comm(0xcf);Write_Data(SEG_TAB[N]); //显示切换键位所示的位置N//0:秒,1:分,2:时,3:日,4:月,5:年6:星期}if(S4==0)//时间调整--加{delay1(10);if(S4==0){if(N==1){min++;if(min==60) min=0;Write_Comm(0x84);Write_Data(SEG_TAB[min/10]);Write_Comm(0x85);Write_Data(SEG_TAB[min%10]);}if(N==2){hour ++;if(hour==24) hour=0;Write_Comm(0x81);Write_Data(SEG_TAB[hour/10]);Write_Comm(0x82);Write_Data(SEG_TAB[hour%10]);}if(N==3){day++;if(moth==2&&(year%4==0)&&day==30)day=1; //闰年2月天数计算if(moth==2&&(year%4!=0)&&day==29)day=1; //平年2月if((moth==4||moth==6||moth==9||moth==11)&&day==31)day=1;//4,6,9,11月计算if(day==32)day=1;//1,3,5,7,8,10,12月天数计算Write_Comm(0xC9);Write_Data(SEG_TAB[day/10]);Write_Comm(0xCa);Write_Data(SEG_TAB[day%10]);}if(N==4){moth++;if(moth==13)moth=1;Write_Comm(0xc6);Write_Data(SEG_TAB[moth/10]);Write_Comm(0xc7);Write_Data(SEG_TAB[moth%10]);}if(N==5){year++;if(year==51)year=0;Write_Comm(0xc3);Write_Data(SEG_TAB[year/10]);Write_Comm(0xc4);Write_Data(SEG_TAB[year%10]);}if(N==6){week++;if(week==8) week=1;Write_Comm(0xcc);Write_Data(SEG_TAB[year/10]);}while(S4==0);}}if(S5==0)//时间调整--减{delay1(10);if(S5==0){if(N==1){min--;if(min<0) {min=59;}Write_Comm(0x84);Write_Data(SEG_TAB[min/10]);Write_Comm(0x85);Write_Data(SEG_TAB[min%10]);}if(N==2){ hour--;if(hour<0){hour=23;}Write_Comm(0x81);Write_Data(SEG_TAB[hour/10]);Write_Comm(0x82);Write_Data(SEG_TAB[hour%10]);}if(N==3){ day--;if(moth==2&&(year%4==0)&&day<1)day=29;if(moth==2&&(year%4!=0)&&day<1)day=28; if((moth==4||moth==6||moth==9||moth==11)&&day<1)day=30;if(day<1)day=31;Write_Comm(0xC9);Write_Data(SEG_TAB[day/10]);Write_Comm(0xCa);Write_Data(SEG_TAB[day%10]);}if(N==4){ moth--;if(moth==0){moth=12;}Write_Comm(0xc6);Write_Data(SEG_TAB[moth/10]);Write_Comm(0xc7);Write_Data(SEG_TAB[moth%10]);}if(N==5){ year--;if(year<0){year=50;}Write_Comm(0xc3);Write_Data(SEG_TAB[year/10]);Write_Comm(0xc4);Write_Data(SEG_TAB[year%10]);}if(N==6){week--;if(week==0) week=7;Write_Comm(0xcc);Write_Data(SEG_TAB[week]);}while(S5==0);}}}}/******************************************************************** ******/void IsrTimer0(void) interrupt 1 using 1 //定时器0中断服务程序{static unsigned char count = 0; //定义静态变量countTR0 = 0; //时间误差调整TL0 += (_TH0_TL0_ + 9) % 256;TH0 += (_TH0_TL0_ + 9) / 256 + (char)CY;TR0 = 1;count++;if(count == M)//1秒种时间到{count = 0;sec++;if(sec == 60)//1分钟时间到{min++;sec = 0;if(min == 60)//1小时时间到{hour++;min = 0;if(hour == 24)//24小时时间到{ day++;week++;hour = 0;min = 0;sec = 0;if(week==8){ week=1;}if((moth==2&&(year%4==0)&&day==30)||(moth==2&&(year%4!=0)&&day==29)||( (moth==4||moth==6||moth==9||moth==11)&&day==31)||(day==32)){ //一个月到moth++;day=1;if(moth==13) //一年到{ year++;moth=1;}}}}}}Write_Comm(0x81); //时间显示Write_Data(SEG_TAB[hour/10]);Write_Comm(0x82);Write_Data(SEG_TAB[hour%10]);Write_Comm(0x83);Write_Data(':');Write_Comm(0x84);Write_Data(SEG_TAB[min/10]);Write_Comm(0x85);Write_Data(SEG_TAB[min%10]);Write_Comm(0x86);Write_Data(':');Write_Comm(0x87);Write_Data(SEG_TAB[sec/10]);Write_Comm(0x88);Write_Data(SEG_TAB[sec%10]);Write_Comm(0x8f);Write_Data( '.');Write_Comm(0xc3);Write_Data(SEG_TAB[year/10]);Write_Comm(0xc4);Write_Data(SEG_TAB[year%10]);Write_Comm(0xc5);Write_Data('-');Write_Comm(0xc6);Write_Data(SEG_TAB[moth/10]);Write_Comm(0xc7);Write_Data(SEG_TAB[moth%10]);Write_Comm(0xc8);Write_Data('-');Write_Comm(0xc9);Write_Data(SEG_TAB[day/10]);Write_Comm(0xca);Write_Data(SEG_TAB[day%10]);Write_Comm(0xcb);Write_Data('-');Write_Comm(0xcc);Write_Data(SEG_TAB[week]);Write_Comm(0xcd);Write_Data(0xfe);Write_Comm(0xce);Write_Data(0xfe);Write_Comm(0xcf+1);Write_Data(0xfe);}附录2:电路图。