[教程]一文搞懂STM32使用DHT11采集温湿度

news2025/1/21 9:42:12

1、DHT11简介

      DHT11数字温湿度传感器是一款含有已校准数字信号输出的温湿度复合传感器。它应用专用的数字模块采集技术和温湿度传感技术,确保产品具有极高 的可靠性与卓越的长期稳定性。传感器包括一个电阻式感湿元件和一个NTC测 温元件,并与一个高性能8位单片机相连接。因此该产品具有品质卓越、超快 响应、抗干扰能力强、性价比极高等优点。每个DHT11传感器都在极为精确的 湿度校验室中进行校准。校准系数以程序的形式储存在OTP内存中,传感器内 部在检测信号的处理过程中要调用这些校准系数。单线制串行接口,使系统集 成变得简易快捷。超小的体积、极低的功耗,信号传输距离可达20米以上,使 其成为各类应用甚至最为苛刻的应用场合的最佳选则。产品为 4 针单排引脚 封装。连接方便,特殊封装形式可根据用户需求而提供(来自于数据手册)。

2、原理分析

      简介中的重点,传感器包括一个电阻式感湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接,也就是说DHT11的组成实际是单片机通过ADC采集电压,并将采集处理好的数据根据一定的协议方式与其他设备进行通信。

      感湿电阻、NTC(温感电阻)随温湿度的变化阻值会产生变化,这样我们采集到的电压值就会跟着变化。(概念电路图如下)

2、应用

    暖通空调 、测试及检测设备、汽车 、数据记录器、消费品 、自动控制、气象站 、家电、湿度调节器 、医疗、除湿器

4、DHT11数据手册知识点概述

      DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,具体格式在下面说明,当前小数 部分用于以后扩展,现读出为零.操作流程如下: 一次完整的数据传输为40bit,高位先出。 数据格式:8bit湿度整数数据+8bit湿度小数数据 +8bi温度整数数据+8bit温度小数数据 +8bit校验和 数据传送正确时校验和数据等于“8bit湿度整数数据+8bit湿度小数数据+8bi温度整数数据+8bit温度小数数据。

    其实从这段描述可以看出3点:1)单总线的方式进行数据传输(一根线进行数据的收发)2)完整的数据有40bit(需要连续读取40次)3)数据格式(可以确定温湿度在哪些位表示)。

5、时序图分析

1)总的通信过程

    首先我们对总的通信过程进行分析:从上图可以看出单片机作为主机,首先输出低电平(数据总线由高变低),持续一段时间的低电平,单片机再拉高总线。之后总线由输出变为输入 ,接收DHT11发送的低电平信号,这个低电平持续一段时间之后,又拉高(DHT11拉高的)一段时间,之后进行数据传输,可以看到,传输数据是0和1都是通过高电平实现的,通过高电平持续的时间来判断是数据1还是数据0,这里还有一个需要注意的点,每个数据传输有一段低电平的间隔时间。

2)起始和响应时序图分析(握手)

   上部分提到了,总的通信过程。后面的时序图都是详细的单个部分时序图。根据上图我们可以看到, 最开始单片机将总线电平由高拉低,持续时间为至少18ms,然后再拉高20-40us,这个过程是起始信号,然后总线状态由输出变为输入,如果DHT11正常且接收到了单片机发送的起始信号,DHT11会将总线拉低80us,之后再拉高80us,作为回应。这段主要就是握手的过程,单片机告诉DHT11我要接数据了,然后DHT11回应我准备好了。之后DHT11就开始发送温湿度数据了。

3)数据接收数据时序图分析

  根据数据接收时序图可以看出,每次数据发送的开始会有一段50us的低电平信号,之后就进行1bit的数据传输了,如果传输数据0,高电平持续26us-28us,如果传输数据1,高电平持续70us。

 6、程序设计

1)由于我们需要对一个GPIO口实现,收发两种功能所以首先写两个函数,分别是输入初始化和输出初始化。

static void DHT11_IO_IN(void)//初始化配置函数  输入
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;//自己使用的DHT11与单片机连接的GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;//输入模式
    GPIO_InitStruct.Pull = GPIO_PULLUP;//上拉输入
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

static void DHT11_IO_OUT(void)//初始化配置函数  输入
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;//自己使用的DHT11与单片机连接的GPIO引脚
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;//推挽输出
    GPIO_InitStruct.Pull = GPIO_PULLUP;//这个无所谓
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;//配置速度 这里配置的高速
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);
}

2)接下来我们按照通信时序图一步一步写函数

a、单片机发送起始信号函数

void DHT11_Rst(void)
{
    DHT11_IO_OUT(); 	//SET OUTPUT  转换成输出模式
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_RESET); 	//拉低DQ
    HAL_Delay(20);    	//拉低至少18ms
    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);		//DQ=1
    delay_us(30);     	//主机拉高20~40us
}

 

起始信号函数,根据时序图,GPIO为输出模式,总线由高电平变为低电平,这里我们可以使用GPIO输出函数,直接输出低电平(因为DHT11数据总线默认硬件连了上拉电阻,所以他默认是高电平的)。低电平持续时间最少18ms,所以我们延时20ms来保障持续时间足够(实现上图红色部分)。之后拉高电平20到40us,这里我们通过输出高电平,并延时30us来实现(实现上图黄色部分)。

b 、检测DHT11响应函数

#define	DHT11_DQ_IN  		    HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)
//等待DHT11的回应
//返回1:未检测到DHT11的存在
//返回0:存在
uint8_t DHT11_Check(void)
{
    uint8_t retry=0;
    DHT11_IO_IN();//输入模式
    while (DHT11_DQ_IN&&retry<100)//DHT11会拉低80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    else retry=0;
    while (!DHT11_DQ_IN&&retry<100)//DHT11拉低后会再次拉高80us
    {
        retry++;
        delay_us(1);
    };
    if(retry>=100)return 1;
    return 0;
}

    这里目的是读取DHT11的响应信号,首先我们将IO的模式配置为输入模式。DHT11_DQ_IN是读取IO电平的一个宏定义,文中出现的所有DHT11_DQ_IN都等价于HAL_GPIO_ReadPin(DHT11_GPIO_PORT, DHT11_GPIO_PIN)(读取的IO口就是我们一直用的DHT11的数据总线),可以看到,如果IO电平为高电平且retry变量小于100,我们进入while循环,进1次延时1us并retry加1,这里得到目的是如果一直到大于100us(其实这里耗时会比100us大,因为执行retry加1也需要耗时间,这里忽略不计),IO口的电平状态还是高电平,执行下面语句if(retry>=100)return 1;返回1证明我们没有检测到DHT11,因为根据时序图我们可以看到DHT11如果正常响应了会回应80us的响应信号(上图红色部分)。下面判断上图中 黄色部分,将上图中DHT11_DQ_IN读取到的值取反,即如果是低电平,且retry小于100时延时1us,retry加1。这样做的目的同样是判断DHT11是否响应了,只不过这里是图中黄色部分的高电平响应。同样识别到响应信号了返回0,大于100us了还没有响应返回1。

c 、读取数据

  读取数据我们写3个函数分别是,读取1位,读取8位即1个字节,读取40位即,DHT11发来的一次完整数据。

//从DHT11读取一个位
//返回值:1/0
uint8_t DHT11_Read_Bit(void)
{
    uint8_t retry=0;
    while(DHT11_DQ_IN&&retry<100)//等待变为低电平
    {
        retry++;
        delay_us(1);
    }
    retry=0;
    while(!DHT11_DQ_IN&&retry<100)//等待变高电平
    {
        retry++;
        delay_us(1);
    }
    delay_us(40);//等待40us
    if(DHT11_DQ_IN)return 1;
    else return 0;
}

  这里实现的是读取1位数据,程序中首先同样用while的方法等待50us的低电平(上图红色部分),之后再等待高电平,这里的高电平就是我们要接收的数据0或者1,当识别到高电平时不进入第二个while函数,执行延时40us这里40us远远大于28us(图中黄色部分),且远远小于70us(70us为数据1,上图只显示出了0)。延时之后执行    if(DHT11_DQ_IN)return 1;else return 0;这段函数,也就是说,如果40us之后检测IO电平还是高电平,那么数据肯定时1我们返回1,如果变低了那么就返回0。

//从DHT11读取一个字节
//返回值:读到的数据
uint8_t DHT11_Read_Byte(void)
{
    uint8_t i,dat;
    dat=0;
    for (i=0; i<8; i++)
    {
        dat<<=1;
        dat|=DHT11_Read_Bit();
    }
    return dat;
}

   这里读取1个字节,这里就简单了,我们直接调用上面实现1位的函数,然后循环8次就可以了。

//从DHT11读取一次数据
//temp:温度值(范围:0~50°)
//humi:湿度值(范围:20%~90%)
//返回值:HAL_OK,正常;1,读取失败
uint8_t DHT11_Read_Data(uint8_t *humiH,uint8_t *humiL,uint8_t *tempH,uint8_t *tempL)
{
    uint8_t buf[5];
    uint8_t i;
    DHT11_Rst();
    if(DHT11_Check()==0)
    {
        for(i=0; i<5; i++) //读取40位数据
        {
            buf[i]=DHT11_Read_Byte();
        }
        if((buf[0]+buf[1]+buf[2]+buf[3])==buf[4])
        {
            *humiH=buf[0];
            *humiL=buf[1];
            *tempH=buf[2];
            *tempL=buf[3];
        }
    } else
        return HAL_ERROR;

    return HAL_OK;
}

   这里我们读取一次完整的数据即40bit ,首先判断DHT11_Check()的返回值,这是我们前面写的函数(DHT11响应信号函数),如果返回1证明DHT11没有回响应信号,不进入IF,返回HAL_ERROR,如果DHT11_Check()的返回值为0证明DHT11正确回复了响应信号,程序进入IF执行,IF里先循环5次执行读出完整数据,接下来验证校验和,如果前4个字节数加起来等于第五个字节(校验和)数据正确进入IF,返回读取到的温湿度数据。我们需要读取DHT11数据的地方,直接调用,我们这个函数就可以了。

