STM32实战指南:DHT11温湿度传感器驱动开发与避坑指南

news2025/5/20 8:17:40

知识点1【DHT11的概述】

1、概述

DHT是一款温湿度一体化的数字传感器(无需AD转换)。

2、驱动方式

通过单片机等微处理器简单的电路连接就能实时采集本地湿度和温度。DHT11与单片机之间采用单总线进行通信,仅需要一个IO口。

相对于单片机是片下外设,因此配置的时候无需使用复用方式,使用通用方式即可。

3、DHT11的数据结构

数据长度:40位

8bit的湿度整数,8bit的湿度小数,8bit的温度整数,8bit的温度小数+8bit的校验和位

知识点2【DHT11的使用】

DHT11一次通讯时间最大3ms,主机连续采样间隔建议不小于100ms

以上是理论,但实际使用中有所不同:

我们在实际使用中,连续采样间隔建议是 1s以上

补充

1、第一次采样前,我们打开DHT11后,即我们下面介绍的复位(void DHT11_Reset(void);),需要等待2s以上,因为开启需要一个过程:DHT11 上电后内部有加热片和采集电路,需要约 1–2 s 的时间才能稳定到正常工作温度和电压;如果太快去读,传感器还没“热起来”,数据就不准。这里大家注意一下。

2、复用是针对于片上外设的,片下外设用通用模式即可

1、复位信号

①、DHT的复位信号,主机掌握数据总线

(1)拉低 至少18ms

(2)再拉高20-40us

②、DHT的响应信号,从机掌握数据总线

(1)拉低 40-50us

(2)再拉高40-50us

注意:此时DHT11对主机复位信号的响应信号

在DHT11中,数据(0和1)都是低电平开始的

2、DHT11表示1的方法

(1)拉低12-14us

(2)拉高116-118us

3、DHT11表示0的方法

(1)拉低12-14us

(2)拉高26-28us

因此我们这里区别 0 和 1 的方法就是利用的高电平的持续时间不同,利用这个时间差来判断是0还是1。

知识点3【代码演示】

main.c

#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "delay.h"
#include "usart.h"
#include "DHT11.h"

int flag = 0;

int main(void)
{
	u8 data[5] = {0};
	//有限级组的配置
	Systick_Init(72);
	Usart1_Init(9600);
	
	while(1)
	{	
		Delay_us(2000000);
		DHT11_RcvData(data);
	}
}

DHT11.c

**#include "DHT11.h"

//端口结构体声明
GPIO_InitTypeDef GPIO_DHT11_InitStruct;

//端口初始化
void DHT11_GPIO_Init(void)
{
	//开启时钟
	RCC_APB2PeriphClockCmd(DHT11_CLOCK,ENABLE);
	
	//配置GPIO引脚
	GPIO_StructInit(&GPIO_DHT11_InitStruct);
	GPIO_DHT11_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_DHT11_InitStruct.GPIO_Pin = DHT11_PIN;
	GPIO_DHT11_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD,&GPIO_DHT11_InitStruct);
	
}

//复位
void DHT11_Reset(void)
{
	//时钟+模式配置
	DHT11_GPIO_Init();
	
	//复位信号发送
	//发0 20ms
	GPIO_ResetBits(DHT11_GPIO,DHT11_PIN);
	Delay_us(20000);
	
	//发1 30ms
	GPIO_SetBits(DHT11_GPIO,DHT11_PIN);
	Delay_us(30);
	
	//切换模式:上拉输入模式,准备接收DHT11应答
	GPIO_DHT11_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_Init(GPIOD,&GPIO_DHT11_InitStruct);
	
	//等待引脚电平被拉低(等待DHT11的应答)
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN));
}

//应答
u8 DHT11_Ack(void)
{
	int flag = 0;
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == 0)
	{
		Delay_us(1);
		flag++;
		if(flag == 100)
		{
			return 0;
		}
	}
	
	flag = 0;
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == 1)
	{
		Delay_us(1);
		flag++;
		if(flag == 100)
		{
			return 0;
		}
	}
	return 1;
}

//接收应答 5个byte
void DHT11_RcvData(u8 *data)
{
	int i;
	DHT11_Reset();
	if(DHT11_Ack() == 1)
	{
		for(i = 0; i < 5;i++)
		{
			data[i] = DHT11_RcvByte();
		}
		if(data[4] == data[0] + data[1] + data[2] + data[3] )
		{
			printf("湿度是:%u.%u  温度是:%u.%u\\n",data[0],data[1],data[2],data[3]);
		}
		else
		{
			printf("采集错误\\n");
		}
	}
}

//接收应答 1个byte
u8 DHT11_RcvByte(void)
{
	int i;
	u8 data = 0;
	for(i = 0;i < 8;i++)
	{
		data <<= 1;
		data |= DHT11_RcvBit();
	}
	return data;
}

//接收应答 1个bit
u8 DHT11_RcvBit(void)
{
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == SET);
	
	if(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == RESET)
	{
		while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == RESET);
		Delay_us(40);
		if(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == SET)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	return 1;
}**

DHTT11.h

#ifndef _DHT11_H_
#define _DHT11_H_
#include "stm32f10x.h"
#include "stm32f10x_conf.h"
#include "delay.h"
#include "usart.h"
//这里我使用的是PD0  GPIOD GPIO_Pin_0
#define DHT11_GPIO GPIOD
#define DHT11_PIN GPIO_Pin_0
#define DHT11_CLOCK RCC_APB2Periph_GPIOD

//端口初始化
void DHT11_GPIO_Init(void);

//复位
void DHT11_Reset(void);

//接收应答 5个byte
void DHT11_RcvData(u8 *data);

//接收应答 1个byte
u8 DHT11_RcvByte(void);

//接收应答 1个bit
u8 DHT11_RcvBit(void);

//应答处理 返回0没收到正确应答,返回1接收到正确应答
u8 DHT11_Ack(void);
#endif

usart.c 和 delay.c我这里不再展示,前面的课程配置过很多次了。

代码运行结果

知识点4【代码所犯错误】

1、在复位的时候,是先拉低18ms以上,写代码途中配置成us。

**2、**在下面代码中,while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == SET);

忽略掉这个,默认引脚是上拉输入,也会进行数据采集,因此出现了采集错误的现象,这个很难差错,希望大家能够避免这个坑。(重要)

//接收应答 1个bit
u8 DHT11_RcvBit(void)
{
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == SET);
	
	if(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == RESET)
	{
		while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == RESET);
		Delay_us(40);
		if(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN) == SET)
		{
			return 1;
		}
		else
		{
			return 0;
		}
	}
	return 1;
}

补充拓展

1、使用定时器进行周期性的采样

但是我们知道,在调用TIM_Cmd(TIMx, ENABLE); 的时候,是以下的流程:

  • 重新装载预分频器
  • 如果 ARR 预装载打开,还会把新 ARR 写入实际计数寄存器
  • 同时置位更新中断标志位 UIF

这个过程就说明,触发一次update中断。中断函数中执行的是数据采集工作。

这时候会出现另一个问题

我们上面提过:第一次采集的空闲状态需要至少2s,让我们的传感器完成加热,确保数据采集的正确性。

那么这个第一个中断就势必要关闭

思路

  1. 先开定时器,不使能更新中断
  2. 清除一次 UIF 标志
  3. 再使能更新中断并开 NVIC

代码演示:

// 1. 配置好 TIMx 的时基单元(TIM_TimeBaseInit)……
// 2. 开启时钟、初始化 NVIC 中断优先级(但不使能)

// 不开中断,先使能定时器产生一次 UEV 并清掉标志
TIM_Cmd(TIMx, ENABLE);
// 清除可能残留的 UIF 标志
TIM_ClearFlag(TIMx, TIM_FLAG_Update);

// 现在再开更新中断
TIM_ITConfig(TIMx, TIM_IT_Update, ENABLE);
NVIC_EnableIRQ(TIMx_IRQn);

主要内容我讲完了,这里实现定时器 定时触发 数据采集中断就很简单了,大家可以当作一个小练习,自己尝试一下。

2、代码健壮性的补充

以我们在void DHT11_Reset(void); 为例

//等待引脚电平被拉低(等待DHT11的应答)
while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN));

如果一直是高电平,就会阻塞,会影响程序的正常执行。

因此这里我们可以加入判断

	//等待引脚电平被拉低(等待DHT11的应答)
	int time = 0;
	while(GPIO_ReadInputDataBit(DHT11_GPIO,DHT11_PIN))
	{
		time++;
		Delay_us(1);
		if(time < 100)
		{
			return 0;
		}
	}

这样配置即可避免阻塞的情况发生。

结束

代码重在练习!

代码重在练习!

代码重在练习!

今天的分享就到此结束了,希望对你有所帮助,如果你喜欢我的分享,请点赞收藏加关注,谢谢大家!!!

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

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

相关文章

【android bluetooth 协议分析 01】【HCI 层介绍 8】【ReadLocalVersionInformation命令介绍】

1. HCI_Read_Local_Version_Information 命令介绍 1. 功能&#xff08;Description&#xff09; HCI_Read_Local_Version_Information 命令用于读取本地 Bluetooth Controller 的版本信息&#xff0c;包括 HCI 和 LMP 层的版本&#xff0c;以及厂商 ID 和子版本号。 这类信息用…

esp32课设记录(四)摩斯密码的实现 并用mqtt上传

摩斯密码(Morse Code)是一种通过点(.)和划(-)组合来表示字符的编码系统。下面我将在esp32上实现摩斯密码的输入&#xff0c;并能够发送到mqtt的broker。 先捋一下逻辑&#xff0c;首先esp32的按键已经编写了短按与长按功能&#xff0c;这将是输出摩斯密码点和划的基础。然后当2…

「HHT(希尔伯特黄变换)——ECG信号处理-第十三课」2025年5月19日

一、引言 心电信号&#xff08;ECG&#xff09;是反映心脏电活动的重要生理信号&#xff0c;其特征提取对于心脏疾病的诊断和监测具有关键意义。Hilbert - Huang Transform&#xff08;HHT&#xff09;作为一种强大的信号处理工具&#xff0c;在心电信号特征提取领域得到了广泛…

前端(vue)学习笔记(CLASS 6):路由进阶

1、路由的封装抽离 将之前写在main.js文件中的路由配置与规则抽离出来&#xff0c;放置在router/index.js文件中&#xff0c;再将其导入回main.js文件中&#xff0c;即可实现路由的封装抽离 例如 //index.js import { createMemoryHistory, createRouter } from vue-routerim…

GPT-4.1特点?如何使用GPT-4.1模型,GPT-4.1编码和图像理解能力实例展示

几天前&#xff0c;OpenAI在 API 中推出了三个新模型&#xff1a;GPT-4.1、GPT-4.1 mini 和 GPT-4.1 nano。这些模型的性能全面超越 GPT-4o 和 GPT-4o mini(感觉这个GPT-4.1就是GPT-4o的升级迭代版本)&#xff0c;主要在编码和指令跟踪方面均有显著提升。还拥有更大的上下文窗口…

写一段图片平移的脚本

问题描述&#xff1a; 写一段图片平移的脚本。 平移就是将对象换一个位置。如果你要沿方向移动&#xff0c;移动的距离是&#xff0c;你可以以下面的方式构建移动矩阵&#xff1a;。 你可以使用Numpy 数组构建这个矩阵&#xff08;数据类型是np.float32&#xff09;&#xf…

【C++】哈希的概念与实现

1.哈希概念 通过某种函数使元素的存储位置与它的关键码之间能够建立一一映射的关系&#xff0c;可以不经过任何比较&#xff0c;一次直接从表中得到要搜索的元素。 当向该结构中&#xff1a; 插入元素&#xff1a; 根据待插入元素的关键码&#xff0c;以此函数计算出该元素的…

Yocto和Buildroot功能和区别

一.介绍 Yocto 和 Buildroot 都是用于嵌入式 Linux 系统开发的工具集&#xff0c;它们的目的是帮助开发者轻松构建定制的 Linux 系统镜像&#xff0c;以便在嵌入式设备上运行。 二.对比 1.Yocto Yocto 是一个开源的嵌入式 Linux 构建系统&#xff0c;它允许开发者创建自定义…

详解RabbitMQ工作模式之发布订阅模式

目录 发布订阅模式 概念 概念介绍 特点和优势 应用场景 注意事项 代码案例 引入依赖 常量类 编写生产者代码 编写消费者1代码 运行代码 发布订阅模式 概念 RabbitMQ的发布订阅模式&#xff08;Publish/Subscribe&#xff09;是一种消息传递模式&#xff0c;它允许消…

微信学习之导航功能

