您的位置:首页 > 其它

(13)树莓派B+ 读取DHT11温度湿度模块的数值

2015-05-23 17:00 253 查看
温度湿度模块看起来简单,只有三个引脚(实际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)测试几次结果



错误率还是比较高的,暂时不知道如何比较准确的识别时序信号,而且在树莓派执行多任务时,很可能错的很离谱。
另外,在没有上拉电阻的时候,也可以正常工作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: