树莓派高级GPIO库,wiringpi2 for python使用笔记(四)实战DHT11解码

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
时间: 2024-10-14 02:28:33

树莓派高级GPIO库,wiringpi2 for python使用笔记(四)实战DHT11解码的相关文章

树莓派高级GPIO库,wiringpi2 for python使用笔记(三)GPIO操作

GPIO库的核心功能,当然就是操作GPIO了,GPIO就是"通用输入/输出"接口,比如点亮一个LED.继电器等,或者通过iic spi 1-wire等协议,读取.写入数据,这都是GPIO的用处,可以说没有GPIO,树莓派只能当小电脑用,有了GPIO,就升级成一个控制器了.先来说说怎么操作一个数字量(高低电平). 先看代码: import wiringpi2 as gpio from wiringpi2 import GPIO gpio.wiringPiSetup() #初始化 gpio

树莓派高级GPIO库,wringpi2 for python使用笔记(一)安装

网上的教程,一般Python用RPi.GPIO来控制树莓派的GPIO,而C/C++一般用wringpi库来操作GPIO,RPi.GPIO过于简单,很多高级功能不支持,比如i2c/SPI库等,也缺乏高精度定时等高级特性.相比之下,wiringpi则功能丰富的多,其实wringpi已经有了python绑定,可以非常简单的在python中使用这个库.鉴于网上基本没有这个库的中文说明,我一边学习,一边以做笔记的形式,写几篇关于这个库的基本使用的文章. 安装:首先安装python-pip: 我用的Arch

树莓派高级GPIO库,wiringpi2 for python使用笔记(二)高精度计时、延时函数

学过单片机的同学应该清楚,我们在编写传感器驱动时,需要用到高精度的定时器.延时等功能,wiringpi提供了一组函数来实现这些功能,这些函数分别是: micros() #返回当前的微秒数,这个数在调用wiringPiSetup()后被清零并重新计时 millis() #返回当前的毫秒数,同上,这个数在调用wiringPiSetup()后被清零并重新计时 delayMicroseconds() #高精度微秒延时 delay() #毫秒延时. python相对于C,一个很大的问题就是执行速度慢,所以

[Python笔记]Python学习笔记四

模块 在Python中,一个.py文件就是一个模块(Module) 使用模块的好处就是大大提高代码的可维护性,并且可以被其他地方引用. 同时可以避免函数名和变量名的冲突. Package(包) 每个包目录下面必须有一个 __init__.py文件 这个文件可以是空的,这个文件对应模块名就是当前目录名即包名 模块的使用 Python内置了很多模块 #!/usr/bin/env python # -*- coding:utf-8 -*- # 编写一个属于自己的模块 # 文件的第一个字符串被视为模块的

Python学习笔记四

参考教程:廖雪峰官网https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000 Python高级特性 一.切片 如果要取出一个列表或元组的部分元素,可以通过直接下标方法: lista=['apple','banana','cherry'] lista[0] lista[1] lista[2] 但如果要取再多个,或者前面(后面)多少个,或中间多少个,直接访问的方法就很不方便了. Python

Python学习笔记四--字典与集合

字典是Python中唯一的映射类型.所谓映射即指该数据类型包含哈希值(key)和与之对应的值(value)的序列.字典是可变类型.字典中的数据是无序排列的. 4.1.1字典的创建及赋值 dict1={} dict2={'name':John,'age':24} 使用dict()函数创建字典. dict()函数接受序列,或关键字参数作为参数创建字典.若不提供参数则创建空字典. dict1=dict([['x',1],['y',2]])#接受list作为参数创建字典 dict2=dict(x=1.y

Python学习笔记(四)Python对象类型及其运算

Python对象的相关术语: Python程序中保存的所有数据都是围绕对象这个概念展开的 程序中存储的所有数据都是对象 每个对象都有一个身份,一个类型和一个值 例如,school="MaGe linux"会以"MaGe linux"创建一个字符串对象,其身份是指向它在内存中所处位置的指针(其在内存中的地址),而school就是引用这个具体位置的名称 In [14]: name="herry" In [15]: id(name) Out[15]: 

Python学习笔记(四)

一.list创建 list 是Python语言中一种内置的数据类型 list 中可以存放不同类型的数据 list = []  #创建一个空列表 list = [1,2,3] #创建一个非空列表,并初始化其元素为1,2,3 二.list长度 使用 len(list_变量) 可以测得list的长度 三.list切片 list = [1,2,3,4,5,6,7,8,9,10] print(list[3:])  #4,5,6,7,8,9,10 print(list[3:5]) #4,5 print(li

python学习笔记四:字符串格式化

字符串格式化:%,左侧放字符串,右侧放希望被格式化的值,通常为元组 >>> format = "Hello, %s, %s enough for ya?" >>> values = ('world', 'Hot') >>> print format % values Hello, world, Hot enough for ya? 如果在格式化字符串里面包括百分号,那么必须使用%% 模板字符串:类似于shell中的变量替换 1)