先看这个功能的效果&#xff1a; 然后开始学习吧。 一、我们这里用的是vant的Grid控件&#xff0c;首先我们导入&#xff1a; { "usingComponents": {"van-search": "vant/weapp/search/index","my-swiper":"../../components…

城市内涝监测预警系统守护城市安全

一、系统背景 城市内涝是指由于强降水或连续性降水超过城市排水能力&#xff0c;导致城市内产生积水灾害的现象。随着气候变化和城市化进程的加快&#xff0c;城市内涝现象愈发频繁和严重。传统的城市排水系统已难以满足当前的城市排水需求&#xff0c;特别是在暴雨等极端天气条…

用 CodeBuddy 搭建「MiniGoal 小目标打卡器」:一次流畅的 UniApp 开发体验

我正在参加CodeBuddy「首席试玩官」内容创作大赛&#xff0c;本文所使用的 CodeBuddy 免费下载链接&#xff1a;腾讯云代码助手 CodeBuddy - AI 时代的智能编程伙伴 在日常生活中&#xff0c;我们总是希望能够坚持一些小习惯&#xff0c;比如每天锻炼十分钟、读一页书、早睡十分…

Web技术与Nginx网站环境部署

目录 一.web基础 1.域名和DNS &#xff08;1&#xff09;.域名的概念 &#xff08;2&#xff09;.hosts文件 &#xff08;3&#xff09;.DNS &#xff08;4&#xff09;.域名注册 2.网页与HTML &#xff08;1&#xff09;.网页简介 &#xff08;2&#xff09;.HTML &a…

AI移动监测:仓储环境安全的“全天候守护者”

AI移动监测在仓储方面的应用&#xff1a;技术赋能与场景突破 一、背景&#xff1a;仓储环境的“隐形威胁”与AI破局 仓储行业长期面临设备损坏、货物损失、卫生隐患等风险。传统监控依赖人工巡检或固定摄像头&#xff0c;难以实时捕捉动态风险。例如&#xff1a; 动物入侵&a…

2025年5月华为H12-821新增题库带解析

IS-IS核心知识 四台路由器运行IS-IS且已经建立邻接关系&#xff0c;区域号和路由器的等级如图中标记&#xff0c;下列说法中正确的有? R2和R3都会产生ATT置位的Level-1的LSPR1没有R4产生的LSP&#xff0c;因此R1只通过缺省路由和R4通信R2和R3都会产生ATT置位的Leve1-2的LSPR2和…

用 python 编写的一个图片自动分类小程序(三)

图片自动分类识别小程序记录 2025/5/18 0:38修改程序界面&#xff0c;增加一些功能 用 python 编写的一个图片自动识别分类小程序。 操作系统平台&#xff1a;Microsoft Windows 11 编程语言和 IDE&#xff1a;python 3.10 Visual studio code 一&#xff1a;图片自动分…

初识 java

目录 前言 一、jdk&#xff0c;JRE和JVM之间的关系 二、JVM的内存划分 前言 初步了解 jdk&#xff0c;JRE&#xff0c;JVM 之间的关系&#xff0c;JVM 的内存划分。 一、jdk&#xff0c;JRE和JVM之间的关系 jdk 是 java 开发工具集&#xff0c;包含JRE&#xff1b; JRE 是…

frida 配置

1.环境 1.1 下载 frida-server firda-server github下载地址 这边推荐使用最新版的上一个版本 根据虚拟机自行选择版本 我使用这个版本 frida-server-16.7.17-android-x86_64 1.2 启动 frida-server-16.7.17-android-x86_64 将文件解压至虚拟机目录 使用adb命令执行 chmo…

16-看门狗和RTC

一、独立看门狗 1、独立看门狗概述 在由单片机构成的微型计算机系统中&#xff0c;由于单片机的工作常常会受到来自外界电磁场的干扰&#xff0c;造成程序的跑飞&#xff08;不按照正常程序进行运行&#xff0c;如程序重启&#xff0c;但是如果我们填加看门狗的技术&#xff0…

【AI论文】用于评估和改进大型语言模型中指令跟踪的多维约束框架

摘要&#xff1a;接下来的指令评估了大型语言模型&#xff08;LLMs&#xff09;生成符合用户定义约束的输出的能力。 然而&#xff0c;现有的基准测试通常依赖于模板化的约束提示&#xff0c;缺乏现实使用的多样性&#xff0c;并限制了细粒度的性能评估。 为了填补这一空白&…