51单片机 万年历
这个小项目自己做了5天,小有成就感。。。感谢前辈们多多提意见。。
12864液晶、DS12C887时钟芯片、DS18B20温度传感器。。。
#define uchar unsigned char #define uint unsigned int uint temp; float f_temp; sbit Ds=P2^2; sbit Dula=P2^6; sbit Wela=P2^7; sbit LcdCS=P3^5; //寄存器选择输入 通过rs确定是写数据还是写命令 sbit LcdSID=P3^6; //液晶读/写控制 因为不从液晶读取任何数据,所以rw一直为0 sbit LcdSCLK=P3^4; //液晶使能控制 给en一个高脉冲将数据送入液晶控制器 sbit LcdPSB=P3^7; //串并方式控制 sbit DSCS=P1^4; //片选信号,低电平有效 sbit DSAS=P1^5; //地址选通输入端 AS的上升沿将AD0~AD7上出现的地址信息锁存到DS12C887上,下降沿清除地址信息 sbit DSRW=P1^6; // sbit DSDS=P1^7; // uchar code table1[]={" CHPAVC 天若海愚"}; uchar code table3[]={"星期一"}; uchar code table4[]={"星期二"}; uchar code table5[]={"星期三"}; uchar code table6[]={"星期四"}; uchar code table7[]={"星期五"}; uchar code table8[]={"星期六"}; uchar code table9[]={"星期日"}; uchar code table10[]={"°C"}; uchar buff[4]; char year,month,day,week,miao,fen,shi; uchar nyrsfm[17]; //Write_nyrsfm函数中,方法二nyrsfm只需申明成nyrsfm[16]
/*************************************************************** 文件名称: wannianli 作者 : 天若海愚 版本号 : V1.0 说明 : 创建时间: 2013年9月17日13:30:38 修改记录: 无 备注 : 在12864液晶屏上显示从DS12C887采集到的年、月、日、时、分数据,并且利用DS18B20采集温度显示在12864上 ***************************************************************/ #include<reg52.h> #include<define.h> #include<intrins.h> #include<stdio.h> /*********************************** 函数名称: Delay() 函数功能: 延时 入口参数: z 备注 : ***********************************/ void Delay(uint z) { uint x,y; for(x=z;x>0;x--) for(y=110;y>0;y--); } /********************************** 函数名称: Ds_reset() 函数功能: DS18B20复位,初始化函数 入口参数: 无 备注 : **********************************/ void Ds_reset() { uint i; Ds=1; //书P348,时序图,先将数据线置高电平1 _nop_(); //延时,尽可能短一点 Ds=0; i=103; while(i>0) i--; //当总线停留在低电平超过480us,总线上所以器件都将被复位,这里 //延时约680us总线停留在低电平超过480μs,总线上的所有器件都将被复位 _nop_(); //延时,尽可能短一点 Ds=1; i=5; while(i>0) i--; //释放总线后,如果初始化成功则在15~60us内产生一个由DDS18B20返回的低电平0,据 //该状态可以确定它的存在,但是不能无限制地等 } /********************************** 函数名称: Ds_read_bit() 函数功能: 读一位数据函数 入口参数: 无 备注 : **********************************/ bit Ds_read_bit() { uint i; bit dat; Ds=0; //单片机(微处理器)将总线拉低 _nop_(); //读时隙起始于微处理器将总线拉低至少1us Ds=1; //拉低总线后接着释放总线,让从机18b20能够接管总线,输出有效数据 _nop_(); _nop_(); //小延时一下,读取18b20上的数据 ,因为从ds18b20上输出的数据,在读"时间隙"下降沿出现15us内有效 dat=Ds; //主机读从机18b20输出的数据,这些数据在读时隙的下降沿出现15us内有效 i=10; while(i>0) i--; //所有读"时间隙"必须60~120us,这里77us return(dat); } /********************************** 函数名称: Ds_read_byte() 函数功能: 读一个字节数据函数 入口参数: 无 备注 : **********************************/ uchar Ds_read_byte() { uchar i,j,dat; dat=0; //初值不能忘 for(i=1;i<=8;i++) //此句曾写成for(i=0;i<=8;i++),导致程序显示结果出错,细节。。。 { j=Ds_read_bit(); dat=(j<<7)|(dat>>1); //读出的数据最低位在最前面,这样刚好一个字节在dat里 } return(dat); } /********************************** 函数名称: Ds_write_byte() 函数功能: 向DS18B20写一个字节函数 入口参数: 备注 : **********************************/ void Ds_write_byte(uchar dat) { uchar i; uint j; bit testb; for(i=1;i<=8;i++) { testb=dat&0x01; dat=dat>>1; if(testb) { Ds=0; _nop_(); _nop_(); //看时序图,至少延时1us,才产生写"时间隙" Ds=1; //写时间隙开始后的15μs内允许数据线拉到高电平 j=8; while(j>0) j--; //所有写时间隙必须最少持续60us } else { Ds=0; j=8; while(j>0) j--; //主机要生成一个写0 时间隙,必须把数据线拉到低电平并保持至少60μs, Ds=1; _nop_(); _nop_(); } } } /********************************** 函数名称: Ds_change() 函数功能: 18b20开始获取温度并转换 入口参数: 备注 : **********************************/ void Ds_change() { Ds_reset(); //初始化 Delay(1); Ds_write_byte(0xcc); //跳过ROM.直接向18b20发温度 转换指令,适用于一个从机工作 Ds_write_byte(0x44); //写温度转换指令 } /********************************** 函数名称: Get_temp() 函数功能: 读取寄存器中存储的温度数 入口参数: 无 备注 : **********************************/ uint Get_temp() { uchar a,b; Ds_reset(); Delay(1); Ds_write_byte(0xcc); //写跳过读ROM指令 Ds_write_byte(0xbe); //读暂存器。读内部RAM中9字节的温度数据 a=Ds_read_byte(); //读低8位 b=Ds_read_byte(); //读高8位 temp=b; temp<<=8; //两个字节组合为一个字 temp=temp|a; f_temp=temp*0.0625; //得到真实十进制温度值,因为DS18B20可以精确到0.0625 度,所以读回数据的最低位代表的是0.0625 度。 temp=f_temp*10+0.5; //乘以10不是小数点后面只取1位,加0.5是四舍五入 f_temp=f_temp+0.05; return temp; //temp是整型 } /********************************** 函数名称: init_com() 函数功能: 串口初始化 入口参数: 无 备注 : **********************************/ void init_com() { TMOD=0x20; //定时器1,方式2 PCON=0x00; SCON=0x50; //等价于REN=1;SM0=0;SM1=1; TH1=0xFD; TL1=0xFD; //设置波特率 9600 TR1=1; //启动定时器1 } /********************************** 函数名称: comm() 函数功能: 串口发送数据 入口参数: 备注 : **********************************/ void comm(uchar *parr) { do { SBUF=*parr++; while(!TI); //发送完毕,TI由硬件置1 TI=0; }while(*parr); //保持循环直到字符为'\0' } /*********************************** 函数名称: Send_byte() 函数功能: 发送一个字节 入口参数: bbyte 备注 : ***********************************/ void Send_byte(uchar bbyte) { uchar i; for(i=0;i<8;i++) { LcdSID=bbyte&0x80; //取出最高位 LcdSCLK=1; LcdSCLK=0; //sclk由低电平变为高电平的瞬间,液晶控制器将sid上数据读入或输出 bbyte<<=1; //左移1位 } } /*********************************** 函数名称: Write_char() 函数功能: 写数据或写命令 入口参数: start,ddata 备注 : ***********************************/ void Write_char(bit start,uchar ddata) { uchar start_data,Hdata,Ldata; if(start==0) { start_data=0xf8; //写指令 } else { start_data=0xfa; //写数据 } Hdata=ddata&0xf0; //取高4位 Ldata=(ddata<<4)&0xf0; //取低4位 Send_byte(start_data); //发送起始信号 Delay(5); //延时是必须的 Send_byte(Hdata); //发送高4位 Delay(1); //延时是必须的 Send_byte(Ldata); Delay(1); } /*********************************** 函数名称: Lcd_pos() 函数功能: 设置显示位置 入口参数: X,Y 备注 : ***********************************/ void Lcd_pos(uchar X,uchar Y) { uchar pos; if(X==0) { X=0x80; } else if(X==1) { X=0x90; } else if(X==2) { X=0x88; } else if(X==3) { X=0x98; } pos=X+Y; Write_char(0,pos); } /*********************************** 函数名称: Lcd_init() 函数功能: 液晶初始化 入口参数: 无 备注 : ***********************************/ void Lcd_init() { Delay(5); //启动等待,等LCD进入工作状态 LcdPSB=0; //串口驱动 Write_char(0,0x30); //基本指令操作 Delay(5); Write_char(0,0x0c); //显示开,关光标,反白关 Delay(5); Write_char(0,0x01); //清屏,将DDRAM的地址计数器归零 Delay(5); } /********************************** 函数名称: Display_temp() 函数功能: 在12864上显示温度 入口参数: 备注 : 小数点后保留一位 **********************************/ void Display_temp() { uchar i; Lcd_pos(2,4); Write_char(1,buff[0]); Write_char(1,buff[1]); Write_char(1,buff[2]); Write_char(1,buff[3]); for(i=0;i<3;i++) { Write_char(1,table10[i]); //显示符号°C Delay(5); } } /*********以下是操作DS12C87时钟芯片***********/ /*********************************** 函数名称: Write_ds() 函数功能: 写12C8887函数 入口参数: add,date 备注 : ***********************************/ void Write_ds(uchar add,uchar date) { DSCS=0; DSAS=1; DSDS=1; DSRW=1; P0=add; DSAS=0; //dsas的下降沿将AD0~~AD7上的地址信息锁存到DS12C887上 DSRW=0; P0=date; DSAS=1; DSRW=1; //INTER模式,dsrw的上升沿锁存数据 DSCS=1; } /*********************************** 函数名称: Read_ds() 函数功能: 读12C8887函数 入口参数: add 备注 : ***********************************/ uchar Read_ds(uchar add) { uchar ds_date; DSAS=1; DSDS=1; DSRW=1; DSCS=0; P0=add; //先写地址 DSAS=0; DSDS=0; P0=0xff; ds_date=P0; //再读数据 DSDS=1; DSCS=1; DSAS=1; return ds_date; } /********************************** 函数名称: Write_nyrsfm() 函数功能: 将年、月、日、时、分显示在12864液晶上 入口参数: 备注 : 不能通过下面的/* 中的函数来实现 **********************************/ void Write_nyrsfm(char year,char month,char day,char shi,char fen) { //方法一: /*uchar i; Lcd_pos(1,0); nyrsfm[0]=0x32; nyrsfm[1]=0x30; nyrsfm[2]=year/10+0x30; nyrsfm[3]=year%10+0x30; nyrsfm[4]='-'; nyrsfm[5]=month/10+0x30; nyrsfm[6]=month%10+0x30; nyrsfm[7]='-'; nyrsfm[8]=day/10+0x30; nyrsfm[9]=day%10+0x30; nyrsfm[10]=' '; nyrsfm[11]=shi/10+0x30; nyrsfm[12]=shi%10+0x30; nyrsfm[13]=':'; nyrsfm[14]=fen/10+0x30; nyrsfm[15]=fen%10+0x30; nyrsfm[16]='\0'; //while(nyrsfm[i]!='\0') //while()循环不显示,是错误的 //{ // Write_char(1,nyrsfm[i]); // i++; //} for(i=0;i<17;i++) { Write_char(1,nyrsfm[i]); Delay(5); }*/ //方法二: Lcd_pos(1,0); nyrsfm[0]=0x32; Write_char(1,0x32); nyrsfm[1]=0x30; Write_char(1,0x30); nyrsfm[2]=year/10+0x30; Write_char(1,nyrsfm[2]); nyrsfm[3]=year%10+0x30; Write_char(1,nyrsfm[3]); nyrsfm[4]='-'; Write_char(1,nyrsfm[4]); nyrsfm[5]=month/10+0x30; Write_char(1,nyrsfm[5]); nyrsfm[6]=month%10+0x30; Write_char(1,nyrsfm[6]); nyrsfm[7]='-'; Write_char(1,nyrsfm[7]); nyrsfm[8]=day/10+0x30; Write_char(1,nyrsfm[8]); nyrsfm[9]=day%10+0x30; Write_char(1,nyrsfm[9]); nyrsfm[10]=' '; Write_char(1,nyrsfm[10]); nyrsfm[11]=shi/10+0x30; Write_char(1,nyrsfm[11]); nyrsfm[12]=shi%10+0x30; Write_char(1,nyrsfm[12]); nyrsfm[13]=':'; Write_char(1,nyrsfm[13]); nyrsfm[14]=fen/10+0x30; Write_char(1,nyrsfm[14]); nyrsfm[15]=fen%10+0x30; Write_char(1,nyrsfm[15]); } /********************************** 函数名称: Write_week() 函数功能: 星期显示 入口参数: xq 备注 : **********************************/ void Write_week(char xq) { uchar i; Lcd_pos(2,0); switch(xq) { case 1: i=0; while(table3[i]!='\0') { Write_char(1,table3[i]); i++; } break; case 2: i=0; while(table4[i]!='\0') { Write_char(1,table4[i]); i++; } break; case 3: i=0; while(table5[i]!='\0') { Write_char(1,table5[i]); i++; } break; case 4: i=0; while(table6[i]!='\0') { Write_char(1,table6[i]); i++; } break; case 5: i=0; while(table7[i]!='\0') { Write_char(1,table7[i]); i++; } break; case 6: i=0; while(table8[i]!='\0') { Write_char(1,table8[i]); i++; } break; case 7: i=0; while(table9[i]!='\0') { Write_char(1,table9[i]); i++; } break; } } /********************************** 函数名称: Set_time() 函数功能: DS12C887首次上电初始化时间函数 入口参数: 无 备注 : **********************************/ void Set_time() { Write_ds(2,25); Write_ds(4,10); Write_ds(6,3); Write_ds(7,18); Write_ds(8,9); Write_ds(9,13); } /*********************************** 函数名称: init() 函数功能: 初始化 入口参数: 无 备注 : ***********************************/ void init() { uchar i; i=0; Wela=0; Dula=0; EA=1; EX1=1; IT1=1; Set_time(); //首次上电时使用,只需执行一次 Lcd_pos(3,0); while(table1[i]!='\0') { Write_char(1,table1[i]); i++; } } /********************************** 函数名称: Main() 函数功能: 入口参数: 备注 : **********************************/ void Main() { init_com(); //串口初始化 Lcd_init(); //LCD初始化 init(); //不能放在Lcd_init()函数前面,,只有在液晶初始化之后才能够显示init()函数的内容 while(1) { Ds_change(); //读取寄存器中存储的温度数 Get_temp(); //获取温度并转换 sprintf(buff,"%.1f",f_temp); //将浮点型温度格式化为字符型,并保留一位,需声明#include<stdio.h> Delay(1000); comm(buff); //串口发送数据,在串口调试助手上显示,这里主要是为了测试buff的内容而加上这句的 Display_temp(); //在12864液晶上显示温度 Delay(5); year=Read_ds(9); //从DS12C887上读取数据 month=Read_ds(8); day=Read_ds(7); week=Read_ds(6); shi=Read_ds(4); fen=Read_ds(2); Write_nyrsfm(year,month,day,shi,fen); //在12864液晶显示 Write_week(week); //显示星期 } }