樹莓派高級GPIO庫,wiringpi2 for python使用筆記(四)實戰DHT11解碼


DHT11是一款有已校准數字信號輸出的溫濕度傳感器。 精度濕度+-5%RH, 溫度+-2℃,量程濕度20-90%RH, 溫度0~50℃。

我買的封裝好的模塊,上邊自帶了上拉電阻,直接查到樹莓派上即可灰、紫、藍分別代表數據、3.3V、0V,接到樹莓派的3,1,10腳,分別對應PIN8,3.3V,0V。

QQ圖片20160104092520

DHT11與單片機通訊協議為單線協議(1-wire),其實單線協議蠻厲害的,一個GPIO就能實現數據的讀取,但是這個協議沒有同步脈沖,所以對時序要求比較高,比如DHT11對高低電平定義如下:

低電平50us,然后一個26-28us的高電平,代表0

低電平50us,然后一個70us的高電平,代表1

也就是說,需要能分辨出40us以下的時間才能准確的測出,下邊看看具體的時序:

總線空閑狀態為高電平,主機把總線拉低等待DHT11響應,主機把總線拉低必須大於18毫秒,保證DHT11能檢測到起始信號。DHT11接收到主機的開始信號后,等待主機開始信號結束,然后發送80us低電平響應信號.主機發送開始信號結束后,延時等待20-40us后, 讀取DHT11的響應信號,主機發送開始信號后,可以切換到輸入模式,或者輸出高電平均可, 總線由上拉電阻拉高。

image

數字0表示如下圖:

image

數字1表示如下圖:

image

可以看出,每一位包括一開始的響應信號,都是由一個低電平跟一個高電平組成,其中響應信號為80us+80us=160us

數字0為50+26=76us

數字1為50+70=120us

為讀到DHT11的狀態,我編寫了以下的程序:

import wiringpi2 as gpio
owpin=8     #第8腳為1-wire腳
tl=[]       #存放每個數據位的時間
gpio.wiringPiSetup()        #初始化wiringpi庫
gpio.pinMode(owpin,1)       #設置針腳為輸出狀態
gpio.digitalWrite(owpin,1)  #輸出高電平
gpio.delay(1)
###發開始指令,要求DHT11傳輸數據
gpio.digitalWrite(owpin,0)  #拉低25ms開始指令
gpio.delay(25)
gpio.digitalWrite(owpin,1)  #輸出高電平,開始指令結束
gpio.pinMode(owpin,0)       #設針腳為輸入狀態
###開始指令發送完畢,把管腳設置為高電平,並等待DHT11拉低管腳。傳輸數據
while(gpio.digitalRead(owpin)==1): pass #如果管腳一直是1,則一直等待。
###若被拉低,說明傳輸開始,應答信號+40位數據+結束標志共42位
###下邊共循環45次,故意多循環幾次看結果。
for i in range(45):   #測試每個數據周期的時間(包括40bit數據加一個發送開始標志
    tc=gpio.micros()  #記下當前us數(從初始化開始算起,必要時重新初始化)
    '''
    一個數據周期,包括一個低電平,一個高電平,從DHT11第一次拉低信號線開始
    到DHT11發送最后一個50us的低電平結束(然后被拉高,一直維持高電平,所以
    最后的完成標志是一直為高,超過500ms)
    '''
    while(gpio.digitalRead(owpin)==0):pass  #一位數據由一個低電平
    while(gpio.digitalRead(owpin)==1):      #加一個高電平組成
        if gpio.micros()-tc>500:    #如果超過500us就結束了本次循環,傳輸結束后
            break                   #會被上拉電阻拉成高電平,防止進入死循環
    tl.append(gpio.micros()-tc) #記錄每個周期時間的us數,存到tl這個列表

print(tl)      #打印結果

程序里有詳細的解釋,我就不再贅述,這里貼出我這里的執行結果:

[116, 68, 74, 67, 124, 64, 120, 120, 76, 71, 73, 73, 73, 73, 73, 73, 74, 67, 73, 73, 123, 120, 70, 72, 73, 79, 71, 73, 73, 74, 73, 74, 73, 70, 73, 122, 74, 117, 120, 119, 70, 508, 506, 512, 512]

