草庐IT

DHT11温湿度传感器初识

Love小羽 2024-03-22 原文

目录

一、产品概述

1、接线方式

2、特点

3、数据传送逻辑

二、发送时序检测模块是否存在

1、C51单片机(主机)时序分析

2、编写代码检测模块是否存在

3、读取DHT11数据的时序分析

三、温湿度通过串口传到PC显示

四、温湿度检测小系统——使数据显示在LCD1602液晶板上


一、产品概述

DHT11温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器,应用领域:暖通空调、汽车、消费品、气象站、温度调节器、除湿器、家电、医疗、自动控制等

1、接线方式

VCC:接供电的正极

GND:接地

DAT:接数据

2、特点

相对湿度和温度的测量

全部校准、数字输出

长期稳定性

超长的信号传输距离:20米

4引脚安装:可以买封装好的

完全互换:直接出结果,不用转化

3、数据传送逻辑

只有一根数据线DATA,上官一号发送序列指令给DHT11模块,模块一次完整的数据传输为40bit高位先出

40bit的数据格式

8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验合

二、发送时序检测模块是否存在

时序逻辑分析

对于主机来说:发送时序的开始在a点,结束在d点

1、C51单片机(主机)时序分析

a:dht=1;
b:dht=0;
b、c之间延时30ms;
c:dht=1;
开始读DHT模块有无信号,在40-100μs之间读,比如在60μs的时候读,如果在60μs的时候是低电平,说明模块存在

那怎么看模块是否有回应呢,请看上图中的de段,C51单片机发送完时序信号是处于高电平的状态,当DHT这边有信号过来时,会把信号从高电平状态拉到低电平状态,所以当de=0时,说明DHT模块有数据显示了

那对于单片机来说怎么获取到这个低电平状态呢,那就得去读取de段的数据了,读上面的引脚是否为低电平

从c点到e点共有两种情况:

第一种:cd之间的延时最短时间是20μs,de之间的延时时间是80μs,那么从c点到e点,所需要的延时时间就是20-100μs

第二种:cd之间的延时最长时间是40μs,de之间的延时时间是80μs,那么从c点到e点,所需要的延时时间就是40-120μs

综上述情况, 我们要读取它们重合的时间,才能把以上两点都包括在内,重合的时间是40-100μs

2、编写代码检测模块是否存在

#include "reg52.h"
#include "intrins.h"

sbit dht    = P3^3;
sbit ledOne = P3^7;

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;
	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void Delay60us()		//@11.0592MHz
{
	unsigned char i;
	i = 25;
	while (--i);
}

void check_DHT(){

	//a:dht=1;
	dht = 1;
	//b:dht=0;
	dht = 0;
	//b、c之间延时30ms;
	Delay30ms();
	//c:dht=1;
	dht = 1;
	//开始读DHT模块有无信号,在40-100μs之间读,比如在60μs的时候读,如果在60μs的时候是低电平,说明模块存在
	Delay60us();
	if(dht == 0){
		ledOne = 0;     //亮灯,说明DHT模块存在
	}
}

void main()
{
	ledOne = 1;      //灭灯
	Delay1000ms();   //上电后稳定DHT11的供电
	Delay1000ms();
	check_DHT();     //看模块是否存在
	while(1);        //一直循环,不让main退出	
}

执行结果:

当单片机接温湿度传感器的时候,单片机的P3^7小灯就会亮,单片机不接温湿度传感器的时候,小灯就不亮

3、读取DHT11数据的时序分析

a:dht=1;
b:dht=0;
b、c之间延时30ms;
c:dht=1;

由于c点到d点之间有个20-40μs的延时时间,所以不太好找d点的位置,那么怎么找d点的位置呢

采用卡点的方法:

卡d点:while(dht);这时dht=1,当dht=1时,说明一直是高电平信号,当dht不等于1时,条件不满足时,说明信号从高电平下降到低电平了,也就是说找到d点了

卡e点:while(!dht); 这时dht=0,一直处于低电平状态,当dht不等于0时,条件不满足,说明信号从低电平上升到高电平了,也就是找到e点了

