1. 程式人生 > >(13)樹莓派B+ 讀取DHT11溫度溼度模組的數值

(13)樹莓派B+ 讀取DHT11溫度溼度模組的數值

溫度溼度模組看起來簡單,只有三個引腳(實際4個介面),但仔細一想,3個引腳分別作為VCC、GND、DATA用處的話,因為傳給樹莓派GPIO的只有高電平、低電平,那麼怎麼來讀取整型的溫度數字和溼度數字呢?這麼一想,並不簡單!反而是因為引腳少,它需要高低變化的時序訊號來表達數值,還有一些其它訊號如開始訊號等等。看樣子這次我們得先深入瞭解一下DHT11模組了。 0. 先來看模組結構和特性

如上圖,三根引腳分別是:“+”對應VCC 3.3V或者5V,中間OUT為DATA接GPIO口,“-”對應電源負極。
1. DHT11模組時序訊號 百度一下就能搜到中文版的說明書,傳送門:http://wenku.baidu.com/link?url=TZ8UkMmHxmwWfMUlThThtYJpEx1-um21yejWpvB_XSAog3TIr2AStTXNPTYb0FhJ_mgsWI0_KUf-LAAU4t6_aBELQBLL8NPSh315A1Bztw7 我們需要掌握兩個關鍵的時序訊號:開始握手階段和資料傳送階段。 1)開始握手階段

主機端GPIO傳送開始訊號首先拉低至少18ms,然後拉高20-40us,模式變為IN等待訊號輸入。 DHT11等待主機端開始訊號(低電平)結束後,傳送80us低電平響應開始訊號。然後DHT11拉高電平80us。握手完畢。 2)資料傳送階段
一次的溼度和溫度資料,DHT11需要傳送40bits(0、1)資料,每一位資料之前都以50us低電平開始,隨後的高電平時序訊號,持續26us-28us的表示這一位是0,持續70us表示這一位是1,然後繼續50us低電平,緊接著下一位的高電平開始。。 40bits資料的組成是=8bits溼度整數部分+8bits溼度小數部分(暫時沒用)+8bits溫度整數部分+8bits溫度小數部分(暫時沒用)+8bits校驗和 8bits的順序都是高位先出,然後用移位相加的方式,將這8位轉換成整型數字。 2. 線路連線 DHT11說明書中,推薦的做法是,在資料埠接5K上拉電阻,如下所示:
在樹莓派B+端選擇第2(5V)、14(0V)、16(GPIO.4)來分別連線DHT11的“+”、“-”和“OUT”端:
電路圖並不難,關鍵是如何按照時序訊號來寫相關處理程式碼。 3. 程式碼實現(python3) 我們還是使用RPi.GPIO庫來實現,這裡的難點是,讀取DHT11的輸出訊號,需要微秒級的定時,否則在資料傳輸階段,很難準確的識別出每一位是“0”還是“1”,而python又在單核 700MHz的CPU上,很難實現準確的微秒級定時,只能通過執行幾條無意義程式碼來替代微秒延時。 1)與DHT11互動階段 #!/usr/bin/python
#coding=utf-8
#注意本程式是python3!!!
import RPi.GPIO as GPIO
import time
from ctypes import *
import os #存放時序資料
data = [0 for i in range(40)]
def driver():
        j = 0
        # 感測器上電後,要等待1s以越過不穩定狀態
        GPIO.setmode(GPIO.BOARD)
        time.sleep(1)
        # 先向感測器傳送開始訊號,握手-LOW-
        GPIO.setup(16, GPIO.OUT)
        GPIO.output(16, GPIO.LOW)
        # 主機把匯流排拉低必須大於18毫秒,這裡採用20毫秒
        time.sleep(0.02)
        # 然後主機拉高並延時等待感測器的響應
        GPIO.output(16, GPIO.HIGH)         # 執行1次需要十幾微秒
        i = 1
        i = 1
        # 等待感測器的握手響應訊號和資料訊號
        GPIO.setup(16, GPIO.IN)
        while GPIO.input(16) == 1:
                continue
        # 匯流排為低電平,說明感測器傳送響應訊號,80us低電平
        while GPIO.input(16) == 0:
                continue
        # 然後感測器再把匯流排拉高80us,然後才準備傳送資料
        while GPIO.input(16) == 1:
                continue
        # 開始傳送資料
        # 一次完整的資料為40bit,高位先出
        # 8bit溼度整數資料+8bit溼度小數資料+8bit溫度整數資料+8bit溫度小數資料+8bit校驗和
        while j < 40:
                k = 0
                #每一位的起始訊號,都以50us低電平開始
                while GPIO.input(16) == 0:
                        continue
                #每一位的數值訊號,高電平的長短決定了資料位是0還是1。
                while GPIO.input(16) == 1:
                        #需要知道每次迴圈的耗時,才能知道k < x是表示0
                        k += 1
                        if k > 100:
                                break
                # 高電平持續26-28us表示0, 高電平持續70us表示1
                if k < 3:
                        data[j] = 0
                else:
                        data[j] = 1
                j += 1
        print(data) 2)計算溼度、溫度、校驗和 按照每8位轉換成一個十進位制數字 def compute():
        humidity_bit = data[0:8]
        humidity_point_bit = data[8:16]
        temperature_bit = data[16:24]
        temperature_point_bit = data[24:32]
        check_bit = data[32:40]
        humidity = 0
        humidity_point = 0
        temperature = 0
        temperature_point = 0
        check = 0
        for i in range(8):                # 溼度整數部分
                humidity += humidity_bit[i] * 2**(7-i)
                humidity_point += humidity_point_bit[i] * 2**(7-i)                # 溫度整數部分
                temperature += temperature_bit[i] * 2**(7-i)
                temperature_point += temperature_point_bit[i] * 2**(7-i)
                check += check_bit[i] * 2**(7-i)
        sum = humidity + humidity_point + temperature + temperature_point
        print("temperature:", temperature, ", humidity:", humidity)
        if check == sum:
                print("temperature:", temperature, ", humidity:", humidity)
        else:
                print("wrong!", check, "!=",  sum) if __name__ == "__main__":
        driver()
        compute()
        GPIO.cleanup() 3)測試幾次結果

錯誤率還是比較高的,暫時不知道如何比較準確的識別時序訊號,而且在樹莓派執行多工時,很可能錯的很離譜。 另外,在沒有上拉電阻的時候,也可以正常工作。