【正点原子STM32连载】 第四十三章 DHT11数字温湿度传感器 摘自【正点原子】STM32F103 战舰开发指南V1.2

news2024/11/19 16:37:40

1)实验平台:正点原子stm32f103战舰开发板V4
2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420
3)全套实验源码+手册+视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第四十三章 DHT11数字温湿度传感器

本章,我们将介绍数字温湿度传感器DHT11的使用,与前一章的温度传感器相比,该传感器不但能测温度,还能测湿度。我们将学习如何获取DHT11传感器的温湿度数据,并把数据显示在LCD上。
本章分为如下几个小节:
43.1 DHT11及工作时序简介
43.2 硬件设计
43.3 程序设计
43.4 下载验证

43.1 DHT11及工作时序简介
43.1.1 DHT11简介
DHT11是一款温湿度一体化的数字传感器。该传感器包括一个电容式测湿元件和一个NTC测温元件,并与一个高性能8位单片机相连接。通过单片机等微处理器简单的电路连接就能够实时的采集本地湿度和温度。DHT11与单片机之间能采用简单的单总线进行通信,仅仅需要一个I/O口。传感器内部湿度和温度数据40Bit的数据一次性传给单片机,数据采用校验和方式进行校验,有效的保证数据传输的准确性。DHT11功耗很低,5V电源电压下,工作平均最大电流0.5mA。
DHT11的技术参数如下:
工作电压范围:3.3V ~ 5.5V
工作电流:平均0.5mA
输出:单总线数字信号
测量范围:湿度5 ~ 95%RH,温度-20 ~ 60℃
精度:湿度±5%,温度±2℃
分辨率:湿度1%,温度0.1℃
DHT11的管脚排列如图43.1.1所示:
在这里插入图片描述

图43.1.1.1 DHT11管脚排列图
43.1.2 DHT11工作时序简介
虽然DHT11与DS18B20类似,都是单总线访问,但是DHT11的访问,相对DS18B20来说简单很多。下面我们先来看看DHT11的数据结构。
DHT11数字温湿度传感器采用单总线数据格式。即,单个数据引脚端口完成输入输出双向传输。其数据包由5byte(40bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先处。DHT11的数据格式为:8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数部分+8bit校验和。其中校验和数据为前面四个字节相加。
传感器数据输出的是未编码的二进制数据。数据(湿度、温度、整数、小数)之间应该分开处理。例如,某次从DHT11读到的数据如图43.1.2.1所示:
在这里插入图片描述

图43.1.2.1 某次读取到DHT11数据
由以上数据就可得到湿度和温度的值,计算方法:
湿度 = byte4 . byte3 = 45.0(%RH)
温度 = byte2 . byte1 = 28.0(℃)
校验 = byte4 + byte3 + byte2 + byte1 = 73 (= 湿度 + 温度) (校验正确)
可以看出,DHT11的数据格式十分简单的,DHT11和MCU的一次通信最大为34ms左右,建议主机连续读取时间间隔不要小于2s。
下面,我们介绍一下DHT11的传输时序。DHT11的数据发送流程如图43.1.2.2所示:
在这里插入图片描述

图43.1.2.2 DHT11数据发送流程图
首先主机发送开始信号,即:拉低数据线,保持t1(至少18ms)时间,然后拉高数据线t2(1035us)时间,然后读取DHT11的响应,正常的话,DHT11会拉低数据线,保持t3(7888us)时间,作为响应信号,然后DHT11拉高数据线,保持t4(80~92us)时间后,开始输出数据。
DHT11输出数字‘0’时序如图43.1.2.3所示:
在这里插入图片描述

图43.1.2.3 DHT11数字‘0’时序图
DHT11输出数字‘1’的时序如图43.1.2.4所示:
在这里插入图片描述

图43.1.2.4 DHT11输出数字‘1’时序图
DHT11输出数字‘0’和‘1’时序,一开始都是DHT11拉低数据线54us,后面拉高数据线保持的时间就不一样,数字‘0’就是2327us,而数字‘1’就是6874us。
通过以上了解,我们就可以通过STM32F103来实现对DHT11的读取了。DHT11的介绍就到这里,更详细的介绍,请参考DHT11数据手册。
43.2 硬件设计

  1. 例程功能
    本实验开机的时候先检测是否有DHT11存在,如果没有,则提示错误。只有在检测到DHT11之后才开始读取温湿度值并显示在LCD上,如果发现了DHT11,则程序每隔100ms左右读取一次数据,并把温湿度显示在LCD上。LED0闪烁用于提示程序正在运行。
  2. 硬件资源
    1)LED灯
    LED0 – PB5
    2)DHT11温湿度传感器 – PG11
    3)串口1(PA9/PA10连接在板载USB转串口芯片CH340上面)
    4)正点原子 2.8/3.5/4.3/7/10寸TFTLCD模块(仅限MCU屏,16位8080并口驱动)
  3. 原理图
    DHT11接口与STM32的连接关系跟上一章节中DS18B20和STM32的关系是一样的,使用到的GPIO口是PG11。这里原理图就不列出来了,可以翻看上一章节原理图。
    DHT11和DS18B20的接口是共用一个的,不过DHT11有4条腿,需要把U6的4个接口都用上,将DHT11传感器插入到这个上面就可以通过STM32F1来读取温湿度值了。连接示意图如图43.2.1所示:
    在这里插入图片描述

图43.2.1 DHT11连接示意图
这里要注意,将DHT11贴有字的一面朝内,而有很多孔的一面(网面)朝外,然后插入如图所示的四个孔内就可以了。
43.3 程序设计
DHT11实验中使用的是单总线协议,用到的是HAL中GPIO相关函数,前面也有介绍到,这里就不做展开了。下面介绍一下如何驱动DHT11。
DHT11配置步骤
1)使能DHT11数据线对应的GPIO时钟。
本实验中DHT11的数据线引脚是PG11,因此需要先使能GPIOG的时钟,代码如下:
__HAL_RCC_GPIOG_CLK_ENABLE(); /* PG口时钟使能 */
2)设置对应GPIO工作模式(开漏输出)
本实验GPIO使用开漏输出模式,通过函数HAL_GPIO_Init设置实现。
3)参考单总线协议,编写信号代码(复位脉冲、应答脉冲、读0/1)
复位脉冲:拉低数据线,保持至少18ms时间,然后拉高数据线10~35us时间。
应答脉冲:DHT11拉低数据线,保持78~88us时间。
读0/1信号:DHT11拉低数据线延时54us,然后拉高数据线延时一定时间,主机通过判断高电平时间得到0或者1。
4)编写DHT11的读函数
基于读1bit数据的基础上,编写DHT11读1字节函数。
5)编写DHT11获取温度函数
参考DHT11典型温湿度读取过程,编写获取温湿度函数。
43.3.1 程序流程图
在这里插入图片描述

图43.3.2.1 DHT11实验程序流程图
43.3.2 程序解析
1.DHT11驱动代码
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。DHT11驱动源码包括两个文件:dht11.c和dht11.h。
首先我们先看一下dht11头文件里面的内容,其定义如下:

/* DHT11 引脚 定义 */
#define DHT11_DQ_GPIO_PORT                  GPIOG
#define DHT11_DQ_GPIO_PIN                   GPIO_PIN_11
#define DHT11_DQ_GPIO_CLK_ENABLE()        
do{ __HAL_RCC_GPIOG_CLK_ENABLE(); }while(0)   /* PG口时钟使能 */
/* IO操作函数 */
#define DHT11_DQ_OUT(x)     do{ x ? \
     HAL_GPIO_WritePin(DHT11_DQ_GPIO_PORT,DHT11_DQ_GPIO_PIN, GPIO_PIN_SET):\
     HAL_GPIO_WritePin(DHT11_DQ_GPIO_PORT,DHT11_DQ_GPIO_PIN, GPIO_PIN_RESET); \
                                }while(0)                 /* 数据端口输出 */
