手把手教你玩轉DHT11(原理+驅動)

大家生活中一定經常使用溫溼度數據,比如:天氣預報、智能家居、智慧大屏等等。這些數據可以通過溫溼度傳感器進行獲取。在嵌入式開發中,溫溼度傳感器是一種十分常用的傳感器。本文將爲大家介紹溫溼度傳感器 DHT11,內容包含模塊介紹、工作原理、驅動方法,並提供編程實戰示例。

1. 源碼下載及前置閱讀

本文首發 良許嵌入式網https://www.lxlinux.net/e/ ,歡迎關注!

本文所涉及的源碼及安裝包如下(由於平臺限制,請點擊以下鏈接閱讀原文下載):

https://www.lxlinux.net/e/stm32/dht11-tutorial.html

如果不知道如何搭建 STM32 編程環境,不知道如何燒錄 STM32 代碼,可以閱讀這篇文章:

https://www.lxlinux.net/e/stm32/stm32-quick-start-for-beginner.html

如果你連代碼都不知道怎麼燒錄到 STM32 的,可以參考下文,提供了 5 種代碼燒錄方式:

https://www.lxlinux.net/e/stm32/five-ways-to-flash-program-to-stm32.html

文中所使用的芯片是 STM32F103C8T6 ,配套了一個工程模板,如果你需要自己搭建一個工程模板,可以參考下文:

https://www.lxlinux.net/e/stm32/create-stm32-hal-project-template.html

2. DHT11介紹

DHT11(數字溫溼度傳感器)爲 3 或 4 針單排引腳封裝,連接方便。具有品質卓越、超快響應、抗干擾能力強、性價比極高、超小的體積、極低的功耗的優點,使其成爲在測溫、測溼應用,在苛刻應用場合的一個非常不錯的選擇。

DHT11 內置一個電阻式感溼元件和一個 NTC 測溫元件,並與一個單片機相連接(DHT11 內部)。每個 DHT11 都在極爲精確的溼度校驗室中進行校準,校準係數以程序的形式存在傳感器中,傳感器內部在檢測信號的處理過程中要調用這些校準係數。DHT11 採用簡易快捷的單線制串行接口,方便系統集成。

2.1 DHT11型號介紹

DHT11 有 3 腳和 4 腳兩款,在使用上沒有差別,接線都一樣,主要接三根,四腳的款式有一腳懸空。四腳款接杜邦線會有點不穩,只能插麪包板上,建議直接買三腳的。

<img src="https://lxlinux.superbed.verylink.top/item/655b12b2c458853aef4b8a5b.jpg" style="zoom: 67%;" />

同樣可以測量溫溼度的還有 DHT20、DHT22 等,都是大同小異。

DHT11 雖然可以同時測量溫溼度,但是測量範圍是打不過專業測溫傳感器的,比如 ds18b20 測量的溫度範圍就有 -55°C ~ 125°C,而 DHT11 只有 0~50℃。

2.2 DHT11工作參數及引腳介紹

DHT11 工作參數:

  1. 溼度測量範圍:20~90%RH
  2. 溼度測量精度:±5%RH
  3. 溫度測量範圍:0~50℃
  4. 溫度測量精度:±2℃
  5. 工作電壓:DC 3.3V/5V

接線如下,別把正負極接反啦,接反會燒壞掉的。

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

DHT11 採用單總線協議,也就是使用一根 DATA 線進行數據的收發。DHT11 的 DATA 線一次通訊時間 4ms 左右,數據分整數部分、小數部分和校驗位,具體爲: 8bit 溼度整數數據 + 8bit 溼度小數數據 + 8bit 溫度整數數據 + 8bit 溫度小數數據 + 8bit 校驗位。

3. DHT11工作原理

3.1 正常工作驗證

上電後,「電源指示燈/POWER」紅燈亮,表示上電成功,正常工作。

3.2 DHT11工作時序

3.2.1 整體工作時序

DHT11 整體工作時序爲:主機發送開始信號、DHT11 響應輸出、主機接收 40bit 數據(溼度數據+溫度數據+校驗值),結束信號(可選)。具體過程如下:

  1. 總線空閒狀態爲高電平,主機拉低總線等待 DHT11 響應, 主機把總線拉低必須大於 18ms,保證 DHT11 能檢測到起始信號;

  2. 主機發送開始信號結束後,拉高總線電平並延時等待 20-40us 後,讀取 DHT11 的響應信號;

  3. DHT11 接收到主機的開始信號後,等待微處理器開始信號結束,發送 80us 低電平響應信號;

  4. DHT11 發送 80us 高電平準備發送數據;

  5. DHT11 發送 40bit 數據(溼度數據+溫度數據+校驗值)。

