基于51单片机的DHT11温湿度检测

基于51单片机的DHT11温湿度检测 系统提供2种工作模式在显示模式中1.显示温湿度2.超出温湿度限定的范围时蜂鸣器,LED实现报警3.加减温度时显示标志在设置模式中分别设置温湿度上下限硬件单片机AT89C52液晶LCD1602显示器温湿度传感器DHT11存储器AT24C02elseI2C总线Time0定时器目录LCD1602液晶判忙液晶初始化写入指令数据液晶显示行列的位置液晶数据转换成字符串以及显示温湿度传感器DHT11DHT11采集数据​编辑读取数字0​编辑读取数字1​编辑校验I2C总线存储器AT24C02通过I2C总线来设置AT24C02Time0定时器主函数proteus仿真图LCD1602液晶判忙LCD 1602的响应速度相对于单片机的速度来说是偏慢的。举个简单的例子把一桶油通过漏斗向一个瓶子里倒倒油的速度即流量必须维持在一定范围之内倒得太快油会从漏斗顶部溢出来这样就浪费掉了。我们通过眼睛可以判断并使油面保持在顶面以下以漏斗的额定流量来倒油这样效率最高。而对于单片机来说1602好比那个瓶子漏斗写入1602中要显示的数据好比油如果以单片机的高运行速度向1602写数据就很可能造成上面所说的溢出比如连续写入abc结果只显示出了a这是因为1602的显示芯片每次都要花时间来处理输入的ascii码数据并把它显示出来。而我们却不容易主动地去控制写入数据的速度所以1602使用忙信号就有必要了每次单片机只有检测到忙信号为0即不忙时才向1602发数据。比如要显示abc,则这样操作写a---判忙---写b---判忙---写c---判忙。这样就不会出错了。/*等待液晶准备*/ void Lcdready() { unsigned char sta; P0 0xFF; //P0在使用时要规定置1 RS 0; //数据/指令选择位 1为数据0为指令 RW 1; //读/写位 1为读0为写 do { EN 1; //使能位 sta P0;//读取状态字即把P0口的数据赋值给sta EN 0; }while(sta 0x80);//当sta最高位为0时则跳出循环若为1时则继续循环相当于单片机停止让液晶先工作 }液晶初始化写入指令数据/* 初始化 1602 液晶 */ void init1602() { WriteCmd(0x38); WriteCmd(0x0C); WriteCmd(0x06); WriteCmd(0x01); } /* 向 LCD1602 液晶写入命令cmd-待写入命令值 */ void WriteCmd(unsigned char cmd) { Lcdready(); RS 0; RW 0; P0 cmd; EN 1; EN 0; } /* 向 LCD1602 液晶写入数据dat-待写入数据 */ void WriteData(unsigned char dat) { Lcdready(); RS 1; RW 0; P0 dat; EN 1; EN 0; }液晶显示行列的位置/* 设置显示 RAM 起始地址(x,y)-对应屏幕上的字符坐标 */ void Lcdaddr(unsigned char x,unsigned char y) { unsigned char m; if(y0) m 0x00x; //第一行字符地址从 0x00 起始 else m 0x40x; //第二行字符地址从 0x40 起始 WriteCmd(m | 0x80);//设置 RAM 地址 }液晶数据转换成字符串以及显示/* 整型数转换为字符串str-字符串指针dat-待转换数返回值-字符串长度 */ unsigned char IntToString(unsigned char *str, int dat) { unsigned char i 0; unsigned char len 0; unsigned char buf[6]; if (dat 0) //如果为负数首先取绝对值并在指针上添加负号 { dat -dat; *str -; len; } do //先转换为低位在前的十进制数组 { buf[i] dat % 10; //取最低位 dat / 10; } while (dat 0); len i; //i最后的值就是有效字符的个数 while (i-- 0) //将数组值转换为ASCII码反向拷贝到接收指针上 { *str buf[i] 0; //加0其实是加ASCII码中的0x30 } *str \0; //添加字符串结束符 return len; //返回字符串长度 } /* 在液晶上显示字符串(x,y)-对应屏幕上的起始坐标str-字符串指针 */ void Lcdshow(unsigned char x,unsigned char y,unsigned char *str) { Lcdaddr(x,y); while(*str ! \0) //表示一直循环到字符串结尾 { WriteData(*str); //表示从高到低依次写入str数组的值 } }注ASCII码中数字0地址为0x30,1为0x31,2为0x32......因此在指针*strbuf[i]中为了表示数字0,1,2.......要加上0x30的地址0。温湿度传感器DHT11DHT11采集数据读取数字0读取数字1校验8bit湿度整数数据8bit湿度小数数据8bi温度整数数据8bit温度小数数据相加所得结果的末8位。#ifndef _DHT11_H_ #define _DHT11_H_ #define uchar unsigned char #define uint unsigned int sbit DHT11_DBP3^4; uchar Temp_H,Temp_L,Humi_H,Humi_L,Check_data;//温度整数位小数位湿度整数位小数位数据校验位 uchar U8FLAG; void Delay_ms(uint n); //ms延时函数 void Delay_us(uchar n); //us延时函数 uchar DHT11_receive(void); //DHT11接收数据函数 void DHT11_read(void); //DHT11读取温湿度函数 bit DHT11_Check(void); //DHT11校验函数返回1校验成功、返回0校验失败 void Delay_ms(uint n) { unsigned char j; while(n--) { for(j0;j125;j); } } void Delay_us(uchar n) { nn/2; while(--n); } /接收DHT11传回来的数据 uchar DHT11_receive(void) { uchar i,Data; for(i0;i8;i) { U8FLAG2; //延迟80ns while(!DHT11_DBU8FLAG); //当DHT11_DB由低电平0拉到高电平1时跳出延迟 Delay_us(35); Data1; if(DHT11_DB) //判断输出为1或0 Data|1; U8FLAG2; while(DHT11_DBU8FLAG); //当DHT11_DB由高电平1拉到低电平0时跳出延迟 } return Data; } /读取DHT11温湿度 void DHT11_read(void) { DHT11_DB0; Delay_ms(18); DHT11_DB1; Delay_us(40); if(!DHT11_DB) //T ! { U8FLAG2; while(!DHT11_DBU8FLAG); U8FLAG2; while(DHT11_DBU8FLAG); Humi_HDHT11_receive(); //湿度整数 Humi_LDHT11_receive(); //湿度小数 Temp_HDHT11_receive(); //温度整数 Temp_LDHT11_receive(); //温度小数 Check_dataDHT11_receive(); //校验 DHT11_DB1; //最后拉高电平 } } /校验 bit DHT11_Check(void) { if((Temp_HTemp_LHumi_HHumi_L)Check_data) //判断校验和是否正确 return 1; else return 0; } #endifI2C总线#include REGX52.H #include I2C.h void I2CStart() //I2C开始 { I2C_SCL1; I2C_SDA1; I2C_SDA0; I2C_SCL0; } void I2CStop() //I2C结束 { I2C_SCL0; I2C_SDA0; I2C_SCL1; I2C_SDA1; } bit I2CWrite(unsigned char dat) //I2C写操作dat-代写数值ack-返回应答值 { bit ack; //用来暂存应答值 unsigned char mask; //用来暂存数据 for(mask0x80;mask!0;mask1) { if((maskdat)) I2C_SDA1; else I2C_SDA0; I2C_SCL1; //拉高SCL I2C_SCL0; //再拉低SCL完成一个周期 } I2C_SDA1; //主机释放SDA I2C_SCL1; ackI2C_SDA; //读取SDA值即为应答值 I2C_SCL0; return (~ack); //因为原本的I2C是0表示应答1表示非应答所以这里取反 } unsigned char I2CReadNAK() //I2C总线读操作发送非应答信号并继续读下去返回值-读到的字节 { unsigned char mask; unsigned char dat; //暂存数据 I2C_SDA1; //确保主机释放SDA for(mask0x80;mask!0;mask1) { I2C_SCL1; if(I2C_SDA) dat|mask; else dat~mask; I2C_SCL0; } I2C_SDA1; //拉高SDA发送非应答信号 I2C_SCL1; //拉高SCL I2C_SCL0; //再拉低SCL完成非应答 return dat; //返回数据 } unsigned char I2CReadACK() //I2C总线读操作发送应答信号并不再读下去返回值-读到的字节 { unsigned char mask; unsigned dat; //暂存数据 I2C_SDA1; //确保主机释放SDA for(mask0x80;mask!0;mask1) { I2C_SCL1; if(I2C_SDA) dat|mask; else dat~mask; I2C_SCL0; } I2C_SDA0; //拉高SDA发送应答信号 I2C_SCL1; //拉高SCL I2C_SCL0; //再拉低SCL完成应答 return dat; //返回数据 }存储器AT24C02通过I2C总线来设置AT24C02/** * brief AT24C02写入一个字节 * param WordAddress 要写入字节的地址 * param Data 要写入的数据 * retval 无 */ void AT24C02_WriteByte(unsigned char WordAddress,Data) { I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_SendByte(Data); I2C_ReceiveAck(); I2C_Stop(); } /** * brief AT24C02读取一个字节 * param WordAddress 要读出字节的地址 * retval 读出的数据 */ unsigned char AT24C02_ReadByte(unsigned char WordAddress) { unsigned char Data; I2C_Start(); I2C_SendByte(AT24C02_ADDRESS); I2C_ReceiveAck(); I2C_SendByte(WordAddress); I2C_ReceiveAck(); I2C_Start(); I2C_SendByte(AT24C02_ADDRESS|0x01); I2C_ReceiveAck(); DataI2C_ReceiveByte(); I2C_SendAck(1); I2C_Stop(); return Data; }Time0定时器用来在设置模式是实现闪烁功能#include REGX52.H /** * brief 定时器0初始化1毫秒12.000MHz * param 无 * retval 无 */ void Timer0Init(void) { TMOD 0xF0; //设置定时器模式 TMOD | 0x01; //设置定时器模式 TL0 0x18; //设置定时初值 TH0 0xFC; //设置定时初值 TF0 0; //清除TF0标志 TR0 1; //定时器0开始计时 ET01; EA1; PT00; } /*定时器中断函数模板 void Timer0_Routine() interrupt 1 { static unsigned int T0Count; TL0 0x18; //设置定时初值 TH0 0xFC; //设置定时初值 T0Count; if(T0Count1000) { T0Count0; } } */主函数#include REGX52.H #include DHT11.h #include AT24C02.h #include LCD1602.h #include Timer0.h //灯、蜂鸣器、按键引脚定义 sbit led P3^7; //超限指示灯 sbit led1 P3^2; //正常指示灯 sbit buzz P2^3; //蜂鸣器 sbit key_set P1^0; //设置键 sbit key_jia P1^3; //加键 sbit key_jian P1^6; //减键 //变量定义 uchar temp_old,humi_old;//存储上一次的温度、湿度 uchar temp_up,temp_down;//存储温度上、下限值 uchar humi_up,humi_down;//存储湿度上、下限值 uchar set_f; //设置选择标记0非设置1设置湿度上限2设置湿度下限 // 3设置温度上限4设置温度下限。 uchar Flash; //闪烁 //显示固定内容 void fix_display() { LCD_ShowString(1,1,Humi:); LCD_ShowString(2,1,Temp:); LCD_ShowString(1,12,RH); LCD_ShowChar(2,12,C); } //显示当前测出的内容 void now_display() { if(humi_oldHumi_H) //判断湿度是否正在上升 { LCD_ShowString(1,15,UP); //上升标志UP Delay_ms(50); humi_oldHumi_H; //记录此时的湿度 } else if(humi_oldHumi_H) //判断湿度是否正在下降 { LCD_ShowString(1,15,DW); Delay_ms(50); humi_oldHumi_H; } if(temp_oldTemp_H) { LCD_ShowString(1,15,UP); Delay_ms(50); temp_oldTemp_H; } else if(temp_oldTemp_H) { LCD_ShowString(1,15,DW); Delay_ms(50); temp_oldTemp_H; } if(temp_upTemp_H||temp_downTemp_H||humi_upHumi_H||humi_downHumi_H) //当温湿度超过界限时LED和蜂鸣器报警 { buzz0; led0; led11; } else //LED1正常显示 { buzz1; led1; led10; } LCD_ShowNum(1,6,Humi_H,2); //显示实测的温湿度 LCD_ShowChar(1,8,.); LCD_ShowNum(1,9,Humi_L,2); LCD_ShowNum(2,6,Temp_H,2); LCD_ShowChar(2,8,.); LCD_ShowNum(2,9,Temp_L,2); } //显示设置时的内容 void set_display() { LCD_ShowString(1,1,Humi:Up); LCD_ShowString(2,1,Temp:Up); LCD_ShowString(1,10,Down); LCD_ShowString(2,10,Down); //利用定时器设置Flash闪烁值以达到闪烁效果 if(Flash1set_f1){LCD_ShowString(1,8, );} //当Flash等于1则清零等于0则显示 else{LCD_ShowNum(1,8,humi_up,2);} //当set_f标记键分别等于1234时设置不同位置 if(Flash1set_f2){LCD_ShowString(1,14, );} else{LCD_ShowNum(1,14,humi_down,2);} if(Flash1set_f3){LCD_ShowString(2,8, );} else{LCD_ShowNum(2,8,temp_up,2);} if(Flash1set_f4){LCD_ShowString(2,14, );} else{LCD_ShowNum(2,14,temp_down,2);} } //按键扫描 void scan() { if(key_set0) //当设置键摁下时 { Delay_ms(7); if(key_set0) { buzz1; //蜂鸣器关闭所有LED熄灭 led1; led11; if(set_f0) //进入设置模式 { LCD_WriteCommand(0x01); //清屏 Delay_ms(10); } set_f; //每摁一次key_f设置键set_f标记键便加1 if(set_f5) //当循环4次即摁下key_set设置键4次时把set_f标记键置0 { set_f0; AT24C02_WriteByte(0,humi_up); //把设置好的温湿度上下限写入AT24C02 AT24C02_WriteByte(1,humi_down); AT24C02_WriteByte(2,temp_up); AT24C02_WriteByte(3,temp_down); LCD_WriteCommand(0x01); //清屏 Delay_ms(10); fix_display(); //显示固定内容 } } while(!key_set); //当摁下key_sey设置键松手时完成一次操作 } if(key_jia0set_f!0) //加法键 { Delay_ms(7); if(set_f1){humi_up;} if(set_f2){humi_down;} if(set_f3){temp_up;} if(set_f4){temp_down;} } if(key_jian0set_f!0) //减法键 { Delay_ms(7); if(set_f1){humi_up--;} if(set_f2){humi_down--;} if(set_f3){temp_up--;} if(set_f4){temp_down--;} } } void main() { Timer0Init(); //定时器初始化 LCD_Init(); //LCD初始化 fix_display(); //显示固定内容 Delay_ms(100); AT24C02_WriteByte(0,75); //在AT24C02中设置温湿度上下限 AT24C02_WriteByte(0,45); AT24C02_WriteByte(0,30); AT24C02_WriteByte(0,15); humi_upAT24C02_ReadByte(0); //分别赋值给humi_uphumi_downtemp_uptemp_down humi_downAT24C02_ReadByte(1); temp_upAT24C02_ReadByte(2); temp_downAT24C02_ReadByte(3); while(1) { scan(); //按键扫描 if(set_f0) //表示并未进入设置操作 { EA0; //关闭中断 DHT11_read(); //DHT11读取温湿度值 now_display(); //显示实测内容 EA1; //打开中断 } else set_display(); //显示设置内容 } } void Timer0_Routine() interrupt 1 //中断程序 { static unsigned int T0Count; TL0 0x18; //设置初值 TH0 0xFC; T0Count; if(T0Count500) //每隔500ms,即0.5秒 { T0Count0; Flash!Flash; //Flash翻转 } }注意定时器中断在实际应用中容易出现温度的读取数据出现乱码原因在定时器的中断打断了温度的传输等待时间使数据传输不完整。因此在读取DHT11前使EA0关闭中断读取后EA1打开中断。proteus仿真图