51单片机温湿度测控系统设计

Phedra ·
更新时间:2024-09-20
· 820 次阅读

标题:51单片机温湿度测控系统设计

一,器材目录
51单片机最小系统板,DHT11温湿度传感器三个(求取平均温湿度),继电器,水泵,排气扇,加热片,PNP三极管8550(驱动继电器工作),LCD1602显示温湿度值,发光二极管。
二,实现要求
三个温湿度传感器DHT11采集温湿度显示到LCD1602上,通过四个按键实现屏幕切换设置恒温值,温度上下限,湿度上下限,K1具有设置和确定功能,K2增加,K3减少,K4返回,如果温度低于设定恒温值时,调用PID算法控制加热设备,温度高于设定恒温值时,停止加热,如果当前温湿度高于或者低于所设置的温湿度上下限时,蜂鸣器报警提示,并且开启相应的除湿或加湿设备工作,当按键设定完恒温值,温湿度上下限时,可以随时查看所设定值的大小。
三,PROTEUS仿真电路图
由于在仿真中找不到水泵,排气扇,加热片这些器件,直接用发光二极管替代!
在这里插入图片描述四,DHT11温湿度传感器接线图
DHT11 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由单总线完成。单总线通常要求外接一个约 5.1kΩ 的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结极,只有主机呼叫从机时,从机才能应答,因此主机访问器件都必须严格遵循单总线序列,如果出现序列混乱,器件将不响应主机。
在这里插入图片描述五,DHT11数据采集分析
1.数据总时序
用户主机(MCU)发送一次开始信号后,DHT11 从低功耗模式转换到高速模式,待主机开始信号结束后,DHT11 发送响应信号,送出 40bit 的数据,幵触发一次信采集。
在这里插入图片描述2.主机发送起始信号
单片机连接DHT11的DATA引脚的I/O口输出低电平,且低电平保持时间不能小于 18ms,然后等待 DHT11 作出应答信号。
在这里插入图片描述3.检测从机应答信号
DHT11 的 DATA 引脚检测到外部信号有低电平时, 等待外部信号低电平结束, 延迟后 DHT11 的 DATA引脚处于输出状态,输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接收数据。
在这里插入图片描述4.接收数据
(1)数据判定规则
位数据“0”的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加 70微秒的高电平。
接收数据时可以先等待低电平过去,即等待数据线拉高,再延时60us,因为60us大于28us且小于70us,再检测此时数据线是否为高,如果为高,则数据判定为1,否则为0。
在这里插入图片描述(2)数据格式
一次传送 40 位数据,高位先出。
8bit 湿度整数数据 + 8bit 湿度小数数据+8bit 温度整数数据 + 8bit 温度小数数据+8bit 校验位。
(3)数据校正
判断“8bit 湿度整数数据 + 8bit 湿度小数数据+8bit 温度整数数据 + 8bit 温度小数数据”的结果是否等于8bit 校验位。如果等于则数据接收正确,否则应该放弃这一次的数据,重新接收。
六,LCD1602操作时序
(1),RS,RW操作时序
在这里插入图片描述在这里插入图片描述(2),指令集
LCD_1602 初始化指令小结:
0x38 设置 162 显示,57 点阵,8 位数据接口
0x01 清屏
0x0F 开显示,显示光标,光标闪烁
0x08 只开显示
0x0e 开显示,显示光标,光标不闪烁
0x0c 开显示,不显示光标
0x06 地址加 1,当写入数据的时候光标右移
0x02 地址计数器 AC=0;(此时地址为 0x80) 光标归原点,但
是 DDRAM 中断内容不变
七,程序代码

#include #include #include #define uchar unsigned char #define uint unsigned int sbit RS=P2^7;//LCD1602 RS口 sbit RW=P2^6;//LCD1602 RW口 sbit EN=P2^5;//LCD1602 EN口 sbit AddHeat=P2^2;//加热控制口 sbit AddHum=P2^3;//加湿控制口 sbit DelHum=P2^4;//除湿控制口 sbit Data1=P1^0;//1号DHT11 数据输出口 sbit Data2=P1^1;//2号DHT11 数据输出口 sbit Data3=P1^2;//3号DHT11 数据输出口 sbit Buzzer=P1^3;//蜂鸣器控制口 sbit SetSure=P1^4;//设置和确认键 sbit Add=P1^5;//数据加 sbit Sub=P1^6;//数据减 sbit Back=P1^7;//返回键 uchar table[6];//存入三个传感器的温湿度值 uint FinData[2];//温湿度平均值 uchar table1[]={" NowTem: "}; uchar table2[]={" NowHum: "}; uchar high_time,low_time;//定义调节时间变量 uchar ConTem=0;//温度恒定值 uchar TemH=0;//温度上限值 uchar TemL=0;//温度下限值 uchar HumH=0;//湿度上限值 uchar HumL=0;//湿度下限值 uchar count=0,time=0;//设定值变量,记录按键次数变量 uchar count1=0;//定时器中使用 uchar Flag1=0,Flag2=1;//标志位 void FirDHT11_receive();//声明函数 void SecDHT11_receive(); void ThDHT11_receive(); struct PID { uint SetPoint;// 设定目标 uint Proportion;// 比例常数 uint Integral;// 积分常数 uint Derivative;// 微分常数 uint LastError;//当前误差 uint PrevError;//前次误差 uint SumError;//误差和 }; struct PID spid;//定义PID变量 unsigned int rout;//累计误差值 void PIDInit(struct PID*pp)//PID初始化 { memset(pp,0,sizeof(struct PID)); //PID参数初始化全部设置为0 } uint PIDCalc(struct PID*pp,uint NextPoint)//PID误差计算 { uint dError,Error ; Error=pp->SetPoint-NextPoint;// 偏差 pp->SumError+=Error;// 积分 dError=pp->LastError-pp->PrevError;//当前微分 pp->PrevError=pp->LastError ; pp->LastError=Error ; return(pp->Proportion*Error+pp->Integral*pp->SumError+pp->Derivative*dError);//比例积分项微分项 } void compare_temper()//温度比较 { uchar i ; float temper=FinData[1]/10;//当前温度值 float set_temper=ConTem;//设定温度值 if(set_temper>temper) { if(set_temper-temper>1) { high_time=100 ; //大于1°不进行PID运算 low_time=0 ; } else { //在1°范围内进行PID运算 for(i=0;i<10;i++) { FirDHT11_receive(); SecDHT11_receive(); ThDHT11_receive(); FinData[1]=(table[1]+table[3]+table[5])/3*10; rout=PIDCalc(&spid,FinData[1]/10); //执行PID运算 } if(high_time0;x--) for(y=110;y>0;y--); } void LCD_WriteCom(uchar com)//LCD1602写指令函数 { RS=0; P0=com; delay(5); EN=1; delay(5); EN=0; } void LCD_WriteData(uchar dat)//LCD1602写数据函数 { RS=1; P0=dat; delay(5); EN=1; delay(5); EN=0; } void LCD_Init()//LCD1602初始化 { EN=0; RW=0; LCD_WriteCom(0x38); LCD_WriteCom(0x0c); LCD_WriteCom(0x06); LCD_WriteCom(0x01); } 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 FirDHT11_start()//1号DHT11起始信号 { Data1=1;//数据口先拉高 DHT11_delay_us(2);//短暂延时 Data1=0;//数据口拉低 DHT11_delay_ms(30); //延时18ms以上 Data1=1;//重新拉高 DHT11_delay_us(30);//延时 } uchar FirDHT11_rec_byte()//接收一个字节 { uchar i,dat=0; for(i=0;i<8;i++) { while(!Data1);//等待50us低电平过去 DHT11_delay_us(8);//延时60us左右 dat<<=1; if(Data1==1)//此时数据口仍然为1,则数据为1 dat+=1; while(Data1);//等待数据口重新拉低 } return dat;//返回8位数据 } void FirDHT11_receive()//数据接收函数 { uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; FirDHT11_start();//发送起始信号 if(Data1==0)//如果为0,则DHT11应答 { while(Data1==0);//等待80us低电平过去 DHT11_delay_us(40); //等待80us高电平过去 R_H=FirDHT11_rec_byte();//湿度整数8位 R_L=FirDHT11_rec_byte(); //湿度小数8位 T_H=FirDHT11_rec_byte(); //温度整数8位 T_L=FirDHT11_rec_byte(); //温度小数8位 revise=FirDHT11_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; } table[0]=RH+RL/10*0.1+RL%10*0.01;//温湿度存入数组中 table[1]=TH+TL/10*0.1+TL%10*0.01; } } void SecDHT11_start()//2号DHT11原理与1号相同 { Data2=1; DHT11_delay_us(2); Data2=0; DHT11_delay_ms(30); Data2=1; DHT11_delay_us(30); } uchar SecDHT11_rec_byte() { uchar i,dat=0; for(i=0;i<8;i++) { while(!Data2); DHT11_delay_us(8); dat<<=1; if(Data2==1) dat+=1; while(Data2); } return dat; } void SecDHT11_receive() { uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; SecDHT11_start(); if(Data2==0) { while(Data2==0); DHT11_delay_us(40); R_H=SecDHT11_rec_byte(); R_L=SecDHT11_rec_byte(); T_H=SecDHT11_rec_byte(); T_L=SecDHT11_rec_byte(); revise=SecDHT11_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; } table[2]=RH+RL/10*0.1+RL%10*0.01; table[3]=TH+TL/10*0.1+TL%10*0.01; } } void ThDHT11_start()//3号DHT11原理与1号相同 { Data3=1; DHT11_delay_us(2); Data3=0; DHT11_delay_ms(30); Data3=1; DHT11_delay_us(30); } uchar ThDHT11_rec_byte() { uchar i,dat=0; for(i=0;i<8;i++) { while(!Data3); DHT11_delay_us(8); dat<=2) Flag2++;//Flag2表示所要设置的对象 } while(SetSure==0);//等待按键松开 } if(Add==0)//按键加按下 { delay(2);//延时消抖 if(Add==0)//再次确定按键按下 { count++;//数值加1 if(Flag2==1) ConTem=count;//Flag2为1,表示设置对象为恒温值 else if(Flag2==2) TemH=count;//Flag2为2,表示设置对象为温度上限 else if(Flag2==3) TemL=count;//Flag2为3,表示设置对象为温度下限 else if(Flag2==4) HumH=count;//Flag2为4,表示设置对象为湿度上限 else if(Flag2==5) HumL=count;//Flag2为5,表示设置对象为湿度下限 while(Add==0);//等待按键松开 } } if(Sub==0)//按键减按下 { delay(2);//延时消抖 if(Sub==0)//再次确认按下 { count--;//数据减1 if(Flag2==1) ConTem=count;//Flag2为1,表示设置对象为恒温值 else if(Flag2==2) TemH=count;//Flag2为2,表示设置对象为温度上限 else if(Flag2==3) TemL=count;//Flag2为3,表示设置对象为温度下限 else if(Flag2==4) HumH=count;//Flag2为4,表示设置对象为湿度上限 else if(Flag2==5) HumL=count;//Flag2为5,表示设置对象为湿度下限 while(Sub==0);//等待按键松开 } } if(Back==0)//返回按键按下 { delay(2);//延时消抖 if(Back==0)//再次确定按键按下 { Flag1=0;//跳出屏幕切换 Flag2=1;//Flag2重新初始为1 time=0;//time 初始为0 while(Back==0);//等待按键松开 } } } void SwitchDisplay()//切换屏幕显示 { LCD_WriteCom(0x80);//第一行显示 LCD_WriteData('C');//显示字符C:,表示恒温 LCD_WriteData(':'); LCD_WriteData(ConTem/10+0x30);//显示恒温十位 LCD_WriteData(ConTem%10+0x30);//显示恒温个位 LCD_WriteData(' ');//显示空格 LCD_WriteData('T');//显示温度上限字符TH: LCD_WriteData('H'); LCD_WriteData(':'); LCD_WriteData(TemH/10+0x30);//显示温度上限十位 LCD_WriteData(TemH%10+0x30);//显示温度上限个位 LCD_WriteData(' ');//显示空格 LCD_WriteData('T');//显示温度下限字符TL: LCD_WriteData('L'); LCD_WriteData(':'); LCD_WriteData(TemL/10+0x30);//显示温度下限十位 LCD_WriteData(TemL%10+0x30);//显示温度下限个位 LCD_WriteCom(0x80+0x40);//第二行显示 LCD_WriteData('R');//显示湿度上限字符RH: LCD_WriteData('H'); LCD_WriteData(':'); LCD_WriteData(HumH/10+0x30);//显示湿度上限十位 LCD_WriteData(HumH%10+0x30);//显示湿度上限个位 LCD_WriteData(' ');//显示空格 LCD_WriteData('R');//显示湿度下限字符RL: LCD_WriteData('L'); LCD_WriteData(':'); LCD_WriteData(HumL/10+0x30);//显示湿度下限十位 LCD_WriteData(HumL%10+0x30);//显示湿度下限个位 LCD_WriteData(' ');//显示空格 LCD_WriteData('F');//显示字符Flag2: LCD_WriteData('g'); LCD_WriteData(':'); LCD_WriteData(Flag2%10+0x30);//显示标志位Flag2 } void Lcd_DisPlay()//正常显示 { uchar i; FirDHT11_receive();//1号DHT11接收数据 SecDHT11_receive();//2号DHT11接收数据 ThDHT11_receive();//3号DHT11接收数据 FinData[0]=(table[0]+table[2]+table[4])/3*10;//平均湿度 FinData[1]=(table[1]+table[3]+table[5])/3*10;//平均温度 LCD_WriteCom(0x80);//以下为显示程序 for(i=0;i<9;i++) LCD_WriteData(table1[i]); LCD_WriteData(FinData[1]/100+0x30); LCD_WriteData(FinData[1]/10%10+0x30); LCD_WriteData('.'); LCD_WriteData(FinData[1]%10+0x30); LCD_WriteData('^'); LCD_WriteData('C'); LCD_WriteData(' '); LCD_WriteCom(0x80+0x40); for(i=0;i<9;i++) LCD_WriteData(table2[i]); LCD_WriteData(FinData[0]/100+0x30); LCD_WriteData(FinData[0]/10%10+0x30); LCD_WriteData('.'); LCD_WriteData(FinData[0]%10+0x30); LCD_WriteData('%'); LCD_WriteData(' '); LCD_WriteData(' '); } void AddDelHum_Alarm()//加湿除湿和报警函数 { if(FinData[0]/10HumH)//湿度大于湿度上限值 { DelHum=0;//开启除湿设备 //Buzzer=0;//蜂鸣器报警 } else if(FinData[1]/10TemH)//温度低于温度下限或高于温度上限 Buzzer=0;//蜂鸣器报警 else { AddHum=DelHum=1;//关闭设备 //Buzzer=1;//关闭蜂鸣器 } } int main() { AddHeat=AddHum=DelHum=1;//初始化关闭设备 Buzzer=1;//关闭蜂鸣器 LCD_Init();//LCD1602初始化 PIDBEGIN();//开启PID while(1) { KeyScan();//按键扫描 Lcd_DisPlay();//LCD1602显示温湿度 compare_temper();//温度比较 AddDelHum_Alarm();//湿度调节和报警 while(Flag1==1)//进入分屏模式 { KeyScan();//按键扫描 SwitchDisplay();//分屏显示 } } } void Timer0() interrupt 1 { count1++; if(count1<=(high_time))//加热设备开启时间 AddHeat=0; else if(count1<=100)//加热设备关闭时间 { AddHeat=1; } else count1=0 ; TH0=0x63;//定时器重新赋值 TL0=0xC0; }

八,现象结果
(1)初始上电显示平均温湿度值
在这里插入图片描述(2)分屏设置恒温值,温湿度上下限
在这里插入图片描述C代表恒温值,TH代表温度上限,TL代表温度下限,RH代表湿度上限,RL代表湿度下限,Fg为1代表设置恒温值大小,Fg为2代表设置温度上限值大小,Fg为3代表设置温度下限值大小,Fg为4代表设置湿度上限值大小,Fg为5代表设置湿度下限值大小。
其他具体的一些调节现象大家可以自行演示一下哈哈。
九,总结心得
疫情在家状态自然没有在学校好了,但也写了一些程序了嘿嘿,程序方面有了一定提升,越努力越幸运,真的很感谢老师们,没有他们的教导,实践能力上不会有当前这么大的一个提升,很感激。
再有一个就是第一次用PID算法写了调节温度的程序,之前也只是了解,现在动手实践上了,心里还是挺兴奋的。
博观而约取,厚积而薄发。
祝大家都能够学有所成!


作者:阿南9527



温湿度 51单片机 系统设计 系统 单片机

需要 登录 后方可回复, 如果你还没有账号请 注册新账号