過程 主機 DHT11
1 拉低>18ms
2 拉高20~40us
3 響應 80us 低電平
4 拉高 80us
5 發送 40bit 數據(溼度數據+溫度數據+校驗值)

3.2.2 起始及響應信號

總流程講完介紹一下細分流程:

首先主機拉低總線至少 18ms,然後再拉高總線,延時 20~40us,此時起始信號(有時也叫復位信號)發送完畢。

DHT11 檢測到復位信號後,觸發一次採樣,並拉低總線 80us 表示響應信號,告訴主機數據已經準備好了。DHT11 之後拉高總線 80us,然後開始傳輸數據。如果檢測到響應信號爲高電平,則 DHT11 初始化失敗,請檢查線路是否連接正常。

3.2.3 讀時序

DHT11 開始傳輸數據。每 1bit 數據都以 50us 低電平開始,告訴主機開始傳輸一位數據了。DHT11 以高電平的長短定義數據位是 0 還是 1:當 50us 低電平過後拉高總線,高電平持續 26~28us 表示 0,高電平持續 70us 表示數據 1。

當最後 1bit 數據傳送完畢後,DHT11 拉低總線 50us,表示數據傳輸完畢,隨後總線由上拉電阻拉高進入空閒狀態。

位數據0表示方式:

以 50us 低電平開始,高電平持續 26~28us 表示 0。

位數據1表示方式:

以 50us 低電平開始,高電平持續 70us 表示 1。

3.3 DHT11數據格式

DHT11 的 DATA 傳輸一次完整的數據爲 40bit,按照高位在前,低位在後的順序傳輸。

數據格式爲:8bit 溼度整數數據 + 8bit 溼度小數數據 + 8bit 溫度整數數據 + 8bit 溫度小數數據 + 8bit 校驗位,一共 5 字節(40bit)數據。

正常情況下,前四個字節的和剛好與校驗位相等,通過這種機制可以保證數據傳輸的準確性。

4. 編程實戰

4.1 硬件接線

本教程使用的硬件如下:

  • 單片機:STM32F103C8T6

  • 溫溼度傳感器:DHT11

  • 串口:USB 轉 TTL

  • 燒錄器:ST-LINK V2

DHT11 STM32 USB 轉 TTL
VCC 3.3/5V
DAT A8
GND G
A10 TX
A9 RX
G GND

我們使用 A8 作爲 DHT11 的數據引腳,串口 1 進行 log 輸出。

接線如下圖:

4.2 加載DHT11模塊

我們在模板工程裏的 BSP 目錄下創建一個 dht11 目錄,然後創建 dht11.c 及 dht11.h 兩個空文件。

打開工程,跟着我的貪喫蛇點點點:)

4.3 微秒級延時實現

DHT11 對時序要求嚴格,需要微妙級延時,我們常用的 HAL_Delay() 是毫秒級時延。實現微秒級延時的方法有很多,但是我們可以直接用模板工程中 delay 文件的 delay_us

void delay_us(uint32_t nus)
{
    uint32_t temp;
    SysTick->LOAD = nus * g_fac_us; /* 時間加載 */
    SysTick->VAL = 0x00;            /* 清空計數器 */
    SysTick->CTRL |= 1 << 0 ;       /* 開始倒數 */

    do
    {
        temp = SysTick->CTRL;
    } while ((temp & 0x01) && !(temp & (1 << 16))); /* CTRL.ENABLE位必須爲1, 並等待時間到達 */

    SysTick->CTRL &= ~(1 << 0) ;    /* 關閉SYSTICK */
    SysTick->VAL = 0X00;            /* 清空計數器 */
}

4.4 DATA引腳配置

DHT11 採用單總線協議與單片機通信,有時作爲輸入有時作爲輸出,所以我們需要在 DATA 引腳上配置輸入和輸出。

void DHT_GPIO_INPUT(void) 
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=DHT11_PIN;
	GPIO_InitStructure.Mode=GPIO_MODE_INPUT;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(DHT11_IO,&GPIO_InitStructure);
}

void DHT_GPIO_OUTPUT(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.Pin=DHT11_PIN;
	GPIO_InitStructure.Mode=GPIO_MODE_OUTPUT_PP;
	GPIO_InitStructure.Speed=GPIO_SPEED_FREQ_HIGH;
	HAL_GPIO_Init(DHT11_IO,&GPIO_InitStructure);
}

4.5 起始及響應信號實現

按照前面介紹的 DHT11 工作時序:

**主機發送起始信號:**先將總線設爲輸出模式,總線空閒狀態爲高電平,拉低總線至少 18ms,然後再拉高總線,延時 20~40us,此時復位信號發送完畢。

**DHT11 發送響應信號:**總線設爲輸入模式,使用 while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN)) 檢測總線從高電平跳變到低電平,等待 DHT11 拉低總線 。DHT11 響應信號持續 80us,使用 while(!HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN)) 檢測總線低電平跳變到高電平。之後 DHT11 拉高總線 80us ,使用 while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN)) 檢測總線從高電平跳變到低電平,然後 DHT11 開始傳輸數據。

起始信號及響應信號代碼如下:

void DHT11_Start()
{
	DHT_GPIO_OUTPUT();
	HAL_GPIO_WritePin(DHT11_IO, DHT11_PIN, GPIO_PIN_SET);
	HAL_GPIO_WritePin(DHT11_IO, DHT11_PIN, GPIO_PIN_RESET);
	HAL_Delay(20);                                               //拉低總線至少 18ms
	HAL_GPIO_WritePin(DHT11_IO, DHT11_PIN, GPIO_PIN_SET);
	
	DHT_GPIO_INPUT();

	while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));               //上一步將總線設爲高電平,等待DHT11響應低電平
	while(!HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));              //上一步DHT11響應低電平,等待DHT11拉高總線
	while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));               //上一步DHT11拉高了總線,等待DHT11拉低總線,開始傳送數據
}

4.6 讀取1字節數據

將 DHT11 發來的二進制數據存儲到 ReadData 變量中,讀取一位後,左移一位,循環8次,最終得到 1 byte 數據。

那麼如何判斷我們讀到的數據是 0 還是 1 呢?

通過 3.2.3 的分析可以知道,0 和 1 的時序只是高電平持續時間不同,所以我們只需要在 DHT11 拉低電平之後延時 40~60 微秒(代碼中使用 50 微秒),再讀取電平狀態就可以了,如果是高電平則爲 1,低電平則爲 0 。

uint8_t DHT_Read_Byte(void)  //從DHT11讀取一位(8字節)信號
{
    uint8_t i;
    uint8_t ReadData = 0;    //ReadData用於存放8bit數據,即8個單次讀取的1bit數據的組合
	uint8_t temp;            //臨時存放信號電平(0或1)
	
    for(i=0;i<8;i++){
        while(!HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));
        Delay_us(50);
        if(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN) == 1){
            temp = 1;
            while(HAL_GPIO_ReadPin(DHT11_IO, DHT11_PIN));
        }else{
            temp = 0;
        } 
        ReadData = ReadData << 1;
        ReadData |= temp;
    }
    return ReadData;
}

4.7 一次數據讀取及顯示

根據 3.2 的時序,我們就可以使用代碼實現 DHT11 一次讀取數據過程。

注意:DHT11 讀取數據間隔至少爲 2 秒,否則讀取到的數據可能不穩定,所以在最後可以延時 2 秒。

void DHT_Read()
{
	uint8_t i;
	
	DHT11_Start();
	DHT_GPIO_INPUT();
    
	for(i= 0;i < 5;i++){
		Data[i] = DHT_Read_Byte();
	}
    if((Data[0]+Data[1]+Data[2]+Data[3])==Data[4])
    {
        printf("溼度: %d.%dRH ,", Data[0], Data[1]);
        printf("溫度: %d.%d℃\r\n", Data[2], Data[3]);
    }else{
        printf("ERROR DATA\r\n");
    }
    HAL_Delay(2000);
}

dht11.h文件內容如下:

#ifndef __DHT11_H__
#define __DHT11_H__

#include "stdio.h"
#include "stm32f1xx.h"
 
#define DHT11_IO 		GPIOA
#define DHT11_PIN		GPIO_PIN_8

void DHT_Read(void);
    
#endif

4.8 最終效果

5. 小結

通過本文的學習與實踐,相信大家已經瞭解並掌握 DHT11 的特性和使用,能夠更好地應用於嵌入式開發。無論是構建智能家居系統還是開發物聯網設備,DHT11 都可以成爲您的得力助手,讓我們一起玩轉 DHT11,love and peace!


另外,想進大廠的同學,一定要好好學算法,這是面試必備的。這裏準備了一份 BAT 大佬總結的 LeetCode 刷題寶典,很多人靠它們進了大廠。

刷題 | LeetCode算法刷題神器,看完 BAT 隨你挑!

有收穫?希望老鐵們來個三連擊,給更多的人看到這篇文章

推薦閱讀:

歡迎關注我的博客:良許嵌入式教程網,滿滿都是乾貨!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章