项目名称:测温过温报警系统及时钟系统
此系统主要由AT89C51、DS18B20温度模块、LCD1602、喇叭组成。大致的原理是DS18B20温度采集到的数据传送给AT89C51的P3.4,最后通过LCD1602显示当前的实时温度,根据中断按键判断显示时间还是温度。
复习使用
(1) DS18B20简介
1.传感器参数:
(1)测温范围为-55℃到+125℃,在-10℃到+85℃范围内误差为±0.4°。
(2)返回16位二进制温度数值。
(3)主机和从机通信使用单总线,即使用单线进行数据的发送和接收。
(4)在使用中不需要任何外围元件,独立芯片即可完成工作。
(5)掉电保护功能 DS18B20 内部含有 EEPROM ,通过配置寄存器可以设定数字转换精度和报警温度,在系统掉电以后,它仍可保存分辨率及报警温度的设定值。
(6)每个DS18B20都有独立唯一的64位-ID,此特性决定了它可以将任意多的DS18b20挂载到一根总线上,通过ROM搜索读取相应DS18B20的温度值。
(7)宽电压供电,电压2.5V~5.5V
2.DS18B20内部结构:
主要由4部分组成:64 位ROM、温度传感器、非挥发的温度报警触发器TH和TL、配置寄存器。ROM中的64位序列号是出厂前被光刻好的,它可以看作是该DS18B20的地址序列码,每个DS18B20的64位序列号均不相同。64位ROM的排的循环冗余校验码(CRC=X^8+X^5+X^4+1)。 ROM的作用是使每一个DS18B20都各不相同,这样就可以实现一根总线上挂接多个DS18B20的目的。
3.DS18B20内部构成:
高速暂存存储器由9个字节组成,当温度转换命令发布后,经转换所得的温度值以二字节补码形式存放在高速暂存存储器的第0和第1个字节。单片机可通过单线接口读到该数据,读取时低位在前,高位在后,对应的温度计算:当符号位S=0时,直接将二进制位转换为十进制;当S=1时,先将补码变为原码,再计算十进制值。
4.DS18B20 单线通信:
DS18B20 单线通信功能是分时完成的,他有严格的时隙概念,如果出现序列混乱, 1-WIRE 器件将不响应主机,因此读写时序很重要。系统对 DS18B20 的各种操作必须按协议进行。
- LCD1602简介
1.引脚功能:
·引脚1:VSS为地电源。
·引脚2:VDD接5V正电源。
·引脚3:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”现象,使用时可以通过一个10kQ的电位器调整其对比度。
·引脚4:RS为寄存器选择脚,高电平时选择数据寄存器、低电平时选择指令寄存器。
·引脚5:R/W为读/写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或显示地址;当RS为低电平,R/W为高电平时,可以读忙信号;当RS为高电平,R/W为低电平时,可以写入数据。
·引脚6:E端为使能端,当E端由高电平跳变为低电平时,液晶模块执行命令。
·引脚7~14:D0~D7为8位双向数据线。
·引脚15:背光源正极。
·引脚16:背光源负极。
2.LCD1602的基本操作
①读状态:RS=0,RW=1,E=高脉冲。输出:D0~D7为状态字。
②读数据:RS=1,RW=1,E=高脉冲。输出:D0~D7为数据。
③写指令:RS=0,RW=0,E=高脉冲。输出:无
④写数据:RS=1,RW=0,E=高脉冲。输出:无。
3、指令说明
显示模式设置
显示开/关光标设置
此次实践中主要查询的是这两个元件,通过了解其原理与工作方式,实现所需功能。其次是查询中断及定时器使用,不在赘述。
- 功能简介
本系统基本功能为测温过温警报及时间显示,按键P1.0、P1.1控制设定温度的加减,P1.2控制是否报警。按键P3.3按下为进入中断,此时系统切换显示此刻时间,时间初始值需人为设定。
3、调试过程中遇到的问题
(1)初次使用LCD1602时,编写程序发现液晶屏上显示不了
解决方案:LCD1602显示字符,需将显示数字改为字符串形式。
(2)DS18B20初始化一直失败
解决方案:DS18B20时序性非常严格,需准确定时,故单独为此元件设定了延时函数。
- DS18B20温度转换出现问题,零上温度小数均可正常显示而零下小数为乱码 暂未解决
- 切换时间显示和温度显示时LCD1602出现重影和乱码现象
解决方案:每次调用时间或温度显示时先清屏,但只能调用一次,一直调用会出现闪烁重影现象,故需加上标志位,每次调用函数时标志位为1,执行一次后标志位置0,标志位为1时执行清屏动作,并且将另一个显示函数的标志位置1,如此便可使每次调用函数时都能使清屏动作执行一次。
protues仿真图:
温度显示:
时间显示:
附上代码:
#include <reg52.h>
#define uint unsigned int
#define uchar unsigned char
#define LCD P2
sbit E=P3^0; //LCD使能端口定义
sbit RW=P3^1; //LCD读写使能端口定义
sbit RS=P3^2; //LCD数据/命令选择方式端口定义
sbit bflag=P2^7; //LCD忙指示端口,高电平为忙
sbit DQ = P3^4; //DS18B20引脚
sbit P10=P1^0; //温度加一
sbit P11=P1^1; //温度减一
sbit P12=P1^2; //报警
sbit P13=P1^3; //喇叭
sbit TW=P3^3; //显示时间还是温度
uint readtemp=0; //读取温度
uint settemp=25; //设定温度
uchar i=0;
uchar miao=50; //时间显示
uchar fen=59;
uchar shi=23;
uchar str[]={"0123456789"}; //驱动数组
uchar str1[]={"Temp:"}; //读取温度
uchar str3[]={"Set:"}; //设定温度
uchar str4[]={"Time:"}; //时间
uint xtemp; //装载小数
int flag=1; //温度清屏标志
int balg=1; //时间清屏标志位
void delay_18b20(uint i) //DS18B20专用延时
{for(;i>0;i--);
}void delay(uint x)
{uint i,j;for(i=0;i<x;i++){for(j=0;j<120;j++);}
}
void check_busy(void) //检查忙标志位函数
{uchar dt;do{dt=0xff;E=0;RS=0;RW=1; //只有RS=0,RW=1,E=1时才能读忙标志位E=1;dt=P2; //P0口的状态送入dt中}while(dt&0x80); //如果忙标志位BF=1,继续循环检测,等待BF=0E=0; //BF=0,结束检测
}
void writedat(unsigned char dat) //函数功能:写数据函数
{check_busy();RS=1; //当RS=1,RW=0时,表明写入的是数据RW=0;E=0;P2=dat;delay(5); //当使能由高到低时,LCD执行相应命令E=1;E=0;
}
void writecom(unsigned char com) //函数功能:写指令函数
{check_busy();RS=0; //当RS=0,RW=0时,表明写入的是命令RW=0;E=0;P2=com; //当使能由高到低时,LCD执行相应命令delay(5);E=1;E=0;
}
void initlcd() //函数功能:设置LCD_1602的开显示 光标不闪烁等的功能
{writecom(0x38); //指令6,8位数据总线,双行显示,每位采用5*7点阵 指令6writecom(0x08); //指令4,关显示,关光标,无闪烁 指令4writecom(0x06); //指令3,光标自动右移,文字不移动 指令3writecom(0x01); //指令1,清显示 指令1writecom(0x0c); //指令4,开显示 指令4
}
void InitDs18b20(void) //初始化
{uchar x=0;DQ=1;delay_18b20(8);DQ = 0; delay_18b20(80); DQ=1;delay_18b20(14);x=DQ;delay_18b20(20);
}
unsigned char ReadOneChar(void) //读字节
{unsigned char i=0;unsigned char dat = 0; //接收数据for(i=0;i<8;i++){DQ= 0;dat=dat>>1; //右移一位DQ = 1; //释放总线if(DQ)dat = dat|0x80;delay_18b20(4);}return dat;
}
void WriteOneChar(unsigned char dat) //写字节
{unsigned char i;for(i=0;i<8;i++){DQ= 0; //拉低电平DQ = dat&0x01;if(DQ){delay_18b20(1);DQ=1;}else { delay_18b20(5);DQ=1;}dat = dat>>1;}
}
uchar ReadTemperature(void)
{unsigned char a=0,b=0;uchar temp=0;InitDs18b20(); //初始化DS18B20WriteOneChar(0xcc); //跳过ROMWriteOneChar(0x44); //启动温度转换指令delay_18b20(100);InitDs18b20(); //每一次写命令都需要初始化WriteOneChar(0xcc); //跳过ROMWriteOneChar(0xbe); //读暂存器内容a=ReadOneChar(); //获取温度的第一个字节 a存低字节b=ReadOneChar(); //获取温度的第二个字节 b存高字节 00000 111 1111 0000 temp=b<<4|a>>4; // 获取温度整数值xtemp=a&0x0f; //获取小数return (temp);
}void display() //正温度显示{uint temp0=0,temp1=0,temp2=0,temp3=0,i=0;temp0=readtemp/100;temp1=(readtemp%100)/10;temp2=readtemp%10;temp3=xtemp*625/1000;writecom(0x80);delay(5);while(str1[i]!='\0'){writedat(str1[i]);i++;}writecom(0x80+7); //定义地址delay(1);writedat(str[temp0]); //百位delay(1);writedat(str[temp1]); //十位delay(1);writedat(str[temp2]); //个位delay(1);writedat('.');delay(1);writedat(str[temp3]); //小数delay(1);writedat(0xdf);delay(1);writedat('c');delay(1);
}
void displayfu() //负温度显示
{uint temp0=0,temp1=0,temp2=0,temp3=0,i=0;temp0=readtemp/100;temp1=(readtemp/10)%10;temp2=readtemp%10;
/* xtemp=~xtemp;xtemp=xtemp&0x0f;xtemp=xtemp+1;temp3=xtemp*625/1000;
*/writecom(0x80);delay(1);while(str1[i]!='\0'){writedat(str1[i]);i++;}writecom(0x80+7);delay(1);writedat('-');writecom(0x80+8);delay(1);writedat(str[temp0]);delay(1);writedat(str[temp1]);delay(1);writedat(str[temp2]);delay(1);writedat('.');delay(1);writedat(str[temp3]);delay(1);writedat(0xdf);delay(1);writedat('c');delay(1);
}
void display0() //0 时
{uint temp0=0,temp1=0,temp2=0,temp3=0,i=0;temp0=readtemp/100;temp1=(readtemp%100)/10;temp2=readtemp%10;temp3=xtemp*625/1000;writecom(0x80);delay(1);while(str1[i]!='\0'){writedat(str1[i]);i++;}writecom(0x80+7);delay(1);writedat(str[temp0]);delay(1);writedat(str[temp1]);delay(1);writedat(str[temp2]);delay(1);writedat('.');delay(1);writedat(str[temp3]);delay(1);writedat(0xdf);delay(1);writedat('c');delay(1);
}
void wenkong()
{if(P10==0) //温控加一{settemp=settemp+1;while(P10==0); //检测按键是否弹起}if(P11==0) //温控减一{settemp=settemp-1;while(P11==0); //检测按键是否弹起}
}
void displayset() //显示设定的温度
{uint temp0=0,temp1=0,temp2=0,i=0;temp0=settemp/100;temp1=(settemp%100)/10;temp2=settemp%10;writecom(0x80+0x40);while(str1[i]!='\0'){writedat(str3[i]);i++;}writecom(0x80+0x40+7);delay(1);writedat(str[temp0]);delay(1);writedat(str[temp1]);delay(1);writedat(str[temp2]);delay(1);writedat('.');delay(1);writedat('0');delay(1);writedat(0xdf);delay(1);writedat('c');delay(1);
}
void delay500()
{unsigned char i;for(i=250;i>0;i--);}
void displaymusic() //报警程序
{uchar readtemp1;uchar count;if(P12==0){readtemp1=readtemp;if(readtemp>settemp){for(count=200;count>0;count--){P13=~P13;delay500();}for(count =200;count>0;count--){ P13=~P13;delay500();delay500();}}}}void findisplay() //温度显示整合
{displayset();wenkong();displaymusic();if(ReadTemperature()==0){writecom(0x01);readtemp=0;display0();}if(ReadTemperature()<128){readtemp=ReadTemperature();display();}else{char a=0;a=ReadTemperature();readtemp=~a+1;displayfu();}
}
void timedisplay()
{ uint i=0;writecom(0x80); //第一行显示time:while(str4[i]!='\0'){writedat(str4[i]);i++;}writecom(0x80+0x40+4); //第二行显示 时间 00:00:00writedat(str[shi/10]); //小时数writedat(str[shi%10]);writedat(':');writedat(str[fen/10]); //分钟数writedat(str[fen%10]);writedat(':');writedat(str[miao/10]); //秒数writedat(str[miao%10]);
}void timer0() interrupt 1 //定时50ms 0.05s
{char i;i++;TH1=(65536-50000)/256;TL1=(65536-50000)%256;if(i==20){i=0;miao++;}if(miao==60){miao=0;fen++;}if(fen==60){fen=0;shi++;}if(shi==24){shi=0;}
}void int1() interrupt 2
{if(balg==1) //清屏标志{writecom(0x01);balg=0;}timedisplay();flag=1; //方便下次温度显示清屏
}
void main()
{TMOD=0x01;TL0=(65536-50000)%256 ; //定义50msTH0=(65536-50000)/256 ;IE=0X86; //启用 TF0 INT1TR0=1;initlcd();while(1){balg=1; //中断清屏准备if(flag==1){writecom(0x01);flag=0;}findisplay();}
}