ESP32驱动DHT11检测温湿度(ESP IDF环境)

DHT11驱动原理

1.1接线:我使用的是ESP32 Wrover,DHT11接线也不复杂,总共三根线,供电电压为3.3V-5V,DATA端就随便接一个IO口即可。

官方使用说明上写着data端上拉5K电阻,而我没有上拉,也能实现数据读取。下面介绍该模块的时序逻辑。

1.2时序图

        用户MCU发送一次开始信号后,DHT11从低功耗模式转换到高速模式,等待主 机开始信号结束后,DHT11发送响应信号,送出40bit的数据,并触发一次信号采集。这个过程主要分为三大步,分别是①单片机开始信号的发送,②检测DHT11的响应信号,③读取温湿度数据。

        那要怎样发送开始信号才能让DHT11模块知道该把数据送到单片机呢?主要分下面几步即可完成开始信号的发送:

(1)将data连接的IO口设置为拉低输出状态,这个过程要大于18ms,那么直接采用延时函数延时个二十多毫秒即可。

(2)在完成第一步之后紧接着把IO口配置为高电平输出,这一步不需要延时处理

(3)完成上面两步后将IO口配置为输入状态,仅仅只是输入,电平状态可以不管,此过程持续20us-40us,我选择延时30us。示例代码如下:                                                                                  

    OutputLow();//低电平输出
    Delay_ms(25);  //>18MS
    OutputHigh();//高电平输出
    InputInitial(); //输入
    ets_delay_us(30);

         下面介绍如何检测DHT11的响应信号,在我们向DHT11发送开始信号后,DHT11就开始回应一个响应信号,这个信号由80us的低电平和80us的高电平构成,响应信号结束就开始发送温湿度数据。那么我们如何让单片机知道什么时候去读数据呢?

        (1)那我们就去检测IO口的电平值,响应信号不是先低后高吗,我就先判断DHT11是否拉低了IO口电平,如果我读到的IO口电平为高,那说明DHT11根本就没响应,要么传感器坏了,要么开始信号没发送成功。

        (2)在上一步检测到IO口的低电平之后,我们需要等待响应信号的低电平结束,那么直接用一个while循环,循环中我们不断检测端口电平,一旦端口电平变高就跳出循环。

        (3)低电平结束之后迎来高电平信号,用同样的方法等待80us的高电平过去,高电平结束再等待个20—40us即可检测温湿度数据

if(!getData())//表示传感器拉低总线
    {
        ucharFLAG=2;
        //等待低电平结束
        while((!getData())&&ucharFLAG++) 
          ets_delay_us(10);
        //等待高电平结束
        while((getData())&&ucharFLAG++) 
         ets_delay_us(40);
    }
    else //没用成功读取,返回0
    {
    	Humi=0,
    	Temp=0;
        printf("ReadError");
    }

        总算来到第三步,开始读数据了,因为DHT11会发送40位的数据。

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

这40位的数据每一位的0/1状态又是怎么确定的呢?下图就是数字0和1的信号表示方法

         因为温湿度数据由四个8bit的数据构成,那我们就读每8位读一次,总共读5次,最后一次获取的是校验和。从上图我们可以看出不管是0信号还是1信号都有低电平+高电平表示,而且低电平持续时间相同,都是50us,那么就要求我们从高电平持续的时间来判断0/1信号。

        具体做法是(1)利用for循环,循环8次,每次读1位数据,每一位数据都由低电平开始,高电平结束。所以我们要等,等这一位数据的低电平部分过去,还是用while循环嵌套电平检测去做,检测到高电平就跳出循环。

        (2)当检测到这一位数据的高电平后,我们要看这段高电平究竟会持续多长时间,0信号的高电平持续26-28us,1信号则持续70us。所以在上一步检测到高电平之后我们不妨等一等,等个30us看看高电平是不是还是存在,若还是高电平,那这一位信号就是1信号,若已经变低了,就是0信号,将这个1或0赋值给一个变量。

        (3)上面读到了一位数据,我们要读8次才行,那要怎样存放变量呢,答案是左移一位再或上你刚读到的这一位数据,这样进行8次就能把一个温湿度部分读完。

