STM32F4_DHT11数字温湿度传感器

news2024/12/23 14:20:44

目录

前言

1. DHT11简介

2. DHT11数据结构

3. DHT11的传输时序

3.1 DHT11开始发送数据流程

3.2 主机复位信号和DHT11响应信号

3.3 数字 “0” 信号表示方法

3.4 数字 “1” 信号表示方法

4. 硬件分析

5. 实验程序详解

5.1 main.c

5.2 DHT11.c

5.3 DHT11.h


前言

        DHT11数字温湿度传感器不但能测温度,还能测湿度。本节我们将利用STM32F4来读取DHT11的温湿度传感器,获取环境的温度和湿度值。

1. DHT11简介

        DHT11是广州奥松有限公司生产的一款温湿度一体化的数字传感器。该传感器包括一个电阻式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连。通过单片机等微处理器简单的电路连接就能实时的采集本地的温度和湿度DHT11 与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O 口

性能指标和特性:

  •         工作电压范围:3.5V~5.5V
  •         工作电流:0.5mA
  •         湿度测量范围:20-90%RH
  •         温度测量范围:0-50℃
  •         湿度分辨率:1%RH  8位
  •         温度分辨率:1℃  8位
  •         采样周期:1S
  •         单总线结构
  •         与TTL兼容(5V)

2. DHT11数据结构

DHT11数字温湿度传感器采用单总线数据格式。通过单个数据的引脚端口就可以完成输入输出双向传输。其数据包由5Byte(5*8=40Bit)组成。数据分小数部分和整数部分。

一次完整的数据传输为40bit,高位先行

数据格式:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据+8bit校验和

                其中校验和数据为前四个字节相加。

DHT11和DS18B20相同,传感器输出的都是未编码的二进制数字。数据(湿度、温度、整数、小数)之间应该分开进行处理。

比方说:

        

DHT11温湿度传感器一次完整的数据传输位如上图所示

那么:humidity(湿度)=byte4 . byte3=45.0(%RH)

           temperature(温度)=byte2 . byte1=28.0(℃)

           校验位byte0:byte4+byte3+byte2+byte1=73(humidity+temperature)

因此:温湿度的读取方式就是整数部分的二进制转换成十进制放在整数部分;小数部分的二进制转换成十进制放在小数部分。

注意:DHT11和MCU的一次通讯时间最大3ms,主机连续向DHT11采样的间隔建议不小于100ms。

3. DHT11的传输时序

3.1 DHT11开始发送数据流程

主机发送开始信号后,延时等待20us~40us后读取DHT11的应答信号,正常的话,DHT11 会拉低数据线,保持40~50us时间,读取总线为低电平,说明DHT11发送响应信号,DHT11发送响应信号后,再把总线拉高,保持40-50us,准备发送数据,每一位 bit 数据都以低电平开始。

如果读取响应为高电平,则DHT11没有响应,检查线路是否连接正常。

3.2 主机复位信号和DHT11响应信号

主机发送复位信号后,主机拉低总线,延时至少18ms,然后将主机拉高,延迟20-40us等待DHT11的应答信号。读取总线电平,如果为低电平,则表示从机DHT11发送应答信号成功。然后从机把总线拉高,准备发送数据。之所以拉高,是因为每发送一位数据都是以低电平开始的,为发送数据做准备。

3.3 数字 “0” 信号表示方法

因为DHT11也是遵循单总线协议的。所以也是通过高低电平延时不同时间进行组合来发送逻辑1或者逻辑0.

从机DHT11拉低总线,延时12-14us,然后从机释放总线,延时26-28us。表示发送数字 “0” 

3.4 数字 “1” 信号表示方法

从机DHT11拉低总线,延时12-14us,然后从机释放总线,延时116-118us。表示发送数字 “1” 

4. 硬件分析

  • VCC:正电源,支持3.5V~5.5V
  • Dout:输出引脚
  • NC:空脚
  • GND:地

DHT11数字温湿度传感器的第一脚接电源正极,第四脚接电源地端。数据端为第二脚。第二端数据引脚可以直接接在主机(单片机)的IO口。同时为了提高稳定性,一般在数据端和电源正极之间接一只4.7K的上拉电阻。第三脚NC为空脚,此管脚悬空不用。

5. 实验程序详解

实验现象:开机的时候先检测是否有DHT11存在,如果没有,则提示错误。只有在检测到DHT11之后才会开始读取温湿度值。如果发现了DHT11,则程序每隔100ms左右读取一次数据。

