zoukankan      html  css  js  c++  java
  • DHT11温湿度传感器编程思路以及代码的实现(转载)

    源自:https://blog.csdn.net/qq_34952376/article/details/81193938

    在我们刚开始进入单片机的学习中,练习写传感器的时序是必不可少的,其实我比较推荐大家刚开始练习的时候使用DHT11来练习。

    推荐的原因:

    1. 因为DHT11的时序简单。
    2. DHT11是国产的,全中文的参考手册,不用担心英文看不懂。
    3. 功能少,就只有一个测量温湿度的功能。

    综上,因此DHT11我认为是非常适合刚开始入门单片机的朋友学习的。

     

    那么在使用传感器前,我们必须要先看数据手册,并不需要全部浏览,我们只需要看他重要的点,就OK了。

    这就是DHT11温湿度传感器的外观,我们了解一下就好。

     

    接下来我们看一起DHT11的参数特性

    用红框圈出来的,就是重点,我们必须知道,这个DHT11温湿度传感器的测量范围,以及精度、分辨率。如果超出了这个范围,那么DHT11就不能够使用了。

    接下来看一下引脚说明。            我们要注意的是:这个器件使用的是单总线协议是总所周知的了,但是他的供电范围我们也需要了解一下,范围是3.5-5.5v。如果超出这个范围,传感器可能会烧、假如低于这个范围,可能传感器会读出错误的温湿度数据或者压根就罢工了。   因此,我们在使用那些3.3v单片机做编程的时候,就要注意这一点了。

    这个是数据手册上显示的DHT11典型的电路连接方法,我们再数据口上要接上一个上拉电阻。供电所使用的为7805的稳压电源,也就是5V。实际上,只要我们满足他的供电电压范围,都是能够工作的。

    接下来我们看一下它的数据格式(重点)

            DHT11用的是单总线协议,一次传送40位的数据。     注意了,看到这一句话,也就是说我们每次读取DHT11的数据时,都要一次性读取40次,也就是读取40位。并且数据前16位是与湿度相关的,中间16位是与温度相关的,最后八位是用来校验的,当我们校验成功后,证明这一次的温湿度结果正确的,我们单片机就可以使用这个温湿度值;如果校验不通过,那么就代表我们这次读取出来的温湿度值,是错误的(也许是我们的时序错误了,也许是传感器的问题),我们不进行采样。

            同时呢,商家的数据手册还给出了一个校验数据的示例图,而且还是全中文的,所以说我说的没错吧,这个器件是真的简单到不能再简单了,非常适合新手入门练习如何写时序。

            DHT11的总体通信流程。第一步:主机先发送开始信号,从机会返回一个相应信号进行应答。    第二步:主机信号线拉高准备接收数据。    第三部:开始接收数据(一次接收40位)。

    那么这个就是一个人数据读取的一个流程,那么我们每一个流程又应该怎么做呢?

     

    步骤一:DHT11 上电后(DHT11 上电后要等待 1S 以越过不稳定状态在此期间不能发送任何指令),测试环境
    温湿度数据,幵记录数据,同时 DHT11 的 DATA 数据线由上拉电阻拉高一直保持高电平;此时 DHT11 的
    DATA 引脚处于输入状态,时刻检测外部信号。

    步骤二:微处理器的 I/O 设置为输出同时输出低电平,且低电平保持时间不能小于 18ms,然后微处理器的 I/O
    设置为输入状态,由于上拉电阻,微处理器的 I/O 即 DHT11 的 DATA 数据线也随之变高,等待 DHT11 作
    出回答信号,发送信号如图所示:

    步骤三:DHT11 的 DATA 引脚检测到外部信号有低电平时,等待外部信号低电平结束,延迟后 DHT11 的 DATA
    引脚处于输出状态,输出 80 微秒的低电平作为应答信号,紧接着输出 80 微秒的高电平通知外设准备接
    收数据,微处理器的 I/O 此时处于输入状态,检测到 I/O 有低电平(DHT11 回应信号)后,等待 80 微秒
    的高电平后的数据接收,发送信号如图所示:
     

    步骤四:由 DHT11 的 DATA 引脚输出 40 位数据,微处理器根据 I/O 电平的变化接收 40 位数据,位数据“0”
    的格式为: 50 微秒的低电平和 26-28 微秒的高电平,位数据“1”的格式为: 50 微秒的低电平加 70
    微秒的高电平。位数据“0”、“1”格式信号如图所示:

    (我们可以把这一段的时序理解为,我们主机先把数据线拉低50us,然后延时等待40us,然后再去读取信号线的电平,如果为低电平,则为位“0”;如果为高电平,则为位“1”)。

    结束信号:DHT11 的 DATA 引脚输出 40 位数据后,继续输出低电平 50 微秒后转为输入状态,由于上拉电阻随
    之变为高电平。但 DHT11 内部重测环境温湿度数据,幵记录数据,等待外部信号的到来。

    我们在数据手册上了解的就这么多就可以了。

     

    同时,我自己也对DHT11的时序做了一个总结

    一. 单片机上点后1s内不读取(不重要)

    二. 主机(单片机)发送起始信号:1.主机先拉高data。2.拉低data延迟18ms。
                                    3.拉高data(单片机引脚设置为输入)。
                                    
    三. 从机(DHT11)收到起始信号后进行应答:
            从机拉低data,主机读取到data线被拉低持续80us后从机拉高data线,
            持续80us,直到高电平结束,意味着主机可以开始接受数据。
           
    四. 主机开始接收数据:
            1.主机先把data线拉高(io设置为输入)。
            2.从机把data线拉低,主机读取data线电平,直到低电平结束(大约50us)
            从机拉高data线后,延迟40us左右(28~70us之间)主机再次读取data线
            电平,如果为低电平,则为“0”,如果为高电平,则为“1”。
            3.继续重复上述1,2步骤累计40次。

    五. data线拉低50us代表读取结束

    六. 校验数据

     

    那么我们在程序上应该如何设计呢?(这里我的程序是基于stm32微处理器来讲解的,其他单片机也一样的操作,时序都是相同的)

     

    准备阶段我们先要有3个函数,数据引脚初始化函数,还有数据引脚切换输入输出方向的函数。

    1.  
      void dht11_init (void )
    2.  
      {
    3.  
      GPIO_InitTypeDef GPIO_InitStructure;
    4.  
      /* Enable clock */
    5.  
      RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);
    6.  
       
    7.  
      /* Configure Ports */
    8.  
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    9.  
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    10.  
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    11.  
      GPIO_Init(GPIOA, &GPIO_InitStructure);
    12.  
       
    13.  
      GPIO_SetBits(GPIOA, GPIO_Pin_7);
    14.  
      }
    15.  
       
    16.  
      void mode_input(void )
    17.  
      {
    18.  
      GPIO_InitTypeDef GPIO_InitStructure;
    19.  
       
    20.  
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    21.  
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    22.  
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    23.  
      GPIO_Init(GPIOA, &GPIO_InitStructure);
    24.  
      }
    25.  
       
    26.  
      void mode_output(void )
    27.  
      {
    28.  
      GPIO_InitTypeDef GPIO_InitStructure;
    29.  
       
    30.  
      GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    31.  
      GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    32.  
      GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    33.  
      GPIO_Init(GPIOA, &GPIO_InitStructure);
    34.  
      }

    接下来我们就再写一个函数,来读取dht11数据即可

    1.  
      unsigned int dht11_read(void)
    2.  
      {
    3.  
      int i;
    4.  
      long long val;
    5.  
      int timeout;
    6.  
       
    7.  
      GPIO_ResetBits(GPIOA, GPIO_Pin_7);
    8.  
      delay_us(18000); //pulldown for 18ms
    9.  
      GPIO_SetBits(GPIOA, GPIO_Pin_7);
    10.  
      delay_us( 20 ); //pullup for 30us
    11.  
       
    12.  
      mode_input();
    13.  
       
    14.  
      //等待dht11拉高80us
    15.  
      timeout = 5000;
    16.  
      while( (! GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--; //wait HIGH
    17.  
       
    18.  
      //等待dht11拉低80us
    19.  
      timeout = 5000;
    20.  
      while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ; //wait LOW
    21.  
       
    22.  
      #define CHECK_TIME 28
    23.  
       
    24.  
      for(i=0;i<40;i++)
    25.  
      {
    26.  
      timeout = 5000;
    27.  
      while( (! GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7)) && (timeout > 0) ) timeout--; //wait HIGH
    28.  
       
    29.  
      delay_us(CHECK_TIME);
    30.  
      if ( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) )
    31.  
      {
    32.  
      val=(val<<1)+1;
    33.  
      } else {
    34.  
      val<<=1;
    35.  
      }
    36.  
       
    37.  
      timeout = 5000;
    38.  
      while( GPIO_ReadInputDataBit (GPIOA, GPIO_Pin_7) && (timeout > 0) ) timeout-- ; //wait LOW
    39.  
      }
    40.  
       
    41.  
      mode_output();
    42.  
      GPIO_SetBits(GPIOA, GPIO_Pin_7);
    43.  
       
    44.  
      if (((val>>32)+(val>>24)+(val>>16)+(val>>8) -val ) & 0xff ) return 0; //校验
    45.  
      else return val>>8;
    46.  
       
    47.  
      }

    只要按照上述的时序步骤来操作,就能够读取出DHT11的温湿度值啦。

    同时我们要注意,只有读出来的数据校验通过了,我们才使用这一次的温湿度数据。

    还有他读取出来40位数据的数据结构: 8位湿度整数数据+8位湿度小数数据+8位温度整数数据+8位温度小数数据+8位校验位

  • 相关阅读:
    leetcode 131. Palindrome Partitioning
    leetcode 526. Beautiful Arrangement
    poj 1852 Ants
    leetcode 1219. Path with Maximum Gold
    leetcode 66. Plus One
    leetcode 43. Multiply Strings
    pytorch中torch.narrow()函数
    pytorch中的torch.repeat()函数与numpy.tile()
    leetcode 1051. Height Checker
    leetcode 561. Array Partition I
  • 原文地址:https://www.cnblogs.com/CodeWorkerLiMing/p/10747507.html
Copyright © 2011-2022 走看看