d 、上电初始化

//初始化DHT11的IO口 DQ 同时检测DHT11的存在
//返回1:不存在
//返回0:存在
uint8_t FS_DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    GPIO_InitStruct.Pin = DHT11_GPIO_PIN;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_GPIO_PORT, &GPIO_InitStruct);

    HAL_GPIO_WritePin(DHT11_GPIO_PORT, DHT11_GPIO_PIN, GPIO_PIN_SET);	// 输出高
    DHT11_Rst();  //复位DHT11
    return DHT11_Check();//等待DHT11的回应
}

  这里我们写个函数,上电初始化使用,先配置为推挽输出模式,然后输出高电平,这里的目的是为了软件端保障DHT11上电默认总线为高电平。之后使用DHT11_Rst(); 对DHT11发送起始信号,然后等待DHT11响应使用了return DHT11_Check();函数,即如果正确响应了返回0,没有正确响应返回0。

  到这里我们的DHT11程序编写就讲解完成了,使用时上电初始化调用FS_DHT11_Init()函数,之后需要读取温湿度数据的地方调用 DHT11_Read_Data(uint8_t *humiH,uint8_t *humiL,uint8_t *tempH,uint8_t *tempL)函数就可以了。

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

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

相关文章

GO语言基础-08-内建函数-make()、new()

文章目录1. make1.1 概述1.2 示例&#xff08;make切片&#xff09;1.3 示例&#xff08;make map&#xff09;1.4 示例&#xff08;make 通道&#xff09;2. new2.1 概念2.2 示例&#xff08;new 切片&#xff09;2.3 示例&#xff08;new和make对比&#xff09;2.4 示例&…

Java基础算法每日5道详解(2)

83. Remove Duplicates from Sorted List 从排序列表中删除重复项 Given the head of a sorted linked list, delete all duplicates such that each element appears only once. Return the linked list sorted as well. Example 1: Input: head [1,1,2] Output: [1,2]Exa…

20230109测试ToyBrick的RK3588开发板运行Buildroot的V20220811版本

20230109测试ToyBrick的RK3588开发板运行Buildroot的V20220811版本 2023/1/9 14:25 开发板&#xff1a;Toybrick的TB-RK3588X开发板 SDK&#xff1a;RK3588_LINUX_20220811\rk3588-linux-20220811.tar.gz_06 H:\BaiduNetdiskDownload\RK3588_LINUX_20220811 rk3588-linux-2022…

【SQLyog错误号码2058解决办法】

当你遇到下图这个错误时&#xff0c;是由于SQLyog在8.0以上版本采用了新的加密方式。 解决办法&#xff1a; win R打开 &#xff0c; 输入cmd&#xff0c;打开命令行窗口&#xff0c; 然后连接你的SQLyog版本的服务器&#xff0c; mysql -uroot -P3306 -p注意&#xff1a;…

【Kotlin】数字类型 ( 安全转换函数 | 浮点型转整型 )

文章目录一、安全转换函数二、浮点型转整型一、安全转换函数 在 Kotlin 中 , 将 字符串 String 类型 转为 数字类型 , 如果 字符串 代表的数字类型 与 要换转的 数字类型 不匹配 , 就会出异常 ; 如 : 执行如下代码 , 就会报异常 ; 字符串内容是 0.5 , 显然是一个 Double 类…

Kotlin Flow响应式编程,StateFlow和SharedFlow

本文同步发表于我的微信公众号&#xff0c;扫一扫文章底部的二维码或在微信搜索 郭霖 即可关注&#xff0c;每个工作日都有文章更新。 大家好&#xff0c;今天是Kotlin Flow响应式编程三部曲的最后一篇。 其实回想一下我写这个Kotlin Flow三部曲的初衷&#xff0c;主要还是因为…

基于瑞芯微平台cif接口dvp相机的视频接入(ov2640、rv1126为例)

基于瑞芯微平台cif接口dvp相机的视频接入&#xff08;ov2640、rv1126为例&#xff09;名词定义视频格式sensor与ispI2CXCLK行场同步信号DATA抓图名词定义 CIF&#xff0c;指RK芯片中的VIP模块&#xff0c;用以接收Sensor数据并保存到Memory中&#xff0c;仅转存数据&#xff0c…