現在分析一下結果:

第一個116us是應答信號,按照說明應該是160us估計是我手里這個DHT11做的不是很標准后邊40個(從68us開始到最后一個70us)為40位數據,后邊因為沒有收到任何數據,所以都超過了500us(時間超過500us就跳出循環防止死循環)

第一個116us不管,60-80us的為0,120左右的為1,則收到的數據為:

濕度整數     濕度小數     溫度整數     溫度小數     校驗
0001 0110  0000 0000  0001 1000  0000 0000  0010 1110
16+4+2=22  0          16+8=24    0          32+8+4+2=46

故讀到的結果是濕度22%,溫度24度,校驗和為22+24=46,讀取成功。

我又加了一些數據處理,以及讀取失敗重新讀取的附加代碼,最終代碼如下:

import wiringpi2 as gpio
owpin=8     #第8腳為1-wire腳
def getval(owpin):
    tl=[]  #存放每個數據位的時間
    tb=[]  #存放數據位
    gpio.wiringPiSetup() #初始化wiringpi庫
    gpio.pinMode(owpin,1)  #設置針腳為輸出狀態
    gpio.digitalWrite(owpin,1) #輸出高電平
    gpio.delay(1)
    gpio.digitalWrite(owpin,0) #拉低20ms開始指令
    gpio.delay(25)
    gpio.digitalWrite(owpin,1) #抬高20-40us
    gpio.delayMicroseconds(20)
    gpio.pinMode(owpin,0)     #設針腳為輸入狀態
    while(gpio.digitalRead(owpin)==1): pass #等待DHT11拉低管腳

    for i in range(45):   #測試每個數據周期的時間(包括40bit數據加一個發送開始標志
        tc=gpio.micros()  #記下當前us數(從初始化開始算起,必要時重新初始化)
        '''
        一個數據周期,包括一個低電平,一個高電平,從DHT11第一次拉低信號線開始
        到DHT11發送最后一個50us的低電平結束(然后被拉高,一直維持高電平,所以
        最后的完成標志是一直為高,超過500ms)
        '''
        while(gpio.digitalRead(owpin)==0):pass
        while(gpio.digitalRead(owpin)==1):
            if gpio.micros()-tc>500: #如果超過500ms就結束了
                break
        if gpio.micros()-tc>500:   #跳出整個循環
            break
        tl.append(gpio.micros()-tc) #記錄每個周期時間的us數,存到tl這個列表

#    print(tl)      #反注釋后可打印時間列表
    tl=tl[1:]       #去掉第一項,剩下40個數據位
    for i in tl:
        if i>100:  #若數據位為1,時間為50us低電平+70us高電平=120us
            tb.append(1)
        else:
            tb.append(0) #若數據位為0,時間為50us低電平+25us高電平=75us
                                #這里取大於100us就為1
#    print(tb)      #反注釋可查看每一位狀態
    return tb

def GetResult(owpin):
    for i in range(10):
        SH=0;SL=0;TH=0;TL=0;C=0
        result=getval(owpin)
#        print(len(result))
        if len(result)==40:
            for i in range(8):
                #計算每一位的狀態,每個字8位,以此為濕度整數,濕度小數,溫度整數,溫度小數,校驗和
                SH*=2;SH+=result[i]
                SL*=2;SL+=result[i+8]
                TH*=2;TH+=result[i+16]
                TL*=2;TL+=result[i+24]
                C*=2;C+=result[i+32]
            if ((SH+SL+TH+TL)%256)==C and C!=0:
                break
            else:
                print("Read Sucess,But checksum error! retrying")
        else:
            print("Read failer! Retrying")
        gpio.delay(200)
    return SH,SL,TH,TL

SH,SL,TH,TL=GetResult(owpin)
print("濕度:",SH,SL,"溫度:",TH,TL)

運行結果如下:

濕度: 20 0 溫度: 24 0


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM