【STM32 HAL库】DHT11温湿度传感器驱动详解

硬件开发板:STM32F407VET6
软件平台:cubemax+keil+VScode

1 DHT11工作原理

1.1 简介

DHT11温湿度传感器是一种数字式温湿度传感器,其工作原理基于集成了湿度感测元件和NTC温度感测元件的传感器模块。以下是DHT11温湿度传感器的工作原理:

  1. 湿度感测:DHT11传感器的湿度感测元件是一种具有高湿度灵敏度的电容式湿度传感器。它利用吸湿薄膜吸收环境中的水分,导致传感元件的电容值发生变化。当环境湿度增加时,吸湿薄膜吸收更多的水分,电容值增大;反之,当环境湿度减小时,电容值减小。通过测量电容值的变化,可以得知环境的湿度情况。

  2. 温度感测:DHT11传感器的温度感测元件是一颗NTC(Negative Temperature Coefficient)热敏电阻,其电阻值随温度的变化而变化。NTC热敏电阻的特性是随着温度升高电阻值减小,反之则增大。通过测量NTC热敏电阻的电阻值,可以推算出环境的温度情况。

  3. 信号处理:DHT11传感器内部集成了一个微控制器,用于采集湿度和温度感测元件的信号,并将其转换为数字信号输出。微控制器会对采集到的模拟信号进行A/D转换,并对其进行校准和处理,最终输出一个数字信号,表示环境的湿度和温度数值。

  4. 数字信号输出:DHT11传感器的输出是一串数字信号,其中包括湿度值和温度值。通常情况下,这些数字信号是通过单总线串行通信协议传输的,可以直接连接到微控制器或其他数字电路进行处理。

综上所述,DHT11温湿度传感器通过湿度感测元件和温度感测元件来实现对环境湿度和温度的检测,通过内部的信号处理和数字输出,将检测到的湿度和温度值以数字信号的形式输出,具有简单、精准、成本低廉等特点,在各种应用场景中得到了广泛的应用。
image

1.2 接线

image

建议连接线长度短于20米时用5K上拉电阻,大于20米时根据实际情况使用合适的上拉电阻
image

1.3 供电

DHT11的供电电压为 3-5.5V。传感器上电后,要等待 1s 以越过不稳定状态在此期间无需发送任何指令。电源引脚(VDD,GND)之间可增加一个100nF 的电容,用以去耦滤波

1.4 串行数据接口

DATA 用于微处理器与 DHT11之间的通讯和同步,采用单总线数据格式,一次通讯时间4ms左右,数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出

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

数据传送正确时校验和数据等于**“8bit湿度整数数据+8bit湿度小数数据+8bit温度整数数据+8bit温度小数数据”** 所得结果的末8位

1.5 时序与通信

image
用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集, 用户可选择读取部分数据.从模式下,DHT11接收到开始信号触发一次温湿度采集, 如果没有接收到主机发送开始信号,DHT11不会主动进行温湿度采集.采集数据后转换到低速模式。

image

  • 总线空闲状态为高电平

  • 主机把总线拉低等待DHT11响应,主机把总线拉低必须大于18毫秒,保证DHT11能检测到起始信号

  • DHT11接收到主机的开始信号后, 等待主机开始信号结束,然后发送80us低电平响应信号

  • 主机发送开始信号结束后,延时等待20-40us后, 读取DHT11的响应信号

  • 主机发送开始信号后,可以切换到输入模式,或者输出高电平均可, 总线由上拉电阻拉高

  • 总线为低电平,说明DHT11发送响应信号

  • DHT11发送响应信号后,再把总线拉高80us,准备发送数据(每一bit数据都以50us低电平时隙开始,高电平的长短定了数据位是0还是1)
    数字0信号表示方法:
    image
    数字1信号表示方法:
    image

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

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

  • 2 cubemx配置

    2.1 配置烧录环境

    image

    2.2 启用外部时钟

    image

    2.3 配置时钟树

    image
    注意这里的timer定时器时钟频率是84MHz

    2.4 配置串口

    打印调试输出
    image

    2.5 配置定时器

    依赖定时器提供微秒延时函数(建议选择32位定时器,这样可以避免计数溢出,保证延时的精确性):上面提到系统分配给timer的频率为84MHz,所以将预分频系数配置为84-1,就可以得到1MHz的定时器频率,即1有1000000个脉冲,每个脉冲周期为1us,每计一次数就是1us
    image

    2.6 配置结束,导出工程

    image

    image

    3 代码

    3.1 printf重定向

    打开keil魔术棒界面,使能微库
    image
    在usart.h中添加头文件和函数声明

    #include "stdio.h"
    
    int fputc(int ch,FILE *f);
    

    在usart.c中添加重定向函数

    //重定向printf
    int fputc(int ch,FILE *f)
    {
      uint8_t temp[1]={ch};
      HAL_UART_Transmit(&huart1,temp,1,2);
      return ch;
    }
    

    3.2 延时函数

    #include "tim.h"
    
    /**
     * ************************************************************************
     * @brief 基于32位定时器构造的微秒延时函数
     * 
     * @param[in] us  微秒值
     * @note 选择32位定时器,可防止计数值溢出,避免加大运算量
     * 
     * ************************************************************************
     */
    void my_delay_us(uint32_t us)
    {
        __HAL_TIM_SET_COUNTER(&htim5, 0);
        
        HAL_TIM_Base_Start(&htim5);
        
        while (__HAL_TIM_GET_COUNTER(&htim5) != us);
        
        HAL_TIM_Base_Stop(&htim5);
    }
    /**
     * ************************************************************************
     * @brief 基于32位定时器构造的毫秒延时函数
     * 
     * @param[in] ms  毫秒值
     * 
     * ************************************************************************
     */
    void my_delay_ms(uint32_t ms)
    {
        my_delay_us(ms * 1000);
    }
    

    3.3 DHT11驱动

    My_DHT11.h

    /**
     * ************************************************************************
     * 
     * @file My_DHT11.h
     * @author zxr
     * @brief 
     * 
     * ************************************************************************
     * @copyright Copyright (c) 2024 zxr 
     * ************************************************************************
     */
    #ifndef __MY_DHT11_H
    #define __MY_DHT11_H
    
    #include "stm32f4xx_hal.h"
    
    #define DHT11_PORT			GPIOA
    #define DHT11_PIN			GPIO_PIN_0
    
    #define DHT11_PULL_1		HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_SET)
    #define DHT11_PULL_0		HAL_GPIO_WritePin(DHT11_PORT, DHT11_PIN, GPIO_PIN_RESET)
    
    #define DHT11_ReadPin		HAL_GPIO_ReadPin(DHT11_PORT, DHT11_PIN)
    
    /**
     * ************************************************************************
     * @brief 存储传感器数据的结构体
     * 
     * 
     * ************************************************************************
     */
    typedef struct
    {
    	uint8_t humi_int;			// 湿度的整数部分
    	uint8_t humi_dec;	 		// 湿度的小数部分
    	uint8_t temp_int;	 		// 温度的整数部分
    	uint8_t temp_dec;	 		// 温度的小数部分
    	uint8_t check_sum;	 		// 校验和
    
    } DHT11_Data_TypeDef;
    
    
    uint8_t DHT11_ReadData(DHT11_Data_TypeDef* DHT11_Data);
    
    #endif
    
    

    My_DHT11.c

    /**
     * ************************************************************************
     * 
     * @file My_DHT11.c
     * @author zxr
     * @brief 
     * 
     * ************************************************************************
     * @copyright Copyright (c) 2024 zxr 
     * ************************************************************************
     */
    #include "My_DHT11.h"
    #include "My_Delay.h"
    
    /**
     * ************************************************************************
     * @brief 将DHT11配置为推挽输出模式
     * ************************************************************************
     */
    static void DHT11_PP_OUT(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct;
    	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);
    }
    
    /**
     * ************************************************************************
     * @brief 将DHT11配置为上拉输入模式
     * ************************************************************************
     */
    static void DHT11_UP_IN(void)
    {
    	GPIO_InitTypeDef GPIO_InitStruct;
    	GPIO_InitStruct.Pin = DHT11_PIN;
    	GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    	GPIO_InitStruct.Pull = GPIO_PULLUP;	//上拉
    	HAL_GPIO_Init(DHT11_PORT, &GPIO_InitStruct);
    }
    
    /**
     * ************************************************************************
     * @brief 读取字节
     * @return temp
     * ************************************************************************
     */
    uint8_t DHT11_ReadByte(void)
    {
    	uint8_t i, temp = 0;
    
    	for (i = 0; i < 8; i++)
    	{
    		while (DHT11_ReadPin == 0);		// 等待低电平结束
    		
    		my_delay_us(40);			//	延时 40 微秒
    		
    		if (DHT11_ReadPin == 1)
    		{
    			while (DHT11_ReadPin == 1);	// 等待高电平结束
    			
    			temp |= (uint8_t)(0X01 << (7 - i));			// 先发送高位
    		}
    		else
    		{
    			temp &= (uint8_t)~(0X01 << (7 - i));
    		}
    	}
    	return temp;
    }
    
    /**
     * ************************************************************************
     * @brief 读取一次数据
     * @param[in] DHT11_Data  定义的结构体变量
     * @return 0或1(数据校验是否成功)
     * @note 它首先向DHT11发送启动信号,然后等待DHT11的应答。如果DHT11正确应答,
     * 		 则继续读取湿度整数、湿度小数、温度整数、温度小数和校验和数据,
     * 		 并计算校验和以进行数据校验
     * ************************************************************************
     */
    uint8_t DHT11_ReadData(DHT11_Data_TypeDef *DHT11_Data)
    {
    	DHT11_PP_OUT();			// 主机输出,主机拉低
    	DHT11_PULL_0;	
    	my_delay_ms(18);				// 延时 18 ms
    	
    	DHT11_PULL_1;					// 主机拉高,延时 30 us
    	my_delay_us(30);	
    
    	DHT11_UP_IN();				// 主机输入,获取 DHT11 数据
    	
    	if (DHT11_ReadPin == 0)				// 收到从机应答
    	{
    		while (DHT11_ReadPin == 0);		// 等待从机应答的低电平结束
    		
    		while (DHT11_ReadPin == 1);		// 等待从机应答的高电平结束
    		
    		/*开始接收数据*/   
    		DHT11_Data->humi_int  = DHT11_ReadByte();
    		DHT11_Data->humi_dec = DHT11_ReadByte();
    		DHT11_Data->temp_int  = DHT11_ReadByte();
    		DHT11_Data->temp_dec = DHT11_ReadByte();
    		DHT11_Data->check_sum = DHT11_ReadByte();
    		
    		DHT11_PP_OUT();		// 读取结束,主机拉高
    		DHT11_PULL_1;	
    		
    		// 数据校验
    		if (DHT11_Data->check_sum == DHT11_Data->humi_int + DHT11_Data->humi_dec + DHT11_Data->temp_int + DHT11_Data->temp_dec)	
    		{
    			return 1;
    		}		
    		else
    		{
    			return 0;
    		}
    	}
    	else		// 未收到从机应答
    	{
    		return 0;
    	}
    }
    
    

    3.4 功能测试

    main.c

    #include "main.h"
    #include "tim.h"
    #include "usart.h"
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include "My_DHT11.h"
    #include "My_Delay.h"	//延时函数文件
    /* USER CODE END Includes */
    
    DHT11_Data_TypeDef DHT11_Data;  //定义结构体变量
    
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_TIM6_Init();
      MX_USART1_UART_Init();
      MX_TIM5_Init();
      /* USER CODE BEGIN 2 */
      printf("code start ok\n");
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      while (1)
      {
    	my_delay_ms(2000);
    	if (DHT11_ReadData(&DHT11_Data))
        {
          printf("湿度:%d RH ,温度:%d ℃ \n", DHT11_Data.humi_int, DHT11_Data.temp_int);
        }
        else
        {
          printf("Read DHT11 ERROR!\n");
        }
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
    
    

    串口打印

    image
    受到延时函数精度影响,观察时间戳发现略有误差

    作者:@Luminescence

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【STM32 HAL库】DHT11温湿度传感器驱动详解

    发表回复