|
LCD1602程序仿真正常,实物上显示不正常,程序在附件里面
单片机源程序如下:
- #include <reg51.h> //调用含有51专用寄存器的头文件
- #include<intrins.h>
- #define uchar unsigned char //用uchar代替unsigned char
- #define uint unsigned int //用uint代替unsigned int
- #define yh 0x80 //LCD第一行的初始位置,因为LCD1602字符地址首位D7恒定为1(100000000=80)
- #define er 0x80+0x40 //LCD第二行初始位置(因为第二行第一个字符位置地址是0x40)
- char a,sec,min,hour,day,mouth,temp,n; //定义变量
- sbit rs=P3^0; //液晶指令/数据输入控制
- sbit en=P3^2; //液晶使能端
- sbit Data=P3^6; //定义数据线
- uchar rec_dat[14]; //用于显示的接收数据数组
- sbit IO=P3^5; //数据线
- sbit SCLK=P3^4; //时钟线
- sbit RST=P3^3; //使能
- sbit mode=P1^0; //设置键
- sbit s1 =P1^1; //加键
- sbit s2 =P1^2; //减键
- sbit yes =P1^3; //调整确认键
- sbit ACC0=ACC^0; //累加器A低位
- sbit ACC7=ACC^7; //累加器A高位
- uchar code tab1[]={"Tem: Hum: %"}; //时间显示的固定字符
- uchar code tab2[]={" - : : "}; //时间显示的固定字符
- /***********************延时子程序********************/
- void delay(uint xms)
- {
- uint x,y;
- for(x=xms;x>0;x--)
- for(y=110;y>0;y--);
- }
- /****************液晶写入指令函数*******************/
- void write_1602com(uchar com)
- {
- rs=0; //数据/指令选择置为指令
- P2=com; //送入数据
- delay(1); //看时序图,可以得出,写程序需要时间
- en=1; //拉高使能端,为制造有效的下降沿做准备
- delay(1);
- en=0; //en由高变低,产生下降沿,液晶执行命令
- }
- /********************液晶写入数据函数***************/
- void write_1602dat(uchar dat)
- {
- rs=1; //数据/指令选择置为数据
- P2=dat; //送入数据
- delay(1);
- en=1; //en置高电平,为制造下降沿做准备
- delay(1);
- en=0; //en由高变低,产生下降沿,液晶执行命令
- }
- /**************液晶初始化函数**********************/
- void lcd_init()
- {
- write_1602com(0x38); //设置液晶工作模式,
- write_1602com(0x0c); //开显示不显示光标
- write_1602com(0x06); //整屏不移动,光标自动右移
- write_1602com(0x01); //清显示
- write_1602com(yh+1); //日历显示固定符号从第一行第1个位置之后开始显示
- for(a=0;a<14;a++)
- {
- write_1602dat(tab1[a]); //向液晶屏写日历显示的固定符号部分
- }
- write_1602com(er+1); //时间显示固定符号写入位置,从第2个位置后开始显示
- for(a=0;a<14;a++)
- {
- write_1602dat(tab2[a]); //写显示时间固定符号,两个冒号
- }
- }
- /***************写数据********************/
- void write_byte(uchar dat)
- {
- ACC=dat;
- RST=1;
- for(a=8;a>0;a--)
- {
- IO=ACC0;
- SCLK=0;
- SCLK=1;
- ACC=ACC>>1;
- }
- }
- /******************读数据********************/
- uchar read_byte()
- {
- RST=1;
- for(a=8;a>0;a--)
- {
- ACC7=IO;
- SCLK=1;
- SCLK=0;
- ACC=ACC>>1;
- }
- return (ACC);
- }
- /*********向1302芯片写函数,指定写入地址,数据*********/
- void write_1302(uchar add,uchar dat)
- {
- RST=0;
- SCLK=0;
- RST=1;
- write_byte(add);
- write_byte(dat);
- SCLK=1;
- RST=0;
- }
- /*********从1302读数据函数,指定读取数据来源地址*******/
- uchar read_1302(uchar add)
- {
- uchar temp;
- RST=0;
- SCLK=0;
- RST=1;
- write_byte(add);
- temp=read_byte();
- SCLK=1;
- RST=0;
- return(temp);
- }
- /****************BCD码转十进制函数,输入BCD,返回十进制*******/
- uchar BCD(uchar m)
- {
- uchar x;
- x=m>>4;
- return(x=x*10+(m&=0x0f));
- }
- /********1302芯片初始化子函数(2019-05-19,16:36:00,week7)********/
- void ds1302_init()
- {
- RST=0;
- SCLK=0;
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x8e,0x80); //打开写保护
- }
- /*****************************月日时分秒显示子函数*******************************/
- void write_time(uchar add,uchar dat)//向LCD写时分秒,有显示位置加、现示数据,两个参数
- {
- uchar gw,sw;
- gw=dat%10; //取得个位数字
- sw=dat/10; //取得十位数字
- write_1602com(er+add); //er是头文件写的值0x80+0x40
- write_1602dat(0x30+sw); //数字+30得到该数字的LCD1602显示码
- write_1602dat(0x30+gw); //数字+30得到该数字的LCD1602显示码
- }
- /***********************按键处理函数**********************/
- void key()
- {
- if(mode==0) //第一次判断"模式"按键按下
- {
- delay(5); //延时消抖
- if(mode==0){TR0=0;n++;}while(!mode);}
- if(n>5) {TR0=1;n=0;} //如果第四次按下mode键则继续计时,并且重新记录按下的按键次数
- /**********************调整+********************************/
- if(n!=0) //当mode按下以后。再按以下键才有效(按键次数不等于零)
- {
- if(s1==0) //调整+键
- {
- delay(30);
- if(s1==0)
- {
- TR0=0;
- switch(n)
- {
- case 1:sec++; //设置键按动1次,调秒
- if(sec==60) sec=0; //秒超过59,再加1,就归零
- write_time(13,sec); //令LCD在正确位置显示"加"设定好的秒数
- temp=(sec)/10*16+(sec)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+14); //因为设置液晶的模式是写入数据后,指针自动+1,需要光标回位
- break;
- case 2:min++; //设置键按动2次,调分
- if(min==60) min=0; //分数据加到60时自动变成0
- write_time(10,min); //令LCD在正确位置显示"加"设定好的分数据
- temp=(min)/10*16+(min)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x82,temp); //向DS1302内写分寄存器82H写入调整后的分数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+11);
- break;
- case 3:hour++;
- if(hour==24) hour=0; //时数据加到24时自动变成0
- write_time(7,hour); //令LCD在正确的位置显示"加"设定好的小时数据
- temp=(hour)/10*16+(hour)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x84,temp); //向DS1302内写小时寄存器84H写入调整后的小时数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+8);
- break;
- case 4:day++; //设置键按动5次,调天
- if(day==32) day=1; //天数据加到32时自动变成1
- write_time(4,day); //令LCD在正确的位置显示"加"设定好的日期数据
- temp=(day)/10*16+(day)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x86,temp); //向DS1302内写日期寄存器86H写入调整后的日期数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+5);
- break;
- case 5:mouth++; //设置键按动6次,调月
- if(mouth>=13) mouth=1; //月数据加到12时自动变成1
- write_time(1,mouth); //令LCD在正确的位置显示"加"设定好的月份数据
- temp=(mouth)/10*16+(mouth)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x88,temp); //向DS1302内写月份寄存器88H写入调整后的月份数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+2);
- break;
- }
- }
- while(!s1);
- }
- }
- /**********************调整-********************************/
- if(n!=0) //当mode按下以后,再按以下键才有效
- {
- if(s2==0) //调整-键
- {
- delay(30);
- if(s2==0)
- {
- TR0=0; //关闭中断
- switch(n)
- {
- case 1:sec--; //设置键按动1次,调秒
- if(sec==-1) sec=59; //秒数据减到-1时自动变成59
- write_time(13,sec); //令LCD在正确位置显示"加"设定好的秒数
- temp=(sec)/10*16+(sec)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x80,temp); //向DS1302内写秒寄存器80H写入调整后的秒数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+14); //因为设置液晶的模式是写入数据后,指针自动+1,需要光标回位
- break;
- case 2:min--;
- if(min==-1) min=59; //分数据减到-1时自动变成59
- write_time(10,min); //令LCD在正确位置显示"加"设定好的分数据
- temp=(min)/10*16+(min)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x82,temp); //向DS1302内写分寄存器82H写入调整后的分数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+11);
- break;
- case 3:hour--;
- if(hour==-1) hour=23; //时数据减到-1时自动变成23
- write_time(7,hour); //令LCD在正确的位置显示"加"设定好的小时数据
- temp=(hour)/10*16+(hour)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x84,temp); //向DS1302内写小时寄存器84H写入调整后的小时数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+8);
- break;
- case 4:day--;
- if(day==-1) day=31; //天数据减到-1时自动变成31
- write_time(4,day); //令LCD在正确的位置显示"加"设定好的日期数据
- temp=(day)/10*16+(day)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x86,temp); //向DS1302内写日期寄存器86H写入调整后的日期数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+5);
- break;
- case 5:mouth--;
- if(mouth==-1) mouth=12; //月数据减到-1时自动变成12
- write_time(1,mouth);
- temp=(mouth)/10*16+(mouth)%10; //十进制转换成DS1302要求的DCB码
- write_1302(0x8e,0x00); //允许写,禁止写保护
- write_1302(0x88,temp); //向DS1302内写月份寄存器88H写入调整后的月份数据BCD码
- write_1302(0x8e,0x80); //打开写保护
- write_1602com(er+2);
- break;
- }
- }
- while(!s2); //按键松开检测
- }
- }
- if(n!=0)
- {
- if(yes==0) //调整值确认键按下,保存设置的参数,直接退出调整状态,进入走时。
- {
- delay(30);
- if(yes==0)
- {
- n=0,
- write_1602com(0x0c); //设置光标不闪烁
- TR0=1; //打开定时器
- temp=(sec)/10*16+(sec)%10;
- write_1302(0x8e,0x00);
- write_1302(0x80,0x00|temp); //秒数据写入DS1302
- write_1302(0x8e,0x80);
- }
- while(!yes); //松开按键检测
- }
- }
- }
- //*********************DHT11************************
- void DHT11_delay_us(uchar n)
- {
- while(--n);
- }
- void DHT11_delay_ms(uint z)
- {
- uint i,j;
- for(i=z;i>0;i--)
- for(j=110;j>0;j--);
- }
- void DHT11_start()
- {
- Data=1;
- DHT11_delay_us(2);
- Data=0;
- DHT11_delay_ms(30); //延时18ms以上
- Data=1;
- DHT11_delay_us(30);
- }
- uchar DHT11_rec_byte() //接收一个字节
- {
- uchar i,dat=0;
- for(i=0;i<8;i++) //从高到低依次接收8位数据
- {
- while(!Data); ////等待50us低电平过去
- DHT11_delay_us(8); //延时60us,如果还为高则数据为1,否则为0
- dat<<=1; //移位使正确接收8位数据,数据为0时直接移位
- if(Data==1) //数据为1时,使dat加1来接收数据1
- dat+=1;
- while(Data); //等待数据线拉低
- }
- return dat;
- }
- void DHT11_receive() //接收40位的数据
- {
- uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise;
- DHT11_start();
- if(Data==0)
- {
- while(Data==0); //等待拉高
- DHT11_delay_us(40); //拉高后延时80us
- R_H=DHT11_rec_byte(); //接收湿度高八位
- R_L=DHT11_rec_byte(); //接收湿度低八位
- T_H=DHT11_rec_byte(); //接收温度高八位
- T_L=DHT11_rec_byte(); //接收温度低八位
- revise=DHT11_rec_byte(); //接收校正位
- DHT11_delay_us(25); //结束
- if((R_H+R_L+T_H+T_L)==revise) //校正
- {
- RH=R_H;
- RL=R_L;
- TH=T_H;
- TL=T_L;
- }
- /*数据处理,方便显示*/
- rec_dat[0]='T';
- rec_dat[1]='e';
- rec_dat[2]='m';
- rec_dat[3]=':';
- rec_dat[4]='0'+(TH/10);
- rec_dat[5]='0'+(TH%10);
- rec_dat[6]=' ';
- rec_dat[7]='H';
- rec_dat[8]='u';
- rec_dat[9]='m';
- rec_dat[10]=':';
- rec_dat[11]='0'+(RH/10);
- rec_dat[12]='0'+(RH%10);
- rec_dat[13]='%';
- }
- }
- /****************************主函数**********************/
- void main()
- {
- uchar i;
- TMOD=0x11; //指定定时/计数器的工作方式为3
- TH0=0; //定时器T0的高四位=0
- TL0=0; //定时器T0的低四位=0
- EA=1; //开总中断
- ET0=1; //允许T0中断
- TR0=1; //开启中断,启动定时器
- lcd_init(); //调用液晶屏初始化子函数
- ds1302_init(); //调用DS1302时钟的初始化子函数
- DHT11_delay_ms(100); //DHT11上电后要等待1S以越过不稳定状态在此期间不能发送任何指令
- DHT11_receive();
- write_1602com(yh+1); //从lcd1602第一行第一个位置开始显示
- for(i=0;i<14;i++)
- write_1602dat(rec_dat[i]); //显示数据
-
- while(1)
- {
- key();
- }
-
- }
- void timer0() interrupt 1 //取得并显示日历和时间
- {
- /***************************读取秒时分周日月年七个数据***********************/
- sec = BCD(read_1302(0x81));
- min = BCD(read_1302(0x83));
- hour = BCD(read_1302(0x85));
- day = BCD(read_1302(0x87));
- mouth= BCD(read_1302(0x89));
- /**************************显示温度、秒、时、分数据***************************/
- write_time(13,sec); //秒,从第二行第8个字后开始显示(调用时分秒显示子函数)
- write_time(10,min); //分,从第二行第5个字符后开始显示
- write_time(7,hour); //小时,从第二行第2个字符后开始显示
- write_time(4,day); //日期,从第二行第9个字符后开始显示
- write_time(1,mouth); //月份,从第二行第6个字符后开始显示
- }
复制代码 |
|