卡f点:while(dht);这时dht=1,当dht=1时,说明一直是高电平信号,当dht不等于1时,条件不满足时,说明信号从高电平下降到低电平了,也就是说找到f点了

卡g点:while(!dht); 这时dht=0,一直处于低电平状态,当dht不等于0时,条件不满足,说明信号从低电平上升到高电平了,也就是找到g点了

DHT11传输的有效数据都是高电平,只是持续的时间不一样

表示0时,高电平持续的时间是26-28μs

表示1时,高电平持续的时间是70μs

怎么读取DHT11传送的数据

从g点以后延时一段时间去读数据,比如延时50μs后,如果读到的信号是低电平,说明是数据是0,读到是信号是高电平,说明数据是1,然后读40bit,也是就是读40次,40次分5轮来读,每轮读8次(8bit=1个char)8次形成一个数据

数字0信号表示方法

 数字1信号表示方法

三、温湿度通过串口传到PC显示

1、环境准备:C51单片机连接DHT11温湿度传感器;VCC-5V、GND-GND、DAT-接/发数据引脚

2、编写代码

#include "reg52.h"
#include "intrins.h"

sbit dht    = P3^3;
sbit ledOne = P3^7;
sfr AUXR = 0x8E;
char datas[5];

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;   //降低时钟对外界的辐射
	SCON = 0x40;   //串行口寄存器工作模式选择方式1,RNE=0,为串行禁止接收状态
	
	TMOD &= 0x0F;  //定时器1工作方式位8位自动重装
	TMOD |= 0x20;
	
	TH1 = 0xFD; 
	TL1 = 0xFD;   //9600波特率的初值
	TR1 = 1;      //启动定时器
	
}

void sendByte(char data_msg){  //发送字节
	SBUF = data_msg;
	while(!TI);   //智能延时,靠硬件延时
	TI = 0;
}

void sendString(char* str){   //发送字符串

	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;
	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;
	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}

void DHT11_Start(){

	dht = 1;
	dht = 0;
	Delay30ms();
	dht = 1;
	while(dht);  //卡d点
	while(!dht); //卡e点
	while(dht);  //卡f点
	
}

void Delay40us()		//@11.0592MHz
{
	unsigned char i;
	_nop_();
	i = 15;
	while (--i);
}

