(13)樹莓派B+ 讀取DHT11溫度溼度模組的數值
阿新 • • 發佈:2019-01-31
溫度溼度模組看起來簡單,只有三個引腳(實際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)測試幾次結果
錯誤率還是比較高的,暫時不知道如何比較準確的識別時序訊號,而且在樹莓派執行多工時,很可能錯的很離譜。 另外,在沒有上拉電阻的時候,也可以正常工作。
如上圖,三根引腳分別是:“+”對應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上拉電阻,如下所示:
電路圖並不難,關鍵是如何按照時序訊號來寫相關處理程式碼。 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)測試幾次結果
錯誤率還是比較高的,暫時不知道如何比較準確的識別時序訊號,而且在樹莓派執行多工時,很可能錯的很離譜。 另外,在沒有上拉電阻的時候,也可以正常工作。