Komo 综合资产收集和漏洞扫描工具

前言 因工作中的需要&#xff0c;开发了这款综合资产收集和漏洞扫描工具&#xff0c;方便在工作中各方面的收集资产和漏洞扫描&#xff0c;同时也可用于挖洞。 Komo已经在工作中辅助我挖到过一些漏洞&#xff0c;同时轻便了我资产收集的过程。 Komo is a comprehensive asset c…

【图像处理OpenCV(C++版)】——3.3 几何变换之极坐标变换

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

MATLAB | 如何从热图中提取数据

这期做了个可能有用的小工具&#xff0c;一般论文中热图很少给出数据&#xff0c;于是就想写个小工具通过热图上的颜色估计出数据值来&#xff0c;目前写了个初版的工具分享给大家&#xff01; 工具函数 由于只是初版&#xff0c;要手动改的地方还是不少的&#xff0c;要设置…

PHP多商户AI智能在线客服系统源码 机器人自动回复 即时通讯聊天系统源码

一套智能在线客服系统源码 多商户网页客服系统源码 支持二十种国际语言 带机器人自动回复。 框架&#xff1a;Thinkphp5workerman&#xff0c; 环境&#xff1a;nginxphp7.3mysql5.6 支持H5公众号APP小程序 了解更多可私信我&#xff01; 系统功能&#xff1a; 1、支持国际…

编写程序时调用第三方程序时使用的是相对路径而不是绝对路径会造成什么严重后果(Windows Linux)

简介 在编写程序时&#xff0c;有很多人调用第三方程序使用的是相对路径&#xff0c;而不是绝对路径&#xff0c;如下&#xff1a; #!/bin/python3import osos.system("whoami") #调用whoami程序&#xff0c;查看当前用户名#!/bin/bashfind / -name "hellowor…

day10|239. 滑动窗口最大值、347.前 K 个高频元素

239. 滑动窗口最大值 给你一个整数数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。 示例 1&#xff1a; 输入&#xff1a;nums [1,3…

如何修改视频MD5的格式?这些方法值得你收藏

MD5实际上是计算机安全领域中广泛使用的一种散列函数&#xff0c;可以用来保护消息的完整性&#xff0c;简单来说就是类似于我们的指纹&#xff0c;可以说MD5是每个文件的“数字指纹”。比如&#xff1a;我们在平台上传一些热门视频&#xff0c;平台会自动识别视频的MD5值&…

嵌入式 LINUX 驱动开发 day01 第一个内核模块程序 多文件编译为一个程序, 内核模块参数, 内核模块依赖

1.第一个内核模块程序 ( 记得配置自己的交叉编译的工具,) 首先两个文件 vser.c Makefile (记得大写的M) vser.c #include <linux/init.h> //内核初始化头文件 #include <linux/module.h> //内核模块文件 #include <linux/kernel.h> //&…

Java基础算法每日5道详解(6)

112. Path Sum 路径总和 Given the root of a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum. A leaf is a node with no children. Example 1: Input: ro…

html+css实现一个响应式管理平台架构模板

文本将会带你使用htmlcss实现一个响应式的管理平台架构模板&#xff0c;目前来说市面上的管理平台架构模板大同小异&#xff0c;文本的知识点都会符合场景所需。 目录 1、管理平台的架构内容 2、顶部的布局 3、下半部分布局 4、左侧菜单区域实现 5、右侧主体区域实现 …

前端重新部署如何通知用户刷新网页?

我把我掘金的文章同步一份给CSDN 1.目标场景 有时候上完线&#xff0c;用户还停留在老的页面&#xff0c;用户不知道网页重新部署了&#xff0c;跳转页面的时候有时候js连接hash变了导致报错跳不过去&#xff0c;并且用户体验不到新功能。 2.思考解决方案 如何去解决这个问…

顶象助力绿球金科打造App低碳出行场景

“低碳出行”、“碳中和”、“碳惠普”正在成为近几年的科技热词之一。 自2020年9月&#xff0c;中国向世界许下“力争2030年前实现碳达峰&#xff0c;2060年前实现碳中和”的承诺以来&#xff0c;一场围绕绿色节能、低碳减排的变革正在席卷各行各业。 “碳中和”已经成为时代…

如何让SCI期刊审稿人,理解你的文章? - 易智编译EaseEditing

首先需要对论文进行全文润色 对于发表论文来说&#xff0c;进行润色是必须的&#xff0c;正因为SCI论文翻译要求高难度大&#xff0c;无论笔译还是口译都一定要有过硬的基本功&#xff0c;知识面要足够宽广&#xff0c;专业综合能力要求高。 所以当一篇论文的整体结构不到位&…