void Read_Data_From_DHT(){

	int i;
	int j;
	char tmp;           //用来进行移位,用来获得最低位
	char flag;          //标志位
	DHT11_Start();
	for(i=0;i<5;i++){

		for(j=0;j<8;j++){
			while(!dht);  //卡g点
			Delay40us();  //延时40us
			if(dht == 1){
				flag = 1;
				while(dht);
			}else{    
				flag = 0;
			}
			tmp = tmp << 1;
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

void main()
{
	ledOne = 1;      //灭灯
	UartInit();      //初始化一个串口
	Delay1000ms();   //上电后稳定DHT11的供电
	Delay1000ms();
	
	while(1){
		Delay1000ms();
		Read_Data_From_DHT();          //获取DHT数据
		
		sendString("H:");              //发送字符串
		sendByte(datas[0]/10 + 0x30);  //发送第1个bit字节,获取湿度的整数位
		sendByte(datas[0]%10 + 0x30);
		sendByte('.');
		sendByte(datas[1]/10 + 0x30);  //发送第2个bit字节,获取湿度的小数位
		sendByte(datas[1]%10 + 0x30);
		sendString("\r\n");            //自动换行
		
		sendString("T:");
		sendByte(datas[2]/10 + 0x30);  //发送第3个bit字节,获取温度的整数位
		sendByte(datas[2]%10 + 0x30);
		sendByte('.');
		sendByte(datas[3]/10 + 0x30);  //发送第4个bit字节,获取温度的小数位
		sendByte(datas[3]%10 + 0x30);
		sendString("\r\n");            //自动换行
		
	}    	
}

3、执行结果:

四、温湿度检测小系统——使数据显示在LCD1602液晶板上

1、环境准备:C51单片机连接DHT11温湿度传感器和LCD1602液晶板

下面的链接里有C51单片机和LCD1602液晶板的连接方式

初识LCD1602及编程实现字符显示_Love小羽的博客-CSDN博客

2、编写代码

#include "reg52.h"
#include "intrins.h"
#define databuffer P0 //定义8位数据线,P0端口组

sbit dht    = P3^3;
sbit ledOne = P3^7;
sfr AUXR = 0x8E;
sbit RS = P1^0;
sbit RW = P1^1;
sbit EN = P1^4;
sbit fengshan = P1^6;  //继电器引脚

char datas[5];
char temp[8];   //定义一个温度类型
char huma[8];   //定义一个湿度类型


void check_busy(){
    
    char tmp = 0x80;
    databuffer = 0x80;
    
    while(tmp & 0x80){
        RS = 0;
        RW = 1;
        
        EN = 0;
        _nop_();
        EN = 1;
        _nop_();
        _nop_();
        tmp = databuffer;  //读取数据
        _nop_();
        EN = 0;
        _nop_();
    }
}

void Write_Cmd_Func(char cmd){
    
    check_busy();
    RS = 0;  //指令寄存器,接收地址
    RW = 0;
    EN = 0;
    _nop_();
    databuffer = cmd;   //发送指令
    _nop_();
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();
}

void Write_Data_Func(char dataShow){
    
    check_busy();
    RS = 1;  //数据寄存器,接收内容
    RW = 0;
    EN = 0;
    _nop_();
    databuffer = dataShow;   //发送数据
    _nop_();
    EN = 1;
    _nop_();
    _nop_();
    EN = 0;
    _nop_();     
}

void Delay15ms()        //@11.0592MHz
{
    unsigned char i, j;
    i = 27;
    j = 226;
    do
    {
        while (--j);
    } while (--i);
}

void Delay5ms()        //@11.0592MHz
{
    unsigned char i, j;
    i = 9;
    j = 244;
    do
    {
        while (--j);
    } while (--i);
}

void UartInit(void)		//9600bps@11.0592MHz
{
	AUXR = 0x01;   //降低时钟对外界的辐射
	SCON = 0x40;   //串行口寄存器工作模式选择方式1,RNE=0,为串行禁止接收状态
	
	TMOD &= 0x0F;  //定时器1工作方式位8位自动重装
	TMOD |= 0x20;
	
	TH1 = 0xFD; 
	TL1 = 0xFD;   //9600波特率的初值
	TR1 = 1;      //启动定时器
	
}

void sendByte(char data_msg){  //发送字节
	SBUF = data_msg;
	while(!TI);   //智能延时,靠硬件延时
	TI = 0;
}

void sendString(char* str){   //发送字符串

	while(*str != '\0'){
		sendByte(*str);
		str++;
	}
}

void Delay1000ms()		//@11.0592MHz
{
	unsigned char i, j, k;

	_nop_();
	i = 8;
	j = 1;
	k = 243;
	do
	{
		do
		{
			while (--k);
		} while (--j);
	} while (--i);
}

void Delay30ms()		//@11.0592MHz
{
	unsigned char i, j;

	i = 54;
	j = 199;
	do
	{
		while (--j);
	} while (--i);
}


void DHT11_Start(){

	dht = 1;
	dht = 0;
	Delay30ms();
	dht = 1;
	while(dht);  //卡d点
	while(!dht); //卡e点
	while(dht);  //卡f点
	
}

void Delay40us()		//@11.0592MHz
{
	unsigned char i;

	_nop_();
	i = 15;
	while (--i);
}

void Read_Data_From_DHT(){

	int i;
	int j;
	char tmp;           //用来进行移位,用来获得最低位
	char flag;          //标志位
	DHT11_Start();
	for(i=0;i<5;i++){

		for(j=0;j<8;j++){
			while(!dht);  //卡g点
			Delay40us();  //延时40us
			if(dht == 1){
				flag = 1;
				while(dht);
			}else{    
				flag = 0;
			}
			tmp = tmp << 1;
			tmp |= flag;
		}
		datas[i] = tmp;
	}
}

void LCD1602_INIT(){

//(1)延时 15ms 
    Delay15ms();
//(2)写指令 38H(不检测忙信号) 
    Write_Cmd_Func(0x38);
//(3)延时 5ms 
    Delay5ms();
//(4)以后每次写指令,读/写数据操作均需要检测忙信号 
    check_busy();
//(5)写指令 38H:显示模式设置 
    Write_Cmd_Func(0x38);
//(6)写指令 08H:显示关闭 
    Write_Cmd_Func(0x08);
//(7)写指令 01H:显示清屏 
    Write_Cmd_Func(0x01);
//(8)写指令 06H:显示光标移动设置 
    Write_Cmd_Func(0x06);
//(9)写指令 0CH:显示开及光标设置
    Write_Cmd_Func(0x0c);
}

void LCD1602_showLine(char row,char col,char *string){

	switch(row){
		
		case 1:
			Write_Cmd_Func(0x80+col);    //选择要显示的地址
			while(*string){
				Write_Data_Func(*string);  //发送要显示内容
				string++;
			}
			break;
			
		case 2:
			Write_Cmd_Func(0x80+0x40+col);
			while(*string){
				Write_Data_Func(*string);
				string++;
			}
			break;
	}
}

void Build_Datas(){

	huma[0] = 'H';                 //温度
	huma[1] = datas[0]/10 + 0x30;
	huma[2] = datas[0]%10 + 0x30;
	huma[3] = '.';
	huma[4] = datas[1]/10 + 0x30;
	huma[5] = datas[1]%10 + 0x30;
	huma[6] = '%';
	huma[7] = '\0';
	
	temp[0] = 'T';                  //湿度
	temp[1] = datas[2]/10 + 0x30;
	temp[2] = datas[2]%10 + 0x30;
	temp[3] = '.';
	temp[4] = datas[3]/10 + 0x30;
	temp[5] = datas[3]%10 + 0x30;
	temp[6] = 'C';
	temp[7] = '\0';
	

}


void main()
{
	
	Delay1000ms();
	UartInit();      //初始化一个串口
	LCD1602_INIT();  //初始化LCD1602液晶显示
	Delay1000ms();   //上电后稳定DHT11的供电
	Delay1000ms();
	ledOne = 0;      
	
	while(1){
		Delay1000ms();
		Read_Data_From_DHT();           //获取DHT数据
		if(datas[2] > 29 ){
			fengshan = 0;
		}
		Build_Datas();
		sendString(huma);               //发送湿度字符串
		sendString("\r\n");
		sendString(temp);               //发送温度字符串
		sendString("\r\n");
		LCD1602_showLine(1,3,temp);		  //第一行显示湿度
		LCD1602_showLine(2,3,huma);		  //第二行显示温度
	}    
}

执行结果:

有关DHT11温湿度传感器初识的更多相关文章

  1. ruby - 安装libv8(3.11.8.13)出错,Bundler无法继续 - 2

    运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin

  2. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  3. ruby - ri 有空文件 – Ubuntu 11.10, Ruby 1.9 - 2

    我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da

  4. ruby - rails 3.2.2(或 3.2.1)+ Postgresql 9.1.3 + Ubuntu 11.10 连接错误 - 2

    我正在使用PostgreSQL9.1.3(x86_64-pc-linux-gnu上的PostgreSQL9.1.3,由gcc-4.6.real(Ubuntu/Linaro4.6.1-9ubuntu3)4.6.1,64位编译)和在ubuntu11.10上运行3.2.2或3.2.1。现在,我可以使用以下命令连接PostgreSQLsupostgres输入密码我可以看到postgres=#我将以下详细信息放在我的config/database.yml中并执行“railsdb”,它工作正常。开发:adapter:postgresqlencoding:utf8reconnect:falsedat

  5. 玩以太坊链上项目的必备技能(初识智能合约语言-Solidity之旅一) - 2

    前面一篇关于智能合约翻译文讲到了,是一种计算机程序,既然是程序,那就可以使用程序语言去编写智能合约了。而若想玩区块链上的项目,大部分区块链项目都是开源的,能看得懂智能合约代码,或找出其中的漏洞,那么,学习Solidity这门高级的智能合约语言是有必要的,当然,这都得在公链``````以太坊上,毕竟国内的联盟链有些是不兼容Solidity。Solidity是一种面向对象的高级语言,用于实现智能合约。智能合约是管理以太坊状态下的账户行为的程序。Solidity是运行在以太坊(Ethereum)虚拟机(EVM)上,其语法受到了c++、python、javascript影响。Solidity是静态类型

  6. ruby-on-rails - Rails 2.3.11 DateTime BigDecimal 精度 - 2

    我目前有一个运行Ruby1.8.7和Rails2.3.2的RubyonRails项目我有一些从数据库中读取数据的单元测试,特别是两个连续项目的日期时间列,这两个项目应该相隔24小时。在一项测试中,我将项目2的日期时间设置为与项目1的日期时间相同。当我执行断言以确保两个值相等时,测试在rails2.3.2下工作正常。当我升级到rails2.3.11时,测试失败显示两次之间的差异将关闭并出现以下错误:expectedbutwas.这两个版本的rails中似乎存在浮点转换问题。如何解决float问题? 最佳答案 这也发生在我身上,我最终这

  7. Win10 / 11新电脑最简单跳过联网激活和使用本地账户登录方法 - 2

    跳过联网激活:OOBE界面直接按Ctrl+Shift+F3进入审核模式。这样就可以直接进入系统进行一些硬件测试等,而不用联网激活导致新机无法退货。需要注意的是,在审核模式下进行的一些操作都会保留,并不会在退出后自动还原!安装的软件在正常开机进系统后还会看见!如果电脑确实没连互联网又不想强行跳过OOBE(网上很多教程会叫你直接结束OOBE进程,但这是不推荐的,因为一些厂商自带优化程序和系统初始化设置在后面都会应用,对于笔记本跳过的话你会发现驱动和内置应用都没有装上。其实这部分脚本就在系统盘的Recovery隐藏文件夹下),可以参考以下方式:https://www.landiannews.com/

  8. 【Linux】初识Linux --指令Ⅰ - 2

    Halo,这里是Ppeua。平时主要更新C语言,C++,数据结构算法,Linux…感兴趣就关注我吧!你定不会失望。目录1.ls显示当前目录下的文件内内容2.pwd-显示用户当前所在的目录3.cd-改变工作目录。将当前工作目录改变到指定的目录下1.cd-回到上一次待的工作空间2.cd..返回上一层目录1.相对路径:cd../aurora2.绝对路径:cd/home/aurora/lesson1/aurora3.cd~进入用户家目录4.cd/进入root目录4.mkdir-新建目录5.rmdir/rm-删除1.rmdir删除空文件夹2.rm删除1.rm-f2.rm-i3.rm-r1.ls显示当前目

  9. ruby-on-rails - 在 El Capitan 上安装 Rails 时出现 -lgmp 错误的库未找到(Mac OS 10.11.1 (15B42)) - 2

    在使用Rubyv2.2.2的ElCapitan(MacOSX10.11.1)上安装Rails时,出现以下错误:ERROR:Errorinstallingnokogiri:ERROR:Failedtobuildgemnativeextension./Users/jon/.rvm/rubies/ruby-2.2.2/bin/ruby-r./siteconf20151117-26799-ux15fd.rbextconf.rb--use-system-librariescheckingiftheCcompileraccepts...***extconf.rbfailed***Couldnotc

  10. ruby-on-rails - Rails 3.2.11 突然需要重启到 'acknowledge' Controller 有什么变化吗? - 2

    标题说明了一切。请注意,这不是模型或初始值设定项的更改。我可以删除Controller中的一个实例变量(例如,@user),然后重新加载一个View,它会工作-直到我重新启动服务器,在这种情况下它会提示变量为nil。我正常工作,然后切换到一组完全不同的Controller和View上工作,现在它无缘无故地发生了。应用处于开发环境中。development.rb内容:Dashboard::Application.configuredoconfig.cache_classes=falseconfig.whiny_nils=trueconfig.consider_all_requests_l

随机推荐