DHT11是一款有已校准數字信號輸出的溫濕度傳感器。 精度濕度+-5%RH, 溫度+-2℃,量程濕度20-90%RH, 溫度0~50℃。
我買的封裝好的模塊,上邊自帶了上拉電阻,直接查到樹莓派上即可灰、紫、藍分別代表數據、3.3V、0V,接到樹莓派的3,1,10腳,分別對應PIN8,3.3V,0V。
DHT11與單片機通訊協議為單線協議(1-wire),其實單線協議蠻厲害的,一個GPIO就能實現數據的讀取,但是這個協議沒有同步脈沖,所以對時序要求比較高,比如DHT11對高低電平定義如下:
低電平50us,然后一個26-28us的高電平,代表0
低電平50us,然后一個70us的高電平,代表1
也就是說,需要能分辨出40us以下的時間才能准確的測出,下邊看看具體的時序:
總線空閑狀態為高電平,主機把總線拉低等待DHT11響應,主機把總線拉低必須大於18毫秒,保證DHT11能檢測到起始信號。DHT11接收到主機的開始信號后,等待主機開始信號結束,然后發送80us低電平響應信號.主機發送開始信號結束后,延時等待20-40us后, 讀取DHT11的響應信號,主機發送開始信號后,可以切換到輸入模式,或者輸出高電平均可, 總線由上拉電阻拉高。
數字0表示如下圖:
數字1表示如下圖:
可以看出,每一位包括一開始的響應信號,都是由一個低電平跟一個高電平組成,其中響應信號為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