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

标题: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 <reg52.h>
#include <intrins.h>
#include <string.h>
#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_time<=100) //限制最大值
                high_time=(uchar)(rout/800);
            else
                high_time=100;
            low_time=(100-high_time);
        }
    }
    else//当实际温度大于设置温度时
    {
            high_time=0;//停止加热
            low_time=100;
    }
}
void PIDBEGIN()
{
    TMOD=0x01;
    TH0=0x63;//定时100us
    TL0=0xC0;
    EA=1;//打开总中断
    ET0=1;//允许T0中断
    TR0=1;//运行定时器0
    high_time=50;//加热时间记录次数
    low_time=50;//停止加热时间记录次数
    PIDInit(&spid);//PID初始化
    spid.Proportion=10;//比例系数
    spid.Integral=8;//积分系数
    spid.Derivative=6;//微分系数
    spid.SetPoint=ConTem;//设定温度值   
}
void delay(uint n)//毫秒延时函数
{ 
  uint x,y; 
  for(x=n;x>0;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<<=1;           
      if(Data3==1)    
         dat+=1;
      while(Data3);     
    }  
    return dat;
}
void ThDHT11_receive()     
{
    uchar R_H,R_L,T_H,T_L,RH,RL,TH,TL,revise; 
    ThDHT11_start();
    if(Data3==0)
    {
        while(Data3==0);       
        DHT11_delay_us(40);  
        R_H=ThDHT11_rec_byte();  
        R_L=ThDHT11_rec_byte(); 
        T_H=ThDHT11_rec_byte();
        T_L=ThDHT11_rec_byte();
        revise=ThDHT11_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[4]=RH+RL/10*0.1+RL%10*0.01;
        table[5]=TH+TL/10*0.1+TL%10*0.01;
    }
}
void KeyScan()//按键扫描函数
{
    if(SetSure==0)//设置-确认键按下
    {
        delay(2);//消抖
        if(SetSure==0)//再次确认按下
        {
            count=0;//count值初始化为0
            time++;//time为1表示为设置键,否则为确认键
            Flag1=1;//Fla1标志位置为1,
            if(time>=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]/10<HumL)//湿度小于湿度下限值
    {
        AddHum=0;//开启加湿设备
        //Buzzer=0;//蜂鸣器报警
    }
    else if(FinData[0]/10>HumH)//湿度大于湿度上限值
    {
        DelHum=0;//开启除湿设备
        //Buzzer=0;//蜂鸣器报警
    }
    else if(FinData[1]/10<TemL||FinData[1]/10>TemH)//温度低于温度下限或高于温度上限
        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算法写了调节温度的程序,之前也只是了解,现在动手实践上了,心里还是挺兴奋的。
博观而约取,厚积而薄发。
祝大家都能够学有所成!

发布了1 篇原创文章 · 获赞 4 · 访问量 171

猜你喜欢

转载自blog.csdn.net/m0_45234752/article/details/105448690