/* 数据端口输入 */
#define DHT11_DQ_IN     HAL_GPIO_ReadPin(DHT11_DQ_GPIO_PORT, DHT11_DQ_GPIO_PIN)
对DHT11的相关引脚以及IO操作进行宏定义,方便程序中调用。
下面我们直接介绍dht11.c的程序,首先先介绍一下DHT11传感器的初始化函数,其定义如下:
/**
 * @brief     	初始化DHT11的IO口 DQ 同时检测DHT11的存在
 * @param     	无
 * @retval     	0, 正常
 *              	1, 不存在/不正常
 */
uint8_t dht11_init(void)
{
    GPIO_InitTypeDef gpio_init_struct;

    DHT11_DQ_GPIO_CLK_ENABLE();     /* 开启DQ引脚时钟 */

    gpio_init_struct.Pin = DHT11_DQ_GPIO_PIN;
    gpio_init_struct.Mode = GPIO_MODE_OUTPUT_OD;            	/* 开漏输出 */
    gpio_init_struct.Pull = GPIO_PULLUP;                    	/* 上拉 */
    gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;        	/* 高速 */
    HAL_GPIO_Init(DHT11_DQ_GPIO_PORT, &gpio_init_struct);	/* 初始化DQ引脚 */
/* DHT11_DQ引脚模式设置,开漏输出,上拉, 这样就不用再设置IO方向了, 开漏输出的时候(=1),
    也可以读取外部信号的高低电平 */

    dht11_reset();
    return dht11_check();
}

在DHT11的初始化函数中,主要对用到的GPIO口进行初始化,同时在函数最后调用复位函数和自检函数,这两个函数在后面会解释到。
下面介绍的是复位DHT11函数和等待DHT11的回应函数,它们的定义如下:

/**
 * @brief     	复位DHT11
 * @param     	data: 要写入的数据
 * @retval    	无
 */
static void dht11_reset(void)
{
    DHT11_DQ_OUT(0);   	/* 拉低DQ */
    delay_ms(20);       	/* 拉低至少18ms */
    DHT11_DQ_OUT(1);    	/* DQ=1 */
    delay_us(30);       	/* 主机拉高10~35us */
}

/**
 * @brief      	等待DHT11的回应
 * @param      	无
 * @retval    	0, DHT11正常
 *            	1, DHT11异常/不存在
 */
uint8_t dht11_check(void)
{
    uint8_t retry = 0;
    uint8_t rval = 0;

    while (DHT11_DQ_IN && retry < 100)	/* DHT11会拉低约83us */
    {
        retry++;
        delay_us(1);
    }
    if (retry >= 100)
    {
        rval = 1;
    }
    else
    {
        retry = 0;
        while (!DHT11_DQ_IN && retry < 100) /* DHT11拉低后会再次拉高87us */
        {
            retry++;
            delay_us(1);
        }
        if (retry >= 100) rval = 1;
    }
    
    return rval;
}

以上两个函数分别代表着前面所说的复位脉冲与应答信号,大家可以对比前面的时序图进行理解。那么在上一章DS18B20的实验中,也对复位脉冲以及应答信号进行了详细的解释,大家也可以对比理解。
DHT11与DS18B20有所不同,DHT11是不需要写函数,只需要读函数即可,下面我们看一下读函数:

/**
 * @brief       从DHT11读取一个位
 * @param       无
 * @retval      读取到的位值: 0 / 1
 */
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)    	/* 根据引脚状态返回 bit */
    {
        return 1;
    }
    else 
    {
        return 0;
    }
}

/**
 * @brief      	从DHT11读取一个字节
 * @param      	无
 * @retval    	读到的数据
 */
static uint8_t dht11_read_byte(void)
{
uint8_t i, data = 0;

    for (i = 0; i < 8; i++)        	/* 循环读取8位数据 */
    {
        data <<= 1;                 	/* 高位数据先输出, 先左移一位 */
        data |= dht11_read_bit();   	/* 读取1bit数据 */
}

    return data;
}