5.1 main.c

#include "stm32f4xx.h"                 
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "Key.h"
#include "usmart.h"
#include "MyI2C.h"
#include "AT24C02.h"
#include "DS18B20.h"
#include "DHT11.h"

//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
	LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
	led_set(sta);
}
int main(void)
{
	u8 t=0;
	u8 temperature;
	u8 humidity;
	delay_init(168);
	uart_init(115200);
	
	LED_Init();
	LCD_Init();
	
	POINT_COLOR=RED;
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
	LCD_ShowString(30,70,200,16,16,"DHT11 Text");
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTER");
	LCD_ShowString(30,110,200,16,16,"2023/20/23");
	
	while(DHT11_Init())//返回1表示没有检测到DHT11的存在,报错
	{
		LCD_ShowString(30,130,200,16,16,"DHT11 Rrror");
		delay_ms(200);
		LCD_Fill(30,130,239,130+16,WHITE);  //清除这块区域 x 30~239 y 130~130+16
		delay_ms(200);
	}
	LCD_ShowString(30,130,200,16,16,"DHT11 OK");
	POINT_COLOR=BLUE;
	LCD_ShowString(30,150,200,16,16,"Temperature:  C");
	LCD_ShowString(30,170,200,16,16,"Humidity:  %");
	while(1)
	{
		if(t%10==0) //每100ms读取一次
		{
			DHT11_Read_Data(&temperature,&humidity);  //读取温湿度的值
			LCD_ShowNum(30+12*8,150,temperature,2,16);  //显示温度
			LCD_ShowNum(30+9*8,170,humidity,2,16);  //显示湿度
		}
		delay_ms(10);
		t++;
		if(t==20)
		{
			t=0;
			LED0=!LED0;
		}
	}
}



5.2 DHT11.c

#include "stm32f4xx.h"            
#include "DHT11.h"
#include "delay.h"


