STM32学习笔记【38.DHT11实验】

文章目录

  • 模块介绍:DHT11温湿度传感器
    • [1. DHT11介绍](#1. DHT11介绍)
      • [1.1 DHT11型号介绍](#1.1 DHT11型号介绍)
      • [1.2 DHT11工作参数及引脚介绍](#1.2 DHT11工作参数及引脚介绍)
    • [2. DHT11工作原理](#2. DHT11工作原理)
      • [2.1 正常工作验证](#2.1 正常工作验证)
      • [2.2 DHT11工作时序【与I2C类似】](#2.2 DHT11工作时序【与I2C类似】)
        • [2.2.1 DHT11整体工作时序](#2.2.1 DHT11整体工作时序)
        • [2.2.2 DHT11起始【复位】及响应信号](#2.2.2 DHT11起始【复位】及响应信号)
        • [2.2.3 读时序](#2.2.3 读时序)
      • [2.3 DHT11数据格式](#2.3 DHT11数据格式)
    • [3. DHT11编程实战](#3. DHT11编程实战)
      • [1. 搭建框架](#1. 搭建框架)
      • [2. 填写输入输出口函数内容](#2. 填写输入输出口函数内容)
      • [3. 起始信号](#3. 起始信号)
      • [4. 读取一个字节和所有数据的函数](#4. 读取一个字节和所有数据的函数)
      • [5. 主函数](#5. 主函数)
      • [6. 代码展示](#6. 代码展示)
      • [7. 实验结果](#7. 实验结果)

模块介绍:DHT11温湿度传感器

生活中经常使用温湿度数据,比如:天气预报、智能家居、智能大屏等等。这些数据可以通过温湿度传感器进行获取。在嵌入式开发中,温湿度传感器是一种十分常用的传感器。

1. DHT11介绍

DHT11(数字温湿度传感器)为3或4针单排引脚封装,连接方便。具有品质卓越、超快响应、抗干扰能力强、性价比高、超小体积、极低功耗的优点,使其称为在测温、测湿应用,在苛刻应用场合的不错选择。

DHT11内置一个电阻式感湿元件 和一个NTC测温元件 ,并与一个单片机相连接(DHT11内部)。每个DHT11都在极为精确的湿度校验室中进行校准,校准系数以程序的形式存在传感器中,传感器内部在检测信号的处理过程中要调用这些校准系数。DHT11采用简易快捷的单线制串行接收,方便系统集成。

1.1 DHT11型号介绍

DHT11有3脚和4脚两款,在使用上没有差别,接线都一样,主要接三根,四脚的款式有一脚悬空。四脚款接杜邦线会有点不稳,只能插面包板上,建议直接买三脚的。

同样可以测量温湿度的还有DHT20、DHT22等,都是大同小异。

DHT11虽然可以同时测量温湿度,但是测量范围没有专业测温传感器范围大。比如DS18B20专业测量温度的,温度范围是-55℃~-125℃,而DHT11只有0 ~ 50 ℃。

1.2 DHT11工作参数及引脚介绍

DHT11工作参数:

  1. 湿度测量范围:20 ~ 90%RH
  2. 湿度测量精度:±5%RH
  3. 温度测量范围:0 ~ 50℃
  4. 温度测量精度:±2℃
  5. 工作电压: DC 3.3V/5V

接线如下:正负极如果接反,会烧坏传感器

DHT11 STM32
VCC 3.3V/5V
DATA 任意一个GPIO口
GND GND

DHT11采用单总线协议,也就是使用一根DATA线进行数据的收发。

DHT11的DATA线一次通讯时间4ms左右,数据分整数部分、小数部分和校验位,具体为:8bit湿度整数数据 + 8bit温度小数数据 + 8bit校验位。

2. DHT11工作原理

2.1 正常工作验证

上电后,【电源指示灯/POWER】红灯亮,表示上电成功,正常工作。

2.2 DHT11工作时序【与I2C类似】

2.2.1 DHT11整体工作时序

DHT11整体工作时序为:主机发送开始信号、DHT11响应输出、主机输出40bit数据(湿度数据+温度数据+校验值),结束信号(可选)。

具体过程如下:

  1. 总线空闲状态为高电平,主机拉低总线等待DHT11响应,主机把总线拉低必须大于18ms,保证DHT11能检测到起始信号;
  2. 主机发送开始信号结束后,拉高总线电平并延时等待20 ~ 40us后,读取DHT11的响应信号;
  3. DHT11接收到主机的开始信号后,等待微处理器开始信号结束,发送80us低电平响应信号;
  4. DHT11发送80us高电平准备发送数据;
  5. DHT11发送40bit数据(湿度数据+温度数据+校验值)
2.2.2 DHT11起始【复位】及响应信号

首先主机拉低总线至少18ms,然后再拉高总线,延时20 ~ 40us,此时起始信号(有时也叫复位信号)发送完毕。

DHT11检测到复位信号,触发一次采样,并拉低总线80us表示响应信号,告诉主机数据已经准备好了。DHT11之后拉高总线80us,然后开始传输数据。如果检测到响应信号为高电平,则DHT11初始化失败,请检查线路是否连接正常。

2.2.3 读时序

DHT11开始传输数据。

每1 bit数据都以50us低电平开始,告诉主机开始传输一位数据了。

DHT11以高电平的长短定义数据位是0还是1:当50us低电平过后拉高总线,高电平持续26 ~ 28us表示0,高电平持续70us表示数据1。

当最后1 bit数据传送完毕后,DHT11拉低总线50us,表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。

位数据0表示方式:

以50us低电平开始,高电平持续26 ~ 28 us表示0

位数据1表示方式:

以50us低电平开始,高电平程序70us表示1 。50us时是低电平,图中画的不太准。

2.3 DHT11数据格式

DHT11的DATA传输一次完整的数据为40bit,按照高位在前,低位在后的顺序传输。

数据格式为:8bit湿度整数数据 + 8bit湿度小数数据 + 8bit温度整数数据 + 8bit温度小数数据 + 8bit校验位,一共5字节(40bit)数据。

校验位 = 前面几个bit数据加起来,放到校验位。

正常情况下,前四个字节的和刚好与校验位相等,通过这种机制可以保证数据传输的准确性。

判断如果前面几个bit数据加起来等于校验位,说明数据传输正确。

3. DHT11编程实战

1. 搭建框架

在串口打印功能实验基础上做,在BSP文件夹下建立dht11文件夹,以及.c和.h文件;

将dht11文件加载进来,以及头文件加载。

DHT11无初始化函数,因为DHT11只有一个IO口,既要作为输入口,也要作为输出口

dht11.c

c 复制代码
// 配置输入口
void dht11_gpio_input(void)
{
}
// 配置输出口
void dht11_gpio_output(void)
{
}
// 时序:起始信号
void dht11_start(void)
{
}
// 读数据, 读一个字节
uint8_t dht11_read_byte(void)
{
}
// 触发一次转换,读取所有数据
void dht11_read(uint8_t *result)
{
}

dht11.h

c 复制代码
#define DHT11_PORT             GPIOA
#define DHT11_PIN              GPIO_PIN_5
#define DHT11_CLK_ENABLE()     __HAL_RCC_GPIOA_CLK_ENABLE()

#define DHT11_DQ_OUT(x)        do { x ? \
                                    HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET) : \
                                    HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); \
                               }while(0)

#define DHT11_DQ_IN            HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)

2. 填写输入输出口函数内容

c 复制代码
// 配置输入口
void dht11_gpio_input(void)
{
    // 打开时钟
    DHT11_CLK_ENABLE();
    引脚FHT11_PIN
    模式是输入
    无上拉或下拉
    高速
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}
// 配置输出口
void dht11_gpio_output(void)
{
    // 打开时钟
    DHT11_CLK_ENABLE();
    引脚DHT11_PIN
    模式是推挽输出
    无上拉或下拉
    高速
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

3. 起始信号

c 复制代码
void dht11_start(void)
{
    dht11_gpio_output(); //引脚为输出模式
    DHT11_DQ_OUT(1/0);   //表示高低电平
    先是高电平
    拉低
    delay_ms(20); // 拉低至少18ms
    拉高,但不用管20 ~ 40 us

   // 主机拉高,等待从机,主动权交给从机
    dht11_gpio_input();  // 输入模式
    // 等待dht11总线拉低电平
    while(DHT11_DQ_IN); 
    // 等待DHT11拉高电平
    while(!DHT11_DQ_IN);

    // 等待dht11总线拉低电平
    while(DHT11_DQ_IN); 
}

4. 读取一个字节和所有数据的函数

逻辑上:

数据0:从高电平拉低50us,再拉高26 ~ 28 us

数据1:从高电平拉低50us,再拉高70 us

等待上升沿之后,再等待50us判断是数据0还是1?50us是高电平,表示数据1;如果是低电平,表示数据0

c 复制代码
char dht11_data[5] = {0};

uint8_t dht11_read_byte(void)
{
    uint8_t temp = 0;
    uint8_t i = 0;
    // 定义一个数据,将8位保存起来
    uint8_t read_data = 0;

    for(i = 0; i < 8; i++)
    {
       // 一个字节是8位,读8次
        while(!DHT11_DQ_IN);
        delay_us(50);
        if(DHT11_DQ_IN == 1)
        {
            temp = 1;
            while(DHT11_DQ_IN); 
        }
        else
           temp = 0;

       // 先移位,再把读到的数据插进去
       read_data = read_data  << 1;
       read_data |= temp;

       }
       return read_data;
}

void dht11_read(uint8_t *result)
{
    uint8_t i = 0;
    // 1.起始响应信号
    dht11_start();
    // 2.读数据,输入模式
    // 3. 循环读入5个数据,40bit数据
    for(i = 0; i < 5; i++)
        dht11_data[i] = dht11_read_byte();

    // 校验读出数据是否准确
    if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4])
    {
        // 将4个字节拷贝到result
        memcpy(result, dht11_data, 4);
        printf("湿度:%d.%dRH,", dht11_data[0], dht11_data[1]);
        printf("温度:%d.%d℃,", dht11_data[2], dht11_data[3]);
    
    }

        delay_ms(2000);
}

5. 主函数

main.c

c 复制代码
uint8_t dht11_result[4];
while(1)
{
   // 为了避免数据冲突,数据要清空
   memset(dht11_result, 0, 4);
   dht11_read(dht11_result);
}

6. 代码展示

dht11.c

c 复制代码
#include "dht11.h"
#include "delay.h"
#include "string.h"
#include "stdio.h"

char dht11_data[5] = {0};

void dht11_gpio_input(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    // 打开时钟
    DHT11_CLK_ENABLE();                            
    
    // 调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;               
    gpio_initstruct.Mode = GPIO_MODE_INPUT;        
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;  
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);

}

void dht11_gpio_output(void)
{
    GPIO_InitTypeDef gpio_initstruct;
    
    // 打开时钟
    DHT11_CLK_ENABLE();                           
    
    // 调用GPIO初始化函数
    gpio_initstruct.Pin = DHT11_PIN;              
    gpio_initstruct.Mode = GPIO_MODE_OUTPUT_PP;   
    gpio_initstruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(DHT11_PORT, &gpio_initstruct);
}

// 起始及响应信号
void dht11_start(void)
{
    dht11_gpio_output();
    DHT11_DQ_OUT(1);
    DHT11_DQ_OUT(0);
    delay_ms(20);
    DHT11_DQ_OUT(1);
    
    // 响应信号:等待拉高/拉低
    dht11_gpio_input();  // 输入模式
    while(DHT11_DQ_IN);  // 等待DHT11拉低电平
    while(!DHT11_DQ_IN);  // 等待DHT11拉高电平
    
    while(DHT11_DQ_IN); // 等待DHT11总线拉低电平

}

// 读时序[读取每个字节]
uint8_t dht11_read_byte(void)
{
    uint8_t temp = 0;
    uint8_t i = 0;
    
    // 定义一个数据,将8位保存起来
    uint8_t read_data = 0;
    
    // 读时序+读数据
    for(i = 0; i < 8; i++)
    {
        while(!DHT11_DQ_IN); // 期待变成上升沿
        delay_us(50);
        if(DHT11_DQ_IN == 1) // 如果是高电平,=1
        {
            temp = 1; // 数据1
            while(DHT11_DQ_IN); // 50us后,等待下降沿,拉低电平
        }
        else
            temp = 0;
        
        // 先移位,再把读到的数据插进去
        read_data = read_data << 1; // 左移1位,右边空出1位
        read_data |= temp;
    }

    return read_data;
}

// 读取每个数据,要有返回值接收
void dht11_read(uint8_t *result)
{
    uint8_t i = 0;
    // 1. 起始响应信号
    dht11_start();
    // 2. 读数据,输入模式
    dht11_gpio_input();
    // 3. 循环读入5个数据,40bit数据
    for(i = 0; i < 5; i++)
        dht11_data[i] = dht11_read_byte();

    // 校验读出数据是否准确
    if(dht11_data[0] + dht11_data[1] + dht11_data[2] + dht11_data[3] == dht11_data[4])
    {
        // 将4个字节拷贝到result
        memcpy(result, dht11_data, 4);
        printf("湿度:%d.%dRH,",dht11_data[0], dht11_data[1]);
        printf("温度:%d.%d℃,",dht11_data[2], dht11_data[3]);
    }

    delay_ms(2000);
}

dht11.h

c 复制代码
#ifndef __DHT11_H__
#define __DHT11_H__

#include "sys.h"


#define DHT11_PORT            GPIOA
#define DHT11_PIN             GPIO_PIN_5
#define DHT11_CLK_ENABLE()    __HAL_RCC_GPIOA_CLK_ENABLE()

// 输出口:IO口拉高拉低
#define DHT11_DQ_OUT(x)       do{ x ? \
                                  HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET) : \
                                  HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET); \
                                 } while(0)

// 输入口
#define DHT11_DQ_IN           HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)


void dht11_read(uint8_t *result);


#endif

main.c

c 复制代码
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "uart1.h"
#include "dht11.h"
#include "string.h"

uint8_t i = 0;

int main(void)
{
    HAL_Init();                         /* 初始化HAL库 */
    stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
    led_init();
    uart1_init(115200);
    printf("hello world! \r\n");
    
    uint8_t dht11_result[4];
    
    while(1)
    {
        // 为了避免数据冲突,数据要清空
        memset(dht11_result, 0, 4);
        dht11_read(dht11_result);
    }
}

7. 实验结果

如果用手捂住DHT11传感器,温湿度会变化。

相关推荐
深圳市泰凌微电子16 小时前
HK航顺 HK32F407RGT7替STM32F405RGT6 微控制器32位MCU
stm32·单片机·嵌入式硬件
caimouse16 小时前
MM 学习笔记 01:miarm.h 核心头文件分析
笔记·学习·reactos
毛丫讲绘本17 小时前
独立老师开始做绘本课,怎样做少走弯路?
人工智能·学习·微信·微信公众平台·微信开放平台
upper202017 小时前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Wyc7240917 小时前
agent第一周学习
学习
疯狂打码的少年19 小时前
【软件工程】软件测试阶段:单元测试与集成测试(高频对比考点)
笔记·单元测试·集成测试·软件工程
小秋求学记.19 小时前
第三天笔记
笔记
迷途呀20 小时前
CSDN Markdown 编辑器完全指南:从快捷键到高级图表
笔记·其他
栀栀栀栀栀栀20 小时前
刷刷刷 2026/7/16
笔记