制作出来的实物图如下:
仿真原理图如下(proteus仿真工程文件可到本帖附件中下载)
单片机源程序如下:
- /**
- P2.7置低电平时,开机接入串口,自动进入时间设置界面
- 每秒钟自动检测温度一次,并在LCD上显示
- 每分钟自动刷新日期、时间、温度,在LCD上显示,并在串口中输出(方便电脑读取)
- */
- #include <stdio.h>
- //#include <AT89X51.H>
- #include<reg51.h>
- #include"ds1302.h"
- #include"temp.h"
- #include <nokia_5110.h>
- //***********************************
- #define delay_time 25767
- sbit SETTIME = P2^7; //为0时,开机进入设置时间界面
- uchar TIME[7];
- char word2[10];
- //毫秒级延时函数
- void delay(unsigned int x)
- {
- unsigned char i;
- while(x--)
- {
- for(i = 0;i < 120;i++);
- }
- }
- char* tempDisplay(int temp) //lcd显示
- {
-
- unsigned char datas[5]; //定义数组
- float tp;
- unsigned char ReturnTemp[9],x;
- if(temp< 0) //当温度值为负数
- {
- ReturnTemp[0]='-';
- //因为读取的温度是实际温度的补码,所以减1,再取反求出原码
- temp=temp-1;
- temp=~temp;
- tp=temp;
- temp=tp*0.0625*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算由?.5,还是在小数点后面。
-
- }
- else
- {
- // LcdWriteCom(0x80); //写地址 80表示初始地址
- // LcdWriteData('+'); //显示正
- ReturnTemp[0]='+';
- tp=temp;//因为数据处理有小数点所以将温度赋给一个浮点型变量
- //如果温度是正的那么,那么正数的原码就是补码它本身
- temp=tp*0.0625*100+0.5;
- //留两个小数点就*100,+0.5是四舍五入,因为C语言浮点数转换为整型的时候把小数点
- //后面的数自动去掉,不管是否大于0.5,而+0.5之后大于0.5的就是进1了,小于0.5的就
- //算加上0.5,还是在小数点后面。
- }
- datas[0] = temp / 10000;
- datas[1] = temp % 10000 / 1000;
- datas[2] = temp % 1000 / 100;
- datas[3] = temp % 100 / 10;
- datas[4] = temp % 10;
-
- // LcdWriteCom(0x82); //写地址 80表示初始地址
- // LcdWriteData('0'+datas[0]); //百位
- // ReturnTemp[1]=datas[0]+48;
- if(datas[0]!=0)
- {
- ReturnTemp[1]=48+datas[0];
- x=1;
- } else {
- x=0;
- }
- //putchar(ReturnTemp[1]);
- // LcdWriteCom(0x83); //写地址 80表示初始地址
- // LcdWriteData('0'+datas[1]); //十位
- if(( datas[1])!=0)
- {
- ReturnTemp[1+x]=datas[1]+48;
- x=x+1;
- } else {
- if(x!=0)
- {
- ReturnTemp[1+x]='0';
- x=x+1;
- }
- }
- // LcdWriteCom(0x84); //写地址 80表示初始地址
- // LcdWriteData('0'+datas[2]); //个位
- ReturnTemp[1+x]='0'+datas[2];
- // putchar(ReturnTemp[1+x]);
- // LcdWriteCom(0x85); //写地址 80表示初始地址
- // LcdWriteData('.'); //显示 ‘.’
- ReturnTemp[2+x]='.';
- // LcdWriteCom(0x86); //写地址 80表示初始地址
- // LcdWriteData('0'+datas[3]); //显示小数点
- ReturnTemp[3+x]=48+datas[3];
- // putchar(ReturnTemp[3+x]);
- // LcdWriteCom(0x87); //写地址 80表示初始地址
- // LcdWriteData('0'+datas[4]); //显示小数点
- ReturnTemp[4+x]=datas[4]+48;
- ReturnTemp[5+x]='C';
- ReturnTemp[6+x]='\0';
- // printf("\n xx %s\n",ReturnTemp);
- return ReturnTemp;
- }
- //设置日期,格式为: setdate 2015-2-21, 可以省略0
- int setdate()
- {
- char year[5],month[3],day[3],i,
- y,m,d; //y存放年,m存放月, d存放日
- for(i=0;i<4;i++)
- year[i]=word2[i];
-
- for(i=0; i<10; i++) //检查WORD2中字符的合法性,只能有数字或者-
- {
- if((word2[i]-48)<0 | word2[i]-48>9)
- {
- if(word2[i] == '\0')
- break;
- if(word2[i] != '-')
- {
- return 0;
- }
- }
- }
- if(word2[7]!='-' & word2[6] != '-')
- {
- return 0;
- }
- if(word2[7] == '-')
- {
- month[0]=word2[5];
- month[1]=word2[6];
- m = 1; //m=1的话,表示月份是2位,否则是1位,节约内存,借变量用一下
- }
- if(word2[6] == '-')
- {
- month[0] = '0';
- month[1] = word2[5];
- m = 0;
- }
- if(word2[9+m]!='\0' & word2[8+m] != '\0')
- {
- return 0;
- }
- if(word2[9+m] == '\0') //处理日期,因为要考虑1位的情况,使情况复杂多了
- {
- day[0] = word2[7+m];
- day[1] = word2[8+m];
- }
- if(word2[8+m]=='\0')
- {
- day[0] = '0';
- day[1] = word2[7+m];
- }
- year[4]='\0',month[2]='\0',day[2]='\0';
- y = (year[2]-48)*10 + (year[3]-48);
- m = (month[0]-48)*10 + (month[1]-48);
- d = (day[0]-48)*10 + day[1]-48; //先获取年月日的值,再判断合法性
- if(year[0] != (2+48) | year[1] != (0+48) | //这个简单,必须是20XX年
- y > 100 | y < 1 | m > 12 | m <1) //年与月都简单,日最麻烦
- {
- return 0;
- }
- if(!(m==1|m==3|m==5|m==7|m==8|m==10|m==12) & (d>=31)) //除了大月外都不能有31号
- {
- return 0;
- }
- if(d>31)
- {
- return 0;
- }
- if(m==2 & d>28)
- {
- if(!(((y%4)==0) & (d==29))) //2月是很蛋痛的,好在只是100年内
- {
- return 0;
- }
- }
- TIME[6]=(year[2]-48)*16+(year[3]-48);
- TIME[4]=(month[0]-48)*16+(month[1]-48);
- TIME[3]=(day[0]-48)*16+(day[1]-48);
-
- //下面这段把设置的日期写入到DS1302中
- Ds1302Write(0x8E,0X00); //禁止写保护,就是关闭写保护功能
- for (i=0; i<7; i++)//写入7个字节的时钟信号:分秒时日月周年
- {
- Ds1302Write(WRITE_RTC_ADDR[i],TIME[i]);
- }
- Ds1302Write(0x8E,0x80); //打开写保护功能
- /*
- putstring(year);
- putstring(month);
- putstring(day);
- putstring("\r\n");
- */
- Ds1302ReadTime();
- return 1;
- }
- //设置时间,格式为settimer 02:22, 不支持省略0
- int settime()
- {
- char minutes[3],hour[3],i,
- h,m; //h存放小时,m存放分
-
- for(i=0; i<6; i++) //检查WORD2中字符的合法性,只能有数字或者:
- {
- if((word2[i]-48)<0 | word2[i]-48>9)
- {
- if(word2[i] == '\0')
- break;
- if(word2[i] != ':')
- {
- return 0;
- }
- }
- }
-
- if(word2[2]!=':' & word2[1] != ':')
- {
- return 0;
- }
- if(word2[2] == ':')
- {
- hour[0]=word2[0];
- hour[1]=word2[1];
- m = 1; //m=1的话,表示小时是2位,=0是1位
- }
- if(word2[1] == ':')
- {
- hour[0] = '0';
- hour[1] = word2[0];
- m = 0;
- }
- if(word2[4+m]!='\0' & word2[3+m] != '\0')
- {
- return 0;
- }
- if(word2[4+m] == '\0') //要考虑小时1位的情况
- {
- minutes[0] = word2[2+m];
- minutes[1] = word2[3+m];
- }
- if(word2[3+m]=='\0')
- {
- minutes[0] = '0';
- minutes[1] = word2[2+m];
- }
- minutes[2]='\0',hour[2]='\0';
- h = (hour[0]-48)*10 + (hour[1]-48);
- m = (minutes[0]-48)*10 + (minutes[1]-48);
- if(h > 23 | h <= 0 | m > 59 | m < 0) //这个比日期简单多了
- {
- return 0;
- }
- Ds1302ReadTime();
- TIME[2]=(hour[0]-48)*16+(hour[1]-48); //BCD码
- TIME[1]=(minutes[0]-48)*16+(minutes[1]-48);
- TIME[0]=0;
- Ds1302Write(0x8E,0X00); //禁止写保护,就是关闭写保护功能
- for (i=0; i<7; i++)//写入7个字节的时钟信号:分秒时日月周年
- {
- Ds1302Write(WRITE_RTC_ADDR[i],TIME[i]);
- }
- Ds1302Write(0x8E,0x80); //打开写保护功能
- Ds1302ReadTime();
- // printf("\nThe current time: ");
- return 1;
- }
- void main()
- {
- char Secondcheck, //检查秒有没有改变
- Mincheck, //检查分有没有改变
- date[11];
- SCON = 0x50; //串口方式1,允许接收
- TMOD = 0x20; //定时器1定时方式2
- TCON = 0x40; //设定时器1开始计数
- TH1 = 0xFD; //11.0592MHz 1200波特率 只有9600能用未找原因
- TL1 = 0xFD;
- TI = 1;
- TR1 = 1; //启动定时器
- printf ("Initing.... \n");
- tempDisplay(Ds18b20ReadTemp());
- LCD_init(); //初始化液晶
- LCD_clear();
- LCD_write_english_string(0,1," Initing.... ");
- delay(1000);
- Mincheck=11; //解决分钟为0时初始化过久的问题
- if(SETTIME==0)
- {
- for(Mincheck=3;Mincheck>0;Mincheck--)
- {
- printf("\nPlease Set Date:");
- scanf("%s",word2);
- //printf("%s",word2);
- if(setdate())
- break;
- }
- for(Mincheck=3;Mincheck>0;Mincheck--)
- {
- printf("\nPlease Set Time:");
- scanf("%s",word2);
- //printf("%s\n",word2);
- if(settime())
- break;
- }
- }
- LCD_clear();//清除LCD
- while(1)
- {
- Ds1302ReadTime();//读取时间1302
-
- delay(500);//多给点时间发送,但是影响秒的同步看来要改定时器发送
-
-
-
- if(Mincheck!=TIME[0])////如果时间秒变化就发送一次串口数据
-
- {
-
- Mincheck=TIME[0];//让Mincheck等于秒,用于下次发送检测
-
- date[0]='2',//发送年
- date[1]='0',
- date[2]=TIME[6]/16+48,//发送年
- date[3]=TIME[6]%16+48;
- date[4]='-',
- date[5]=(TIME[4]/16+48),//发送月
- date[6]=(TIME[4]%16+48),
- date[7]='-',
- date[8]=(TIME[3]/16+48),//发送日
- date[9]=TIME[3]%16+48;
- printf("%s",date);//发送以上全部的 date
-
- printf(" Time: ");
- putchar(TIME[2]/16+48);//发送时
- putchar(TIME[2]%16+48);
- putchar(':');//发送“ :”
- putchar(TIME[1]/16+48);//发送分
- putchar(TIME[1]%16+48);
- putchar(':');//发送“ :”
- putchar(TIME[0]/16+48);//发送秒
- putchar(TIME[0]%16+48);
- printf(" WD: ");
- printf(tempDisplay(Ds18b20ReadTemp()));
- putchar('\n');//结束串口开始符号
-
-
- LCD_write_english_string(25,1," bbjj ");
- LCD_write_english_string(8,0,date);
- LCD_write_chinese_string(0,3,12,1,TIME[2]/16,5);//显示时
- LCD_write_chinese_string(10,3,12,1,TIME[2]%16,5);
- LCD_write_chinese_string(20,3,12,1,11,5);
- LCD_write_chinese_string(30,3,12,1,TIME[1]/16,5);//显示分
- LCD_write_chinese_string(40,3,12,1,TIME[1]%16,5);
- LCD_write_chinese_string(50,3,12,1,11,5);
- LCD_write_chinese_string(60,3,12,1,TIME[0]/16,5);//显示秒
- LCD_write_chinese_string(70,3,12,1,TIME[0]%16,5);
-
-
- }
-
-
- if(Secondcheck!=TIME[0])//如果温度变化就显示一下
- {
- Secondcheck=TIME[0];
- LCD_write_english_string(10,5,"WD:");
- LCD_write_english_string(30,5,tempDisplay(Ds18b20ReadTemp()));//读取温度显示18B20
- }
-
- }
- }
复制代码
所有资料51hei提供下载:
51单片机&5110液晶显示屏实现时间温度串口发送(附仿真截图).7z
(544.03 KB, 下载次数: 60)
|