static void COM(void)    // 温湿写入
{
    uchar i=0;
    for(i=0;i<8;i++)
    {
      
        //等待IO口变低,变低后,通过延时去判断是0还是1
        while((getData()==0)&&ucharFLAG++) ;
        ets_delay_us(35);//延时35us
        uchartemp=0;
 
        //如果这个位是1,35us后,还是1,则该位为1
        if(getData()==1) 
         {
            uchartemp=0x01;
         } 
        else
        {
            uchartemp=0x00;
        }
         
        ucharcomdata<<=1;//左移1位
        ucharcomdata|=uchartemp;//或上刚读到的值
        //等待IO口变高,变高后,表示可以读取下一位
        while((getData()==1)&&ucharFLAG++)
          ets_delay_us(10);

  
    }
}

 其实还有一步,那就是每8位读一次,读5次

 COM();//读取第1字节,
        ucharRH_data_H_temp=ucharcomdata;
        COM();//读取第2字节,
        ucharRH_data_L_temp=ucharcomdata;
        COM();//读取第3字节,
        ucharT_data_H_temp=ucharcomdata;
        COM();//读取第4字节,
        ucharT_data_L_temp=ucharcomdata;
        COM();//读取第5字节,
        ucharcheckdata_temp=ucharcomdata;
        OutputHigh();
        //判断校验和是否一致
        uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_temp+ucharRH_data_L_temp);
        if(uchartemp==ucharcheckdata_temp)
        {
            //校验和一致,
            ucharRH_data_H=ucharRH_data_H_temp;
            ucharRH_data_L=ucharRH_data_L_temp;
            ucharT_data_H=ucharT_data_H_temp;
            ucharT_data_L=ucharT_data_L_temp;
            ucharcheckdata=ucharcheckdata_temp;
            //保存温度和湿度
            Humi=ucharRH_data_H;//湿度高8位
            Humi_small=ucharRH_data_L;//湿度低8位
            Temp=ucharT_data_H;//温度高8位
            Temp_small=ucharT_data_L;//温度低8位

            
            mytemp = ucharT_data_H;
            mytemp = ((uint16)mytemp << 8 | ucharT_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
            myhumi = ucharRH_data_H;
            myhumi = ((uint16)myhumi << 8 | ucharRH_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整

            printf("temprature=%.2f  humidity=%.2f%%RH \r\n",mytemp,myhumi);//打印至上位机
        }

代码实现  

        该代码源于(23条消息) esp32 采集dht11温湿度数据_Joker2019.-CSDN博客_esp32读取dht11

         十分感谢这位大佬,我的代码大部分来源于他的代码,但他的代码我试过不对,打印出来的值是有问题的,问题在于他将四段数据分割存放,这就导致数据位权发生改变。在同事唐兄的帮助下(真心感谢唐兄),我终于理清了思路。下面这段代码至关重要,我看了很多帖子,要不就是用传感器库,要不遮遮掩掩,贴几行啥也不是的代码让你抓耳挠腮。他们的方法我发送到上位机后数据都不对,下面是唐兄教我写的:

mytemp = ucharT_data_H;
mytemp = ((uint16)mytemp << 8 | ucharT_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整
myhumi = ucharRH_data_H;
myhumi = ((uint16)myhumi << 8 | ucharRH_data_L)/10;//左移8位再或上低8位得到完整数据,再除以10得到最终结果,有零有整

printf("temprature=%.2f  humidity=%.2f%%RH \r\n",mytemp,myhumi);//打印至上位机

下面我贴上我的完整代码,只写了一页,没其他.c文件。说是我自己的代码也不准确,因为我也是东拼西凑搞的,十分感谢大家看到这里。

#include <stdio.h>
#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "esp_err.h"
#include "nvs_flash.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/ledc.h"
#include <stdio.h>
#include "driver/uart.h"
#include "driver/gpio.h"
#include "string.h"

#define DHT11_PIN   21//定义DHT11的引脚

#define uchar unsigned char
#define uint8 unsigned char
#define uint16 unsigned short

//温湿度定义
uchar ucharFLAG,uchartemp;
float Humi,Temp;
uchar ucharT_data_H,ucharT_data_L,ucharRH_data_H,ucharRH_data_L,ucharcheckdata;
uchar ucharT_data_H_temp,ucharT_data_L_temp,ucharRH_data_H_temp,ucharRH_data_L_temp,ucharcheckdata_temp;
uchar ucharcomdata;

static void InputInitial(void)//设置端口为输入
{
  gpio_pad_select_gpio(DHT11_PIN);
  gpio_set_direction(DHT11_PIN, GPIO_MODE_INPUT);
}

static void OutputHigh(void)//输出1
{
  gpio_pad_select_gpio(DHT11_PIN);
  gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT);
  gpio_set_level(DHT11_PIN, 1);
}

static void OutputLow(void)//输出0
{
  gpio_pad_select_gpio(DHT11_PIN);
  gpio_set_direction(DHT11_PIN, GPIO_MODE_OUTPUT);
  gpio_set_level(DHT11_PIN, 0);
}

static uint8 getData()//读取状态
{
	return gpio_get_level(DHT11_PIN);
}

//读取一个字节数据
static void COM(void)    // 温湿写入
{
    uchar i;
    for(i=0;i<8;i++)
    {
        ucharFLAG=2;
        //等待IO口变低,变低后,通过延时去判断是0还是1
        while((getData()==0)&&ucharFLAG++) ets_delay_us(10);
        ets_delay_us(35);//延时35us
        uchartemp=0;

        //如果这个位是1,35us后,还是1,否则为0
        if(getData()==1)
          uchartemp=1;
        ucharFLAG=2;

        //等待IO口变高,变高后,表示可以读取下一位
        while((getData()==1)&&ucharFLAG++)
          ets_delay_us(10);
        if(ucharFLAG==1)
          break;
        ucharcomdata<<=1;
        ucharcomdata|=uchartemp;
    }
}

void Delay_ms(uint16 ms)
{
	int i=0;
	for(i=0; i<ms; i++){
		ets_delay_us(1000);
	}
}

void DHT11(void)   //温湿传感启动
{
    OutputLow();
    Delay_ms(19);  //>18MS
    OutputHigh();
    InputInitial(); //输入
    ets_delay_us(30);
    if(!getData())//表示传感器拉低总线
    {
        ucharFLAG=2;
        //等待总线被传感器拉高
        while((!getData())&&ucharFLAG++)
          ets_delay_us(10);
        //等待总线被传感器拉低
        while((getData())&&ucharFLAG++)
          ets_delay_us(10);
        COM();//读取第1字节,
        ucharRH_data_H_temp=ucharcomdata;
        COM();//读取第2字节,
        ucharRH_data_L_temp=ucharcomdata;
        COM();//读取第3字节,
        ucharT_data_H_temp=ucharcomdata;
        COM();//读取第4字节,
        ucharT_data_L_temp=ucharcomdata;
        COM();//读取第5字节,
        ucharcheckdata_temp=ucharcomdata;
        OutputHigh();
        //判断校验和是否一致
        uchartemp=(ucharT_data_H_temp+ucharT_data_L_temp+ucharRH_data_H_temp+ucharRH_data_L_temp);
        if(uchartemp==ucharcheckdata_temp)
        {
            //校验和一致,
            ucharRH_data_H=ucharRH_data_H_temp;
            ucharRH_data_L=ucharRH_data_L_temp;
            ucharT_data_H=ucharT_data_H_temp;
            ucharT_data_L=ucharT_data_L_temp;
            ucharcheckdata=ucharcheckdata_temp;
            //保存温度和湿度
            Humi=ucharRH_data_H;
            Humi=((uint16)Humi<<8|ucharRH_data_L)/10;

            Temp=ucharT_data_H;
            Temp=((uint16)Temp<<8|ucharT_data_L)/10;
        }
        else
        {
          Humi=100;
          Temp=100;
        }
    }
    else //没用成功读取,返回0
    {
    	Humi=0,
    	Temp=0;
    }

    OutputHigh(); //输出
}


void app_main()
{
    char dht11_buff[50]={0};

    while(1)
    {
      DHT11(); //读取温湿度
      printf("Temp=%.2f--Humi=%.2f%%RH \r\n", Temp,Humi);
      vTaskDelay(300);  //延时300毫秒
    }
}

效果图

版权声明:本文为CSDN博主「Changerking」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Changerking/article/details/122366815

生成海报
点赞 0

Changerking

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

4pin oled字模,oled图片编码生成方法

在制作手工的时候,选用了一款4pin 的OLED单色显示屏,在取字模的时候遇到一些问题,特此做一下记录,对于也遇到这方面问题的朋友,也可以提供一些思路。 所选用的4pin

CC2530 常用寄存器

一、端口初始化相关寄存器 代码示例:用按键1和按键2分别控制D4和D6灯 点我跳转 二、INT相关寄存器 代码示例:利用key1,key2来产生中断控制D3,D5灯,key1,key2为下降沿触

【Arduino实验12 1602 LCD显示】

目录 一、实验目的 二、实验设备与环境 三、实验重点 四、实验难点 五、实验内容 5.1实验任务 5.2实验原理 5.3实验内容 5.4实验结果   5.5思考题 一、实验目的 (1)熟悉1602LCD液晶显示模块的功能 (