//复位DHT11
void DHT11_Reset(void)
{
	//复位DHT11的时序:主机拉低总线,延时至少18ms,然后主机拉高总线,延时20-40us,等待从机应答
	DHT11_IO_OUT(); //复位时序是主机来完成的,所以首先设置主机IO引脚输出模式
	DHT11_DQ_OUT=0;  //主机拉低总线
	delay_ms(20);   //延时至少18ms
	DHT11_DQ_OUT=1;  //主机拉高总线
	delay_us(30);    //延时20-40us
}
//等待DHT11的应答信号
//返回1:未检测到DHT11的存在
//返回0:存在
u8 DHT11_CheckExist(void)
{
	//应答信号的时序是:主机发送应答信号后,从机DHT11会拉低信号线,保持40-50us。此时,若读取主机总线为低电平,这说明DHT11发送了应答信号
	//DHT11发送应答信号之后,再把总线拉高,保持40-50us,准备发送数据,每一位bit数据都是以低电平开始的。
	u8 Existence=0;
	DHT11_IO_IN();  //主机IO引脚设置为输入模式,等待从机的应答信号输入
	while(DHT11_DQ_IN&&Existence<100) //从机DHT11会拉低信号线,保持40-80us,等待应答  按位&&操作必须两个条件都为真才是真
	{
		//DHT11_DQ_IN为真,就一直在这里循环,表示始终没有接收到从机DHT11的应答信号;因为总线拉低,读取高电平是始终没有响应的
		//DHT11_DQ_IN是从机DHT11发送主机MCU的信号,应答信号时,从机会拉低信号线,因此,如果主机收到的是高电平,那么意味着没有发送应答信号
		//Existence<100表示:设置一个缓冲时间去等待从机发送应答,这里设置的就是100us
		Existence++;
		delay_us(1);
	}
	if(Existence>=100)
	{
		return 1; //主机给从机的缓冲时间是100us,主机等了从机100us还是没有接收到应答信号,那么返回信息,未检测到DHT11的存在
	}
	else         //在给定的100us内接收到了应答信号,将缓冲计时变量Existence清0,为后续总线拉高后延迟做准备
		Existence=0;
	while(!DHT11_DQ_IN&&Existence<100) //拉低后再次拉高,准备发送数据,延迟40-80us
	{
		//!DHT11_DQ_IN表示主机接收到了应答信号,也就是主机接收到了低电平,延迟100us
		Existence++;
		delay_us(1);
	}
	if(Existence>=100)
	{
		return 1;
	}
	return 0;
}
//从DHT11读取一个位
//返回值:1/0
u8 DHT11_Read_Bit(void)
{
	//DHT11读取一位的时序:不管读的那一位是高电平1还是低电平0,首先从机DHT11都需要拉低总线
	//数据0:从机拉低总线,延时12-14us,然后从机释放总线,延时26-28us
	//数据1:从机拉低总线,延时12-14us,然后从机释放总线,延时116-118us
	u8 data=0;
	while(DHT11_DQ_IN&&data<100)//等待变为低电平
		//主机从DHT11读取一位,从机要拉低总线,在没有拉低之前,主机收到的DHT11_DQ_IN一定是高电平1,而通过观察低电平的时序可以发现,不管是逻辑1还是0,低电平延时12-14us
	//所以设置的100us是远远大于低电平延时时间的,所以data<100这个条件在此while循环中一定为真,跳出循环的条件一定是DHT11_DQ_IN=0,也就是等待变为低电平
	{
		data++;
		delay_us(1);
	}
	data=0; //离开while循环时主机一定收到了低电平,此时DHT11_DQ_IN=0;设置data=0是为后续等待高电平做准备
	while(!DHT11_DQ_IN&&data<100) //等待变为高电平
	{
		//从上面的循环出来时,DHT11_DQ_IN=0,取反就为真
		//条件data<100:低电平0的整个发送时序延时时间(包括低电平延时12-14us,高电平延时26-28us)也远远小于100us,所以data<100在该循环中也是始终为真的
		//				高电平1的整个发送时间是大于100us的(低电平延时12-14us,高电平延时116-118us)
		//经分析,离开循环的条件是DHT11_DQ_IN变为高电平=1,取反等于0,为假。在低电平0的12-14us过去以后,高电平信号就会来临
		data++;
		delay_us(1);
	}
	//delay_us(40) 没有这个延迟40us是无法判断是数据1还是数据0的
	delay_us(40);//从上一个while循环出来以后,时序停留在刚刚变为高电平的瞬间,这个时候延时40us,数据0会跳过高电平,数据1会停留在高电平
	if(DHT11_DQ_IN) //这个时候根据主机读到的是1还是0设置返回值
		return 1;
	else
		return 0;
}
//从DHT11读取一个字节
//返回值:读到的数据
u8 DHT11_Read_Byte(void)
{
	u8 i,data;
	data=0;
	for(i=0;i<8;i++)
	{
		data=data<<1;  //DHT11读取的字节是高位先行的,所以每循环一次都要将上次读取的那一位左移,右侧最低位补0,每次都将新读取的那一位数据和0进行|或运算。
		data=data|DHT11_Read_Bit();
	}
	return data;
}
//从DHT11读取一位数据
//temperature:温度值(范围:0~50℃)
//humidity:湿度值(范围:20%~90%)
//返回值:0 正常;1 读取失败
u8 DHT11_Read_Data(u8 *temperature,u8 *humidity)
{
	//DHT11的数据格式是:5个字节,40位数据,前两位是湿度的整数和小数部分,第三、第四位是温度的整数和小数部分,最后一位是校验位
	u8 Buf[5]; //定义一个数组来存放收到的5个字节的数组
	u8 i;
	DHT11_Reset();
	if(DHT11_CheckExist()==0)  //应答信号返回值为0,表示检测到了DHT11应答信号
	{
		for(i=0;i<5;i++) //调用读字节函数将读取到的5个字节存放到Buf数组中
		{
			Buf[i]=DHT11_Read_Byte();
		}
		if((Buf[0]+Buf[1]+Buf[2]+Buf[3])==Buf[4]) //检验成功
		{
			*humidity=Buf[0];   //这里只显示了温湿度的整数部分
			*temperature=Buf[2];
		}
	}
	else
		return 1;  //DHT11_CheckExist()==1 表示没有检测到DHT11的应答信号,报错
	return 0; //返回正常情况
}
//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
u8 DHT11_Init(void)
{
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);  //使能GPIOG时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;  //默认设置为输出模式
	GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;
	GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOG,&GPIO_InitStructure);
	
	DHT11_Reset();
	return DHT11_CheckExist(); //初始化返回检测DHT11是否成功,0:成功 1:收不到应答信号,报错
}



5.3 DHT11.h

#ifndef _DHT11__H_
#define _DHT11__H_
#include "sys.h"


//IO方向设置
//IO方向是通过位段设置的,调用GPIO模式寄存器,以下代码已经多次使用,如有不理解,可回头温习DS18B20的实验代码
#define DHT11_IO_IN() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=0<<9*2;}	//PG9输入模式
#define DHT11_IO_OUT() {GPIOG->MODER&=~(3<<(9*2));GPIOG->MODER|=1<<9*2;} 	//PG9输出模式 

//IO引脚设置
#define DHT11_DQ_OUT PGout(9)  //PG9输出
#define DHT11_DQ_IN PGin(9)    //PG9输入


void DHT11_Reset(void);
u8 DHT11_CheckExist(void);
u8 DHT11_Read_Bit(void);
u8 DHT11_Read_Byte(void);
u8 DHT11_Read_Data(u8 *temperature,u8 *humidity);
u8 DHT11_Init(void);

#endif

本节程序每一步都进行了详细的注释,有哪里解释的不对的,欢迎指正!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518444.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c#笔记-泛型

泛型方法 假设我们要编写一个方法&#xff0c;它可以获取任意类型数组中的最大值&#xff0c; 并返回该值。我们可能会这样写&#xff1a; static int GetMax(int[] array) {Array.Sort(array);return array[array.Length - 1]; }这个方法可以实现我们的需求&#xff0c;但是…

【虚拟机】VMware16保姆级安装教程

大家好&#xff0c;我是雷工&#xff01; 工作中需要用到各种各样的工控软件&#xff0c;有时候甚至需要不同版本的软件&#xff0c;但频繁装卸软件比较麻烦&#xff0c;而且像WinCC和博图软件对系统要求比较严格&#xff0c;卸载重装可能就出问题&#xff0c;此时就不得不重装…

MODBUS通讯程序设计流程图

MODBUS通讯一般来说是基于RS485电平的通讯&#xff0c;RS485是半双工&#xff0c;很多单片机工程师做的通讯不是稳定&#xff0c;主要原因是流程没有掌控好。 我以前也犯过和他们一样错误&#xff0c;觉得很容易。在QQ群里&#xff0c;有位老工程师和大家一样犯了同样的毛病。…

宣布!BNB Greenfield 测试网上的部署

我们很高兴地宣布&#xff0c;4EVERLAND Hosting 现在支持在 BNB Greenfield Testnet 上部署&#xff0c;为开发和托管去中心化应用程序 (dApp) 提供更具可扩展性和安全性的选项。 这种集成使项目团队能够轻松且经济地利用 4EVERLAND 在去中心化托管方面的尖端技术和专业知识&…

No module named ‘torch_geometric‘解决办法——PyG(PyTorch Geometric)安装教程

1、查询已安装的torch版本 一定要先查询自己的torch版本&#xff0c;也不同版本的依赖跟库都是不一样的。 import torch print("torch版本&#xff1a;",torch.__version__)输出结果&#xff1a; 2、安装依赖 2.1 进入官网下载依赖。 官网下载链接&#xff1a;…

word查找与替换

比如我有一个需求&#xff1a;将word中符号和中文为全角&#xff0c;但英文字符和数字为半角。 这种我们应该怎么办&#xff1f; 可以通过查找和替换解决。 按ctrl H即可看到查找替换框&#xff0c;注意word中查找替换不是ctrl F 查找 功能&#xff1a;在以下选项中查找 我们…

WiFi(Wireless Fidelity)基础(十)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

Springboot +Flowable,各种历史信息如何查询(二)

一.简介 正在执行的流程信息是保存在以 ACT_RU_ 为前缀的表中&#xff0c;执行完毕的流程信息则保存在以 ACT_HI_ 为前缀的表中&#xff0c;也就是流程历史信息表。 假设有一个流程&#xff0c;流程图如下&#xff1a; 当这个流程执行完毕后&#xff0c;以 ACT_RU_ 为前缀的…

【ubuntu】alias命令

1 alias的作用 alias允许用户为命令创建简单的名称或缩写。如果需要在窗口输入复杂且较多的命令时&#xff0c;alias就可以减轻用户的记忆负担&#xff0c;发挥作用啦。 2 语法 &#xff08;1&#xff09;简单命令 alias [name”value”] 举例&#xff1a;alias ppwd 命令…

学习网络安全有哪些就业方向?网络安全就业前景!

在当下&#xff0c;网络安全是非常重要的&#xff0c;与我们的生活息息相关&#xff0c;因此网络安全课程也成为了热门技术&#xff0c;引发众多人学习。那么学习网络安全有哪些就业方向?我想很多人都不太了解&#xff0c;接下来我们一起来看看吧。 首先说一下什么是网络安全…