在这里dht11_read_bit函数从DHT11处读取1位数据,大家可以对照前面的读时序图进行分析,读数字0和1的不同,在于高电平的持续时间,所以这个作为判断的依据。dht11_read_byte函数就是调用一字节读取函数进行实现。
下面介绍读取温湿度函数,其定义如下:

/**
 * @brief       	从DHT11读取一次数据
 * @param       	temp: 温度值(范围:-20~60°)
 * @param       	humi: 湿度值(范围:5%~95%)
 * @retval      	0, 正常.
 *                	1, 失败
 */
uint8_t dht11_read_data(uint8_t *temp, uint8_t *humi)
{
    uint8_t buf[5];
uint8_t i;

dht11_reset();

    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])
        {
            *humi = buf[0];
            *temp = buf[2];
        }
    }
    else
    {
        return 1;
    }
    
    return 0;
}

读取温湿度函数也是根据时序图进行实现的,在发送复位信号以及应答信号产生后,即可以读取5Byte数据进行处理,校验成功即读取数据有效成功。
2. main.c代码
在main.c里面编写如下代码:

int main(void)
{
    uint8_t t = 0;
    uint8_t temperature;
    uint8_t humidity;

    HAL_Init();                            	/* 初始化HAL库 */
    sys_stm32_clock_init(RCC_PLL_MUL9);	/* 设置时钟, 72Mhz */
    delay_init(72);                        	/* 延时初始化 */
    usart_init(115200);                	/* 串口初始化为115200 */
    led_init();                             	/* 初始化LED */
lcd_init();                             	/* 初始化LCD */

    lcd_show_string(30, 50, 200, 16, 16, "STM32F103", RED);
    lcd_show_string(30, 70, 200, 16, 16, "DHT11 TEST", RED);
    lcd_show_string(30, 90, 200, 16, 16, "ATOM@ALIENTEK", RED);

    while (dht11_init()) /* DHT11初始化 */
    {
        lcd_show_string(30, 110, 200, 16, 16, "DHT11 Error", RED);
        delay_ms(200);
        lcd_fill(30, 110, 239, 130 + 16, WHITE);
        delay_ms(200);
}

    lcd_show_string(30, 110, 200, 16, 16, "DHT11 OK", RED);
    lcd_show_string(30, 130, 200, 16, 16, "Temp:  C", BLUE);
    lcd_show_string(30, 150, 200, 16, 16, "Humi:  %", BLUE);

    while (1)
    {
        if (t % 10 == 0) /* 每100ms读取一次 */
        {
            dht11_read_data(&temperature, &humidity);            	 /* 读取温湿度值 */
            lcd_show_num(30 + 40, 130, temperature, 2, 16, BLUE);/* 显示温度 */
            lcd_show_num(30 + 40, 150, humidity, 2, 16, BLUE);  	 /* 显示湿度 */
        }

        delay_ms(10);

        t++;

        if (t == 20)
        {
            t = 0;
            LED0_TOGGLE(); /* LED0闪烁 */
        }
    }
}

主函数代码比较简单,一系列硬件初始化后,如果DHT11初始化成功,那么在循环中调用dht11_get_temperature函数获取温湿度值,每隔100ms读取数据并显示在LCD上。
43.4 下载验证
假定DHT11传感器已经接上去正确的位置,将程序下载到开发板后,可以看到LED0不停的闪烁,提示程序已经在运行了。LCD显示当前的温度值的内容如图43.4.1所示:
在这里插入图片描述

图43.4.1 程序运行效果图
至此,本章实验结束。大家可以将本章通过DHT11读取到的温度值,和前一章的通过DS18B20读取到的温度值对比一下,看看哪个更准确?

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

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

相关文章

如何在 ZBrush 和 UE5 中创建精灵吟游诗人(P1)

小伙伴们大家好&#xff0c;今天 云渲染小编给大家带来的是CG艺术家Hugo Sena关于“精灵吟游诗人”项目背后的工作流程&#xff0c;讨论了角色身体、服装和竖琴的工作流程&#xff0c;并解释了如何在虚幻引擎 5 中设置灯光。篇幅较长&#xff0c;分为上下两篇&#xff0c;大家接…

导出csv列错乱

今天在导出CSV文件的时候文件的列错乱了。 场景是将数据库信息导出&#xff0c;采用的是CSV&#xff0c;比excel快点。然后数据需要经过处理后再导入到其他库&#xff0c;导入的时候部分文件解析出错了&#xff0c;原因就是CSV文件内容的列和导入映射的实体字段数量对不上。见…

把你的 Python 技能从 “Hello World“ 升级到 “万能钥匙“:掌握 Python 的输出、输入、数据类型转换和运算符!

前言 这篇文章我将为大家分享 python 的输出、输入、数据类型的转换和运算符 相关的知识。如果大家也想跟着博主一起学习 python &#xff0c;欢迎订阅专栏哦python学习&#x1f60a; 输出 我们都知道&#xff0c;要想知道程序的运行结果&#xff0c;就需要将结果给打印到屏…

EventLog Analyzer:保障企业等保合规的安全利器

企业等保合规是现代信息安全管理的重要组成部分。在信息化时代&#xff0c;企业面临着越来越多的网络安全威胁和合规要求。为了确保企业的信息系统安全可靠&#xff0c;以及满足相关法规和标准的要求&#xff0c;企业需要借助强大的安全事件管理工具。EventLog Analyzer作为一种…

【力扣】前缀和/滑动窗口:209. 长度最小的子数组

【力扣】前缀和/滑动窗口&#xff1a;209. 长度最小的子数组 文章目录 【力扣】前缀和/滑动窗口&#xff1a;209. 长度最小的子数组1. 问题2. 题解2.1 暴力法2.2 前缀和 二分查找2.3 滑动窗口 参考 1. 问题 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组…

SRGAN图像超分重建算法Python实现(含数据集代码)

摘要&#xff1a;本文介绍深度学习的SRGAN图像超分重建算法&#xff0c;使用Python以及Pytorch框架实现&#xff0c;包含完整训练、测试代码&#xff0c;以及训练数据集文件。博文介绍图像超分算法的原理&#xff0c;包括生成对抗网络和SRGAN模型原理和实现的代码&#xff0c;同…

c++重载函数和虚函数(重写)为什么分别在编译和运行时候确定

函数调用的本质&#xff1a; 函数调用实际上是执行函数体中的代码函数体是内存中的一个代码段函数名代表改代码段的首地址&#xff0c;函数执行时就从这里开始 所以执行一个函数的时候&#xff0c;需要知道具体的函数地址&#xff0c;才能执行函数 重载、重写和隐藏的区别 …

大家想知道ai写作宝怎么样吗?

ai写作是指利用人工智能技术自动生成文章、报告、新闻等文本内容的过程。它可以实现大规模、高效率、低成本的文本创作&#xff0c;免去人工编写的繁琐过程&#xff0c;节约了很多时间和精力。目前在文学创作、新闻报道、广告文案、社交媒体内容创作等方面&#xff0c;ai写作都…

mysql中将多行数据合并成一行数据

mysql中将多行数据合并成一行数据 一个字段可能对应多条数据&#xff0c;用mysql实现将多行数据合并成一行数据 例如&#xff1a;一个活动id&#xff08;activeId&#xff09;对应多个模块名&#xff08;modelName&#xff09;,按照一般的sql语句&#xff1a; SELECT am.act…

英伟达,不止于GPU

光子盒研究院 2013年至2023年这十年&#xff0c;有一家公司的市值翻了115倍、一跃成为美国股市回报率最高的公司——这就是英伟达。 黄仁勋出生于中国台湾&#xff0c;四岁随父母移民美国。1984年大学毕业&#xff0c;获得俄勒冈州立大学和斯坦福大学工程学位。 1993年&#xf…

