1. 采用DS18B20数字温度传感器对两处进行温度的实时测量,通过LCD1602进行显示,并通过RS-485总线上传到主机。 2. 当温度超过设定值时能够报警,同时启动相关降温设备。 3. 主机通过按键和无线遥控能够对温度报警上限值及时间进行设置。 4. 能够对重要数据进行保存。 二、设计方案主机和从机选用STC89C52单片机,通过RS-485总线进行数据通信。主机采用LCD2002显示各从机的温度和当前时间,根据按键或无线遥控的输入命令对控制温度上限值、温度报警值和时间进行设置,并通过RS-485总线传递温度报警值给从机和读取从机温度。从机通过DS18B20采集现场温度,利用LCD1602进行显示,通过RS-485总线接收温度报警值,同时上传温度检测值给主机。 整个系统主要分3大块(主机、从机1和从机2)和十二个模块。(1)主机键盘部分,用来实现输入和设定温度等工作;(2)主机LCD1602显示电路;(3)主机单片机最小系统;(4)主机时钟电路;(5)主机存储电路;(6)主机继电器输出控制电路;(7)主机蜂鸣器报警电路;(8)主机RS-485通信部分;(9)从机RS-485通信部分;(10)从机单片机最小系统;(11)从机LCD1602显示部分;(12)从机温度检测电路。从机1和从机2硬件电路一样,都包括第9模块到第12模块。整个系统方案如图10-1所示。 图10-1 系统方案框图 三、硬件设计系统原理图如图10-2和图10-3所示,对应仿真图如图10-4所示。 图10-2 主机电路原理图 图10-3 从机电路原理图 图10-4 整个系统仿真图 从图10-4仿真图可以看出从机1当前的温度值为42.0℃,从机2当前的温度为34.0℃,温度报警上限温度值为39.0℃,主机与从机显示温度一致,说明Proteus仿真是完全正确的。 1.主机键盘。调整按键8个,其中S2、S3、S4和S5是手动按键,分别为设置键、加键、减键、退出键。通过调整按键可以对温度上限值、时间、星期、年月日的调整状态。 2.主机LCD显示电路。主机通过LCD1602液晶显示从机1和从机2实时采集的温度值、时间和日期。 3. 主机单片机最小系统。主机单片机最小系统是电路的控制中心,主要包括单片机、时钟电路和复位电路。 4.主机时钟电路。 采用DS1302实时时钟芯片进行年、月、日、时、分、秒刷新。可以通过按键调整时间,并具有系统掉电后启用备用电池向DS1302继续供电的功能,使用户不必每次上电调整时间。 5.主机存储电路。AT24C02用于存储用户设定的温度上限值和其他重要数据。 6.主机继电器输出控制电路。当温度大于等于用户设定的上限值时,P20和P21输出低电平,通过三极管VT1和VT2驱动继电器吸合,控制电扇通风降温,同时继电器接通指示灯亮。 7.主机蜂鸣器报警电路。当温度大于等于用户设定的上限值时,主机蜂鸣器报警。
8.主机RS-485通信部分。RS-485通信芯片选用的是SN65HVD3082ED,收发使能端由主机P3.7进行控制,R2用于防止末端信号发射,J5和J6为通信接口。 9.从机RS-485通信部分。RS-485通信芯片选用的是SN65HVD3082ED,收发使能端由主机P3.7进行控制,R2用于防止末端信号发射,J2和J3为通信接口。 10. 从机单片机最小系统。主要包括单片机、时钟电路、复位电路、电源接口和LED指示、ISP下载。 11.从机LCD1602显示部分。第一行显示主机发来的温度上限值,第二行显示从机1所在位置的温度值和下限值。 12. 从机温度检测电路。通过DS18B20进行温度采集,数据端口DQ连接到单片机的P3.3。 四、软件设计1. RS-485总线通信协议 主机STC89C52采用查询方式,从机STC89C52采用中断方式,具体协议如下: (1)主机STC89C52发送查询地址。 (2)从机STC89C52都接收查询地址,并与本从机地址比较,若一样则发送从机地址、采集温度十位、采集温度个位、采集温度小数位和累加和校验。 (3)主机STC89C52接收数据。 (4)主机STC89C52发送温度上限报警值十位、温度上限报警值个位。 (5)从机STC89C52接收温度上限报警值命令。 (6)主机STC89C52未查询完所有的STC89C52,则返回(1)继续查询下一个从机。 (7)通信速率9600bps,数据帧格式:一位起始位,9位数据位,一位停止位,即串行口工作于方式3。 (8)主机发送从机地址和温度上限值采用奇校验(每帧数据的第8位(即D7)为奇校验位);主机接收从机发送的匹配地址和采集到的温度值时采用累加和校验。 (9)从机机接收主机发送的从机地址和温度上限值采用奇校验(每帧数据的第8位(即D7)为奇校验位);从机发送匹配地址和采集到的温度值时采用累加和校验。 2.主机程序设计 (1)主机主程序 主机主流程图如图10-5所示。当工作状态标志为1时,进入参数调整;否则进入正常工作状态。 图10-5主机主程序流程图 (2)液晶显示功能程序 图10-6 主机液晶显示界面 主机LCD1602液晶显示从机1和从机2实时采集的温度值、时间和日期。如图10-6所示。从机1温度为28.3℃,从机2温度为28.7℃,温度上限为30℃、下限为28℃,当前日期为5月16日17点47分。 (3)DS1302实时时钟功能程序 采用DS1302实时时钟芯片进行年、月、日、时、分、秒刷新。可以通过按键调整或者无线遥控调整时间,并具有系统掉电后启用备用电池向DS1302继续供电的功能,使用户不必每次上电调整时间。 (4)AT24C02 掉电存储功能程序。用于存储用户设定的温度上限值。 (5)蜂鸣器报警功能程序。当温度大于等于用户设定的上限值时,主机蜂鸣器报警。 (6)继电器输出控制程序。当温度大于等于用户设定的上限值时,P20和P21输出低电平,通过三极管VT1和VT2驱动继电器吸合,控制电扇通风降温,同时继电器接通指示灯亮。 (7) 按键键盘程序 图10-7 按键键盘布局 按键功能如图10-7所示,当set键按下时进入调整模式,并且可以通过set键切换进入对温度上限值、时间、星期、年月日的调整状态。当进入某种调整状态时其对应值会快速闪烁,通过up键或者down键进行调整。调整完毕后按下out键保存并退出调整模式。 (8)主机通信程序 主机通信流程图如图10-8所示。在数据发送时,采用奇校验校验;在接收数据时,使用累加和校验。 图10-8 主机通信流程图 图10-9从机通信流程图 3.从机程序设计 (1)从机通信程序设计。从机通信程序流程图如图10-9所示。 (2)从机主程序设计。从机主程序流程图如图10-10所示。首先进行系统初始化,然后关中断读取DS18B20中温度值,读完温度值后,开中断,调用温度决策函数。 图10-10从机主程序流程图 五、实物照片系统实物照片如图10-11所示。 图10-11 实物照片 六、设计制作要点1.STC89C52单片机多机通信实现的原理。 因多机通信是在方式2和方式3下进行,因此SM2位主要用于方式2和方式3。当串行口以方式2或方式3接收时,如SM2 =1,则只有当接收到的第9位数据(RB8)为“1”,才将接收到的前8位数据送入SBUF,并置位RI产生中断请求;否则,将接收到的前8位数据丢弃。而当SM2=0时,则不论第9位数据为“0”还是为“1”,都将前8位数据装入SBUF中,并产生中断请求。 2. 单片机晶振频率建议选择11.0592MHz。 3. 通信芯片建议选择TI公司的SN65HVD3082ED,尽量不要选择MAX485(国产)。
单片机源程序如下: - #include<reg52.h> //调用52单片机库
- #include"1602.h" //调用LCD1602库
- #include"ds18b20.h" //调用ds18b20温度传感器
- #define uint unsigned int
- #define uchar unsigned char
- uchar hour,min,sec,count,hour1,hour2, //定义小时 分钟 秒 对应的十位个位 以及 count计数
- sec1,sec2,min1,min2;
- sbit speaker=P2^1; //定义蜂鸣器
- sbit key1=P1^0; //最低温度设置
- sbit key2=P1^1; //最高温度设置
- sbit key3=P1^2; //连接切换模式的备用开关 P33和P12口短接 用于切换模式和退出报警中断
-
- sbit keyh=P1^5; //校时
- sbit keym=P1^6; //校分
- sbit keys=P1^7; //校秒
- //定义时间按键
- uint tem; //定义采集温度
- int htem,htem2,time,time2,x,y,z,dot,dot2,maxtem,mintem; //定义温度十位数、个位数、小数,以及其三种模式的标记值
- int cnt1=1,cnt2=1,cnt3=1,cnt4=1,cnt5=1,cnt6=1,cnt7=1; //定义计数变量
- int i;
- uchar mode=0; //定义模式变量
- uchar code t3[]={" H/L=0/1: C"}; //显示调最值温度的十位
- uchar code t4[]={"O/D: C"}; //显示调最值温度的个位/小数位字模
- uchar code table2[]={
- '0','1','2','3','4','5','6','7','8','9',':'}; //显示时间的字模
- uchar code t5[]={"Max . CD"}; //显示最高温度的字模
- uchar code t6[]={"Min . CD"}; //显示最低温度的字模
- //延时函数
- void delay1(uint i)
- {
- while(i--);
- }
- //显示函数1 初始显示
- void display(uint dat,uchar add)
- {
- uchar bai,shi ,ge;
- bai=dat/100;
- shi=dat%100/10;
- ge=dat%10;
- writelcd_cmd(add);
- writelcd_dat(bai+0x30);
- writelcd_dat(shi+0x30);
- writelcd_cmd(add+3);
- writelcd_dat(ge+0x30);
- }
- void lcd_min() //定义最小温度显示
- {
- uchar l;
-
- writelcd_cmd(0x38); //初始化屏那迤粱
- delay(5);
- writelcd_cmd(0x38);
- delay(5);
- writelcd_cmd(0x38);
- writelcd_cmd(0x08);
- writelcd_cmd(0x01);
- writelcd_cmd(0x06);
- writelcd_cmd(0x0c);
- writelcd_cmd(0x80);
-
- writelcd_cmd(0xc0);
- for(l=0;l<16;l++) //依次读取t6并显示
- {
- writelcd_dat(t6[l]);
- }
-
- display(mintem,0xca);
- delay1(5000000000); //延时显示
- }
- void lcd_max() //定义最大温度显示
- {
- uchar k;
- writelcd_cmd(0x38); //初始化屏幕清屏
- delay(5);
- writelcd_cmd(0x38);
- delay(5);
- writelcd_cmd(0x38);
- writelcd_cmd(0x08);
- writelcd_cmd(0x01);
- writelcd_cmd(0x06);
- writelcd_cmd(0x0c);
- writelcd_cmd(0x80);
- writelcd_cmd(0xc0);
- for(k=0;k<16;k++) //依次读取t5并显示
- {
- writelcd_dat(t5[k]);
- }
- display(maxtem,0xca);
- delay1(5000000000);
- }
- void write_Char(unsigned char a,unsigned char b,unsigned char dat) //显示时间程序母程序 定位
- {
- if (b == 0)
- {
- writelcd_cmd(0x80 + a);
- }
- else
- {
- writelcd_cmd(0xC0 + a);
- }
- writelcd_dat(dat);
- }
-
- void display4(uchar hour1,uchar hour2,uchar min1,uchar min2,uchar sec1,uchar sec2) //显示时间子程序 取值
- {
-
- write_Char(4,0,table2[hour1]); //显示小时
- write_Char(5,0,table2[hour2]);
- write_Char(6,0,table2[0x0a]); //显示冒号
- write_Char(7,0,table2[min1]); //分钟
- write_Char(8,0,table2[min2]);
- write_Char(9,0,table2[0x0a]); //显示冒号
- write_Char(10,0,table2[sec1]); //秒
- write_Char(11,0,table2[sec2]);
-
-
- }
- //显示函数2 调最值温度低十位 在LCD上排显示
- void display2(uint dat,uchar add)
- {
- uchar bai,shi ;
- bai=dat/100;
- shi=dat%100/10;
- //ge=dat%10;
- writelcd_cmd(add);
- writelcd_dat(shi+0x30);
- writelcd_dat(bai+0x30);
- writelcd_cmd(add+3);
- //writelcd_dat(ge+0x30);
- }
- //显示函数3 调最值温度的个位/小数 在LCD下排显示
- void display3(uint dat,uchar add)
- {
- uchar ge;
- //bai=dat/100;
- //shi=dat%100/10;
- ge=dat%10;
- writelcd_cmd(add);
- //writelcd_dat(shi+0x30);
- //writelcd_dat(bai+0x30);
- //writelcd_cmd(add+3);
- writelcd_dat(ge+0x30);
- }
- //按键扫描函数
- void keyscan()
- { uchar i,j; //定义i/j
- writelcd_cmd(0x80); //显示函数表示以此显示t3字模里面的字符
- for(i=0;i<16;i++)
- {writelcd_dat(t3[i]);}
- writelcd_cmd(0xc0);
- for(j=0;j<16;j++) //显示函数表示以此显示t4字模里面的字符
- {writelcd_dat(t4[j]);}
-
- while(mode!=0) //当mode不为0时
- {
- display2(htem,0x8b); //显示调十位
- display3(time,0xca); //显示调个位小数位
-
- while(mode!=0)
- {
- switch(mode) //切换模式case语句循环 5个模式 5个循环 6个变量 初始模式为0 切换后对应地方的光标闪烁
- {
-
- case 1:writelcd_cmd(0xc0+10);writelcd_cmd(0x0f);break; //调个位
- case 2:writelcd_cmd(0x80+11);writelcd_cmd(0x0f);break; //调十位
- case 3:writelcd_cmd(0xc0+10);writelcd_cmd(0x0f);break; //调小数位
- case 4:lcd_max();break; //显示最大温度
- case 5:lcd_min();break; //显示最小温度
- default:mode=0;break;
- }
-
- if(key2==0&&mode==1) //模式为1 key2为0时 调最高位的个位
- {
- delay(50); //按键消抖
- y=0;display3(y,0x80);writelcd_cmd(0xca+11); //max one 在左上角显示0 表示调最高温度
-
- if(key2==0)
- {
- delay(10); //十个循环 对应0-9数字
- cnt1++; //计数+1
- while(key2==0);
- switch(cnt1%11) //按键到10以后清零
- {
- case 1:time=0;display3(time,0xca);writelcd_cmd(0xca+11);break; //在0xca的地方显示个位值 光标闪烁
- case 2:time=1;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 3:time=2;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 4:time=3;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 5:time=4;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 6:time=5;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 7:time=6;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 8:time=7;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 9:time=8;display3(time,0xca);writelcd_cmd(0xca+11);break;
- case 10:time=9;display3(time,0xca);writelcd_cmd(0xca+11);break;
-
- default:break;
- }
- }
- }
- if(key1==0&&mode==1) //类似 最低个位
- {
- delay(50);
- y=1;display3(y,0x80);writelcd_cmd(0xca+11); //min one 在左上角显示1 表示调最低温度
-
- if(key1==0)
- {
- delay(10);
- cnt4++;
- while(key1==0);
- switch(cnt4%11)
- {
- case 1:time2=0;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 2:time2=1;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 3:time2=2;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 4:time2=3;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 5:time2=4;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 6:time2=5;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 7:time2=6;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 8:time2=7;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 9:time2=8;display3(time2,0xca);writelcd_cmd(0xca+11);break;
- case 10:time2=9;display3(time2,0xca);writelcd_cmd(0xca+11);break;
-
- default:break;
- }
- }
- }
- if(key2==0&&mode==2) //最高十位
- {
- delay(10);
- x=00;display2(x,0x80);writelcd_cmd(0x80+11); //max ten 在左上角显示00 表示调最高
- if(key2==0)
- {
- cnt2++;
- while(key2==0);
- switch(cnt2%11)
- {
-
- case 1:htem=00;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 2:htem=10;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 3:htem=20;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 4:htem=30;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 5:htem=40;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 6:htem=50;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 7:htem=60;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 8:htem=70;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 9:htem=80;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
- case 10:htem=90;display2(htem,0x8b);writelcd_cmd(0x80+11);break;
-
-
-
- default:break;
- }
- }
-
- }
- if(key1==0&&mode==2)
- {
- delay(10);
- x=10;display2(x,0x80);writelcd_cmd(0x80+11); //min ten 在左上角显示10表示 调最低
- if(key1==0)
- {
- cnt3++;
- while(key1==0);
- switch(cnt3%11)
- {
-
- case 1:htem2=00;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 2:htem2=10;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 3:htem2=20;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 4:htem2=30;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 5:htem2=40;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 6:htem2=50;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 7:htem2=60;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 8:htem2=70;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 9:htem2=80;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
- case 10:htem2=90;display2(htem2,0x8b);writelcd_cmd(0x80+11);break;
-
-
- default:break;
- }
- }
-
- }
- if(key2==0&&mode==3) //最高小数
- {
- delay(50);
- z=00;display2(z,0x80);writelcd_cmd(0xca+11); //max dot
-
- if(key2==0)
- {
- delay(10);
- cnt5++;
- while(key2==0);
- switch(cnt5%11)
- {
- case 1:dot=0;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 2:dot=1;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 3:dot=2;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 4:dot=3;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 5:dot=4;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 6:dot=5;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 7:dot=6;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 8:dot=7;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 9:dot=8;display3(dot,0xca);writelcd_cmd(0xca+11);break;
- case 10:dot=9;display3(dot,0xca);writelcd_cmd(0xca+11);break;
-
- default:break;
- }
- }
- }
- if(key1==0&&mode==3) //最低小数
- {
- delay(50);
- z=10;display2(z,0x80);writelcd_cmd(0xca+11); //min dot
-
- if(key1==0)
- {
- delay(10);
- cnt6++;
- while(key1==0);
- switch(cnt6%11)
- {
- case 1:dot2=0;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 2:dot2=1;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 3:dot2=2;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 4:dot2=3;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 5:dot2=4;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 6:dot2=5;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 7:dot2=6;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 8:dot2=7;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 9:dot2=8;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
- case 10:dot2=9;display3(dot2,0xca);writelcd_cmd(0xca+11);break;
-
- default:break;
- }
- }
- }
-
- if(mode==4) //模式4 显示最大温度
- {
- maxtem=htem*10+time*10+dot;
- lcd_max();
- }
-
- if(mode==5) //最低温度
- {
- mintem=htem2*10+time2*10+dot2;
- lcd_min();
- }
-
- }
- }
- lcd_init(); //模式为0时执行LCD初始化函数
- }
- //中断初始化函数
- void init()
- {
- lcd_init(); //lcd初始化
- speaker=1; //蜂鸣器口默认高电平
- mode=0; //模式默认0
- htem=80; //最高温度默认80
- time=0;
- htem2=10; //最低温度默认10
- time2=0;
- dot=0;
- dot2=0;
- EA=1; //总中断开启
- EX1=1; //外部中断1允许
- ET1=1; //定时器1允许
- TMOD=0X01; //定时中断1,模式1
- IT1=1; //中断1 下降沿触发
- TH1=0Xee; //定时中断1 0.5us
- TL1=0X00;
- TR1=0; //定时中断1默认关闭
-
- count=0; //时间计数默认为0
- hour=00; //小时00
- min=00; //分钟00
- sec=00; //秒00
-
- TH0=(65536-50000)/256; //内部中断T0初始化
- TL0=(65536-50000)%256;
- TR0=1; //内部中断T0开启
- ET0=1;
-
- }
- //主函数
- void main()
- {
- P1=0xe7; //初始P1口,P14,P13为低电平 使S1 S5 S9 S14 S15 S16变为接地的普通开关 S9与P33短接 用于切换模式和退出报警中断
- init(); //中断初始化
-
-
- while(1)
- {
- if(mode!=0) //当模式不为0
- {keyscan();} //执行按键扫描 以及调温度的程序
- else if(mode==0) //模式为0时
- {
- delay(100);
- tmpchange(); //在ds18b20取数据
- tem=tmp(); //温度值获取 为整数 且小数位实际上是个位 如35.5摄氏度对应的值是355
-
- if(keyh==0) //当S14闭合时 实现小时加一
- {
- delay(5);
- if(keyh==0)
- {
- if(hour!=23) hour++; //hour=23后清零
- else hour=0;
- }
- while(!keyh); //检测松手
-
- }
-
- if(keym==0) //实现分钟加一
- {
- delay(5);
- if(keym==0)
- {
- if(min!=59) //59后清零
- {
- min++;
- }
- else
- {
- min=0;
- }
- }
- while(!keym); //松手检测
-
- }
-
- if(keys==0) //实现秒加一
- {
- ……………………
- …………限于本文篇幅 余下代码请从51黑下载附件…………
复制代码
所有资料51hei提供下载: |