科罗拉多州立大学发布CSU-MLP模型,用随机森林预测中期恶劣天气

本文首发自 HyperAI超神经微信公众号~ 内容一览&#xff1a;近期&#xff0c;来自美国科罗拉多州立大学与 SPC 的相关学者联合发布了一个基于随机森林的机器学习模型 CSU-MLP&#xff0c;该模型能够对中期 (4-8天) 范围内恶劣天气进行准确预报。目前该成果刊已发表在《Weather …

进销存是什么意思?值得推荐的进销存软件有哪些?

在这个消费不断升级、直播带货和电商不断冲击的时代&#xff0c;实体店的生存变得越来越艰难&#xff0c;如何提高门店管理效率、降低管理成本、提升门店客流量&#xff0c;是实体店当下急需解决的几大问题。 进销存软件就是为了帮助实体店解决以上几大难题而设计的&#xff0c…

Xline v0.4.0: 一个用于元数据管理的分布式KV存储

Xline是什么&#xff1f;我们为什么要做Xline&#xff1f; Xline是一个基于Curp协议的&#xff0c;用于管理元数据的分布式KV存储。现有的分布式KV存储大多采用Raft共识协议&#xff0c;需要两次RTT才能完成一次请求。当部署在单个数据中心时&#xff0c;节点之间的延迟较低&a…

对STM32栈的理解Stack_Size EQU 0x00000400

对STM32栈的理解Stack_Size EQU 0x00000400 Stack_Size EQU 0x00000400表示什么意思可以通过查找flash内存的方式定位存储1.flash2.RAM内部 本人主要为个人参考网络及个人总结而来比较&#xff0c;如有雷同请告知&#xff0c;即刻删除 以下引用网上资料 理解堆和栈的区别 &…

RK3588 设备树pinctrl gpio子系统解析,解决GPIO无法正确拉高拉低的问题,RK3588设备树详解

一、RK3588设备树结构 firefly的官方说明文档RK3588gpio系统说明 function {group {rockchip,pin <bank gpio func &ref>;}; };其中&#xff0c;bank是所属的组&#xff0c;Core-3588J 有 5 组 GPIO bank&#xff1a;GPIO0-GPIO4&#xff0c;每组又以 A0-A7, B0-B…

DiffDock源码解析

DiffDock源码解析 数据预处理 数据输入方式 df pd.read_csv(args.protein_ligand_csv), 使用的是csv的方式输入&#xff0c; 格式&#xff1a; 不管受体还是配体&#xff0c; 输入可以是序列或者3维结构的文件 如果蛋白输入的是序列&#xff0c;需要计算蛋白的三维结构&am…

Type-C接口在显示器上有什么作用?Type-C 显示器方案介绍

一显示器的Type-C口&#xff0c;是未来显示器的接口的“终极形态”&#xff0c;未来显示器可以不要USB-A&#xff0c;不要HDMI&#xff0c;不要3.5音频&#xff0c;甚至不要DP口&#xff0c;但Type-C口一定会越来越多。 二显示器Type-C有什么用&#xff0c;有什么类型 1只可以…

大势智慧软硬件技术答疑第三期

1.重建大师6.0试用版&#xff0c;怎么导出DOM、DEM&#xff1f; 答&#xff1a;需要先生成三维模型&#xff0c;然后再提交产品选择DOM和DEM。 2.麻烦问下&#xff0c;修模出来贴的纹理图片&#xff0c;导出osgb后再打开就模糊了是什么情况&#xff1f; 答&#xff1a;拿高清…

OushuDB × 东方证券:数据仓库信创国产化最佳实践

前言&#xff1a;东方证券是一家综合类证券公司&#xff0c;成立于 1998 年&#xff0c;总部设在上海。经过 20 多年的发展&#xff0c;东方证券现有分支机构 177 家、管理 3200 亿资产&#xff0c;服务上亿客户。与一个大型金融机构相匹配的&#xff0c;正是东方证券当前管理的…

想利用业余时间当一名黑客?要具备什么能力,确定不来看看?

几十年前刚有小型电脑的时候&#xff0c;产生了一个由程序专家和部分网络名人所组成的文化社群。该社群的成员创造出了hacker这个词&#xff0c;也就是人们常说的“黑客”。这些黑客们建立了后来的Internet&#xff0c;以及发明了电脑的操作系统。 如果有人对这种文化做出了贡…