《九》TypeScript 中的内置工具

TypeScript 内置了很多类型工具&#xff0c;来辅助进行类型转换。 Partial<Type>&#xff1a; Partial<Type>&#xff1a;用于构造一个所有属性都为可选属性的类型。 interface IPerson {name: stringage: number }// personOptional 类型的所有属性都是可选的 …

C++11【二】

文章目录 一、lambda表达式二、线程库三、包装器四、绑定&#xff08;不常用&#xff09; 一、lambda表达式 如果待排序元素为数据集合中的元素和自定义类型&#xff0c;需要用户定义排序时的比较规则不同如下代码&#xff0c;但都可以使用sort方法&#xff0c; #include <…

前端Vue3使用Moment Timezone处理不同时区时间

文章目录 前言一、基本时间概念UTC &#xff0c; GMT本地时间 为了方便比较&#xff0c;图中黑色背景的是 中国&#xff08;China&#xff09;区电脑&#xff0c;白色主题背景的是美国&#xff08;US&#xff09;区电脑&#xff09;2023-06-27T14:00:00.000Z 二、实际案例表单填…

正确认识:DOTA-E[c(RGDyK)2],DOTA标记序列多肽,双环肽螯合物

试剂基团反应特点&#xff08;Reagent group reaction characteristics&#xff09;&#xff1a; DOTA-E[c(RGDyK)2]为双环肽螯合物&#xff0c;DOTA标记序列多肽&#xff0c;螯合物前体多肽&#xff1a;放射性金属元素&#xff0c;例如&#xff1a; Cu、 Ga、 Lu、等可以通过与…

0基础入门---第四章---神经网络的学习

&#x1f31e;欢迎来到深度学习的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f64f;作者水平很有限&#xff0c;如果发现错误&#xff…

【突发小技巧】手动将jar包导入本地Maven仓库

1、下载jar包 以支付宝sdk为例&#xff1a;https://mvnrepository.com/artifact/com.alipay.sdk/alipay-sdk-java/4.33.12.ALL 后面执行mvn install命令(也就是安装命令&#xff0c;生命周期&#xff1a;编译、测试、打包、安装)&#xff0c;也会参考这个依赖坐标&#xff1a; …

前端安全 - 保护你的应用免受攻击的关键

80. 前端安全 - 保护你的应用免受攻击的关键 作为前端工程师&#xff0c;我们不仅需要关注用户界面的设计和功能实现&#xff0c;还需要关注应用程序的安全性。前端安全是保护我们的应用程序免受恶意攻击和数据泄露的重要方面。本文将介绍前端安全的概念、常见的安全威胁以及一…

Linux集群服务器上运行命令的4个实用工具

目录 1. PSSH-Parallel SSH 安装parallel-ssh&#xff0c; 使用 2. Pdsh-并行远程Shell实用程序 3. ClusterSSH 安装 4. Ansible 我们假设你已经设置好了SSH以便访问所有服务器;其次假设&#xff0c;同时访问多台服务器时&#xff0c;在所有Linux服务器上设置基于密钥的无…

clone浅拷贝

通过一个实例对象 生成另一个对象&#xff0c;称为 拷贝。 需要 实现 java.lang.Cloneable 该接口是一个 标记接口 通过调用 clone() 方法&#xff0c;完成 对象拷贝。该方法是Object类中的方法&#xff0c;所有对象都继承该方法。 clone()方法是 浅拷贝&#xff0c;也就是 只…

Vue+vite创建项目关于vite.config.js文件的配置

Vuevite创建项目关于vite.config.js文件的配置 Vue项目创建时&#xff0c;我们见过vue-cli 创建项目和webpack 创建项目等方式。 现在Vue 3版本使用npm/pnpm create vuelatest 创建项目&#xff0c;是搭配使用vite工具构建的。 创建完成的项目&#xff0c;最明显的去别就是&am…