DHT11 温湿度传感器 - 图1

面向学生

初学者,具备一定的单片机开发基础。

一、简介

本文将讲解如何使用DHT11温湿度传感器来感知环境温湿度,并且把数据在屏幕中显示出来。许多传感器的使用方法都是类似的,通过学习使用DHT11,相信读者可以做到举一反三,能够使用更多种类的传感器。
DHT11数字温湿度传感器是一款能够检测温湿度的复合传感器,其内置一个测温元件、一个电阻式感湿元件和一个单片机。以奥松公司生产的DHT11温湿度传感器为例,其有效测量范围见表。

测量项 有效测量范围
温度 0~50℃
湿度 20~95%

其实物如图所示。
DHT11温湿度传感器

可以看到,DHT11带有4个引脚,其相关说明见表。

PIN(引脚) 名称 说明
1 VCC 供电引脚,3~5.5VDC
2 DATA 温湿度数据输出
3 NC 空置引脚
4 GND 地线引脚,接电源负极

image.png
根据上表说明,可以从DATA引脚来获取温湿度数据,下面简单讲解一下其通信协议。
模块的引脚说明
传感器模块把DHT11的VCC、GND和DATA引脚引出,其对应关系为:

  • 模块的+引脚对应DHT11的VCC引脚
  • 模块的-引脚对应DHT11的GND引脚
  • 模块的out引脚对应DHT11的DATA引脚

    二、DATA引脚的通信协议分析

    从DHT11中获取温湿度数据的方法较比简单,首先是CC2530与DHT11配对(握手),然后按照特定的协议从DATA引脚接收数据。

    1.配对(握手)

    在发送温湿度数据前,DHT11需要先和CC2530配对,配对的协议如下:
    (1)DATA引脚在初始的默认状态时处于高电平(3.3v)。
    (2)CC2530拉低DATA引脚的电平18ms毫秒以上,接着拉高电平20~40us,DHT11就会被激活。
    (3)DHT11会主动拉低DATA引脚的电平,并且持续80us,表示已经收到了CC2530的指令并且配对成功。
    (4)接着DHT11会再次拉高电平,80us后就开始发送温湿度数据给CC2530了。
    这个配对过程如图所示。
    DHT11 温湿度传感器 - 图4

    2.接收数据

    DHT11的温湿度数据是以二进制数据表示的,这些二进制数据是按照一个比特位接着一个比特位这样顺序发送到CC2530的,具体的原理如下:
    (1)在发送每个比特位之前,DHT11都会把DATA引脚的电平拉低50us,以此告诉CC2530:“我接着要发送一个比特位了”。
    (2)接着,DHT11把DATA引脚的电平拉高,如果持续拉高26~28us,表示发送的是数据0;如果持续拉高70us,表示发送的是数据1,如图9-3所示。
    DHT11 温湿度传感器 - 图5
    数据0
    DHT11 温湿度传感器 - 图6
    数据1
    通过这个方式,温湿度数据就发送给了CC2530了。每当配对成功后,DHT11就会默认发送40个比特位,即一共5个字节,其中包含两个字节的当前温度值、两个字节是当前湿度值和一个校验值。DHT11的通信协议大致上介绍完毕,但还有多个细节还没讲解到,有兴趣的读者可查阅更多相关的资料或仔细研究一下接下来介绍的API的源代码。

    三、电路原理图

    模块的电路原理图如图所示。
    DHT11 温湿度传感器 - 图7

    四、DHT11驱动API设计

    接下来,笔者根据以上原理,为DHT11设计了2个API,定义如下:
  1. /**
  2. * @fn halDHT11Init
  3. *
  4. * @brief 初始化函数,使用DHT11前必须先调用此函数
  5. */
  6. void halDHT11Init(void);
  7. /**
  8. * @fn halDHT11GetData
  9. *
  10. * @brief 获取DHT11的温湿度数据
  11. *
  12. * @return 温湿度数据值
  13. */
  14. halDHT11Data_t halDHT11GetData(void);

上述的代码中的halDHT11Data_t是一个结构体,定义如下:

  1. /*
  2. * @brief 用于表示DHT11温湿度数据
  3. */
  4. typedef struct {
  5. unsigned char ok; //ok的值非0时温湿度数据才有效
  6. unsigned char temp; //温度值,取值范围:0~50
  7. unsigned char humi; //湿度值,取值范围:20~95
  8. }halDHT11Data_t;

上述API的实现的参考代码如下:

  1. /*配置DHT11的Data引脚与单片机的P0_6连接,读者可修改为其他的GPIO*/
  2. #define HAL_DHT11_PORT 0
  3. #define HAL_DHT11_PIN 6
  4. /* Boolean value. */
  5. #define HAL_DHT11_FALSE 0
  6. #define HAL_DHT11_TRUE 1
  7. /* DHT11 状态码定义*/
  8. #define HAL_DHT11_SC_ERR HAL_DHT11_FALSE//测量值错误
  9. #define HAL_DHT11_SC_OK HAL_DHT11_TRUE//测量值正确
  10. #define HAL_DHT11_SC_HUMI_OUTOFRANGE 0xF1//湿度值超出标准测量范围
  11. #define HAL_DHT11_SC_TEMP_OUTOFRANGE 0xF2//温度值超出标准测量范围
  12. #define HAL_DHT11_SC_HT_OUTOFRANGE 0xF3//温度和湿度值超出测量范围
  13. /*把GPIO设置为输入或输出模式*/
  14. #define HAL_DHT11_IO_OUTPUT() CC2530_IOCTL(HAL_DHT11_PORT, HAL_DHT11_PIN, CC2530_OUTPUT)//设置为输出模式,读者需要根据不同的单片机来修改此函数
  15. #define HAL_DHT11_IO_INPUT() CC2530_IOCTL(HAL_DHT11_PORT, HAL_DHT11_PIN, CC2530_INPUT_PULLDOWN)//设置为输入模式,读者需要根据不同的单片机来修改此函数
  16. /*获取DHT11 Data引脚的电平状态*/
  17. #define HAL_DHT11_IO_GET(port, pin) CC2530_GPIO_GET(port, pin)//获取指定GPIO的电平状态,读者需要根据不同的单片机来修改此函数
  18. #define HAL_DHT11_IO() HAL_DHT11_IO_GET(HAL_DHT11_PORT, HAL_DHT11_PIN)//获取DHT11的Data引脚的电平状态
  19. /*设置DHT11 Data引脚的电平状态*/
  20. #define HAL_DHT11_IO_SET_LO() HAL_DHT11_IO_SET(HAL_DHT11_PORT, HAL_DHT11_PIN, 0)//设置地电平
  21. #define HAL_DHT11_IO_SET_HI() HAL_DHT11_IO_SET(HAL_DHT11_PORT, HAL_DHT11_PIN, 1)//设置高电平
  22. /* 测量结果合法性检测*/
  23. #define HAL_DHT11_TEMP_OK(t) ((t) <= 50)//检测温度值是否合法
  24. #define HAL_DHT11_HUMI_OK(h) ((h) >= 20 && (h) <= 95)//检测湿度值是否合法
  25. static void halDHT11SetIdle(void);
  26. static uint8_t halDHT11ReadByte(void);
  27. static uint8_t halDHT11CheckData(uint8_t TempI, uint8_t HumiI);
  28. /**
  29. * @fn halDHT11Init
  30. *
  31. * @brief 初始化函数,使用DHT11前必须先调用此函数
  32. */
  33. void halDHT11Init(void)
  34. {
  35. halDHT11SetIdle();
  36. }
  37. /**
  38. * @fn halDHT11GetData
  39. *
  40. * @brief 获取DHT11的温湿度数据
  41. *
  42. * @return 温湿度数据值
  43. */
  44. halDHT11Data_t halDHT11GetData(void)
  45. {
  46. uint8_t HumiI, HumiF, TempI, TempF, CheckSum;
  47. halDHT11Data_t dht11Dat = { .ok = HAL_DHT11_FALSE };
  48. /* >18ms, keeping gpio low-level */
  49. HAL_DHT11_IO_SET_LO();//拉低Data引脚的电平
  50. HAL_DHT11_DELAY_MS(30);//持续30ms
  51. HAL_DHT11_IO_SET_HI();//拉高Data引脚的电平
  52. HAL_DHT11_DELAY_US(32);//延迟32us
  53. HAL_DHT11_IO_INPUT();//把Data引脚对应的GPIO设置为输入模式,接收DHT11的测量结果
  54. if (!HAL_DHT11_IO()) {//如果Data引脚的电平为低电平
  55. /*计时等待低电平结束*/
  56. uint16_t cnt = 1070; //cnt用于在while循环中计时,1070表示约计时1ms(不同的单片的计数值会不同)
  57. while (!HAL_DHT11_IO() && cnt--);
  58. if(!cnt) goto Exit;//如果计时结束,表示在规定时限内DHT11仍未输出高电平
  59. /*计时等待高电平结束*/
  60. cnt = 1070;
  61. HAL_DHT11_DELAY_US(80);//延时80us
  62. while (HAL_DHT11_IO() && cnt--);
  63. if(!cnt) goto Exit;
  64. /* 读取数据 */
  65. HumiI = halDHT11ReadByte();
  66. HumiF = halDHT11ReadByte();
  67. TempI = halDHT11ReadByte();
  68. TempF = halDHT11ReadByte();
  69. CheckSum = halDHT11ReadByte();//读取校验码
  70. if (CheckSum == (HumiI + HumiF + TempI + TempF)) {//如果数据校验正确
  71. dht11Dat.temp = TempI;//保存温度值
  72. dht11Dat.humi = HumiI;//保存湿度值
  73. dht11Dat.ok = halDHT11CheckData(TempI, HumiI);//检测温度和湿度是否合法
  74. }
  75. }
  76. Exit:
  77. halDHT11SetIdle();
  78. return dht11Dat;
  79. }
  80. /*
  81. * 初始化GPIO
  82. */
  83. static void halDHT11SetIdle(void)
  84. {
  85. HAL_DHT11_IO_OUTPUT();//把GPIO配置为输出模式
  86. HAL_DHT11_IO_SET_HI();//把GPIO口配置为高电平
  87. }
  88. /*
  89. *从DHT11的Data引脚中读取一个字节
  90. */
  91. static uint8_t halDHT11ReadByte(void)
  92. {
  93. uint8_t dat = 0;//保存读取到的字节
  94. for (uint8_t i = 0; i < 8; i++) {//循环8次
  95. /*计时等待低电平结束*/
  96. uint16_t cnt = 5350;//cnt用于在while循环中计时,5350表示约计时1ms(不同的单片的计数值会不同)
  97. while (!HAL_DHT11_IO() && cnt--);
  98. if(!cnt) break;//如果计时结束,表示在规定时限内DHT11仍未输出高电平
  99. /* 基于高电平的持续时间来获取的一个bit:
  100. * 持续时间为26~28us: 0
  101. * 持续时间>70us: 1
  102. */
  103. /*延迟50us后再检测Data的电平状态*/
  104. HAL_DHT11_DELAY_US(50);
  105. if (HAL_DHT11_IO()) {//如果为高电平,dat的最低位输入0
  106. dat <<= 1;
  107. dat |= 1;
  108. }
  109. else {//如果为低电平,dat的最低位输入1
  110. dat <<= 1;
  111. continue;
  112. }
  113. /*计时等待高电平结束*/
  114. cnt = 1070;//cnt用于在while循环中计时,1070表示约计时1ms(不同的单片的计数值会不同)
  115. while(HAL_DHT11_IO() && cnt--);
  116. if(!cnt) break;
  117. }
  118. return dat;
  119. }
  120. /*
  121. * 测量结果合法性检测
  122. */
  123. static uint8_t halDHT11CheckData(uint8_t TempI, uint8_t HumiI)
  124. {
  125. if (HAL_DHT11_HUMI_OK(HumiI)) {
  126. if(HAL_DHT11_TEMP_OK(TempI)) return HAL_DHT11_SC_OK;
  127. else return HAL_DHT11_SC_TEMP_OUTOFRANGE;
  128. }
  129. if (HAL_DHT11_TEMP_OK(TempI)) return HAL_DHT11_SC_HUMI_OUTOFRANGE;
  130. else return HAL_DHT11_SC_HT_OUTOFRANGE;
  131. }