当前位置:   article > 正文

树莓派49/100 - Pico上实现DHT11温湿度传感器的PIO汇编程序_pico pio

pico pio

关于树莓派Pico里的PIO(Programmed I/O)编程,琢磨起来上瘾了,连续写了5篇文章:

看到网上有人用PIO写了DHT11/DHT22的温湿度传感器的程序,就下载下来研究了一番,国外论坛讨论的原贴在这里

我复制ashchap的代码,稍微修改后:

import machine
import utime
import rp2 

@rp2.asm_pio(set_init=rp2.PIO.OUT_HIGH, autopush=True, push_thresh=8) 
def DHT11():
    # 给DHT发送开始信号
    set(pindirs, 1)     
    # 电平拉低20毫秒
    set(pins, 0)          
    set(y, 31)    # 32*32*32*0.625 = 20480 > 20 ms              
    label ('loop_y')
    set(x, 31)    
    label ('loop_x')
    nop() [31] 
    jmp(x_dec, 'loop_x')  
    jmp(y_dec, 'loop_y') 

    set(pindirs, 0)  # 从DHT读数据
    wait(1, pin, 0)       
    wait(0, pin, 0)
    wait(1, pin, 0)
    wait(0, pin, 0)       

    label('readdata')
    set(x, 20)                   #reset x register to count down from 20
    wait(1, pin, 0)               #wait for high signal
    label('countdown')
    jmp(pin,'continue')         #if pin still high continue counting
    #pin is low before countdown is complete - bit '0' detected
    set(y, 0)                 
    in_(y, 1)                   #shift '0' into the isr
    jmp('readdata')             #read the next bit
        
    label('continue')
    jmp(x_dec,'countdown')      #decrement x reg and continue counting if x!=0
    #pin is still high after countdown complete - bit '1' detected 
    set(y,1)                  
    in_(y, 1)                   #shift one bit into the isr
    wait(0,pin,0)               #wait for low signal (next bit) 
    jmp('readdata')             #read the next bit

dht_data = machine.Pin(26)  # DHT11的数据针

while True:
    data=[]
    total=0
    sm=rp2.StateMachine(0, DHT11, freq=1_600_000, set_base=dht_data, in_base=dht_data, jmp_pin=dht_data) 
    sm.active(1)    

    #state machine frequency adjusted so that PIO countdown during 'readdata' ends somewhere between the  
    #duration of a '0' and a '1' high signal
    for i in range(5):         #data should be 40 bits (5 bytes) long
        data.append(sm.get())  #read byte
    
    print("data: " + str(data))
    
    #check checksum (lowest 8 bits of the sum of the first 4 bytes)
    for i in range(4):
        total=total+data[i]
    if((total & 255) == data[4]):
        humidity=data[0]        #DHT11 provides integer humidity (no decimal part)
        temperature=(1-2*(data[2] >> 7) )*(data[2] & 0x7f) #DHT11 provides signed integer temperature (no decimal part)
        print("Humidity: %d%%, Temp: %dC" % (humidity, temperature))        
    else:
        print("Checksum: failed")

    utime.sleep_ms(1000)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68

先了解一下DHT11的数据传输协议,要能够看懂下面这个信号时序图。
在这里插入图片描述
主机(单片机Pico)发送开始信号,set(pindirs, 1) 表示DATA信号线是输出状态,Pico向DHT输出,set(pins, 0) 拉低电平,后面的x,y的循环要保证延迟20毫秒。

状态机的频率为1.6M Hz,这样一个指令周期为1/1,600,600 = 0.625us(微秒),pioasm程序里的循环 323232*0.625 = 20480 us > 20 ms,其实jmp指令也占用时间,肯定会超过20ms。

然后,执行 set(pindirs, 0) 变为输入状态,即Pico从DHT读取数据,好像这个时候DATA会变为高电位,开始等待DHT的数据信号。

在这里插入图片描述
后面紧跟着4行wait(1, pins, 0), wait(0, pins, 0),我在下图标出了相应位置。实际上第一个wait(1, pins, 0)不写逻辑也没问题。
在这里插入图片描述
再后面的代码写得就比较啰嗦了,主要用于判断数据位0或1,DHT协议按高电位的持续时间来表示0或1,如下图。

在这里插入图片描述
有人改进了这段代码,但把状态机的工作频率设定为500K Hz:

@rp2.asm_pio(set_init=rp2.PIO.OUT_HIGH, autopush=True, push_thresh=8) 
def DHT11():
    # 状态机要用 500K hz的频率
    # 给DHT发送信号,保持低电位至少20毫秒
    set(pindirs, 1)
    set(pins, 0)

    set(y, 9)   # 延迟20毫秒
    label ('outer_20ms')         # (9+1) * (31+1) * (31+1) / 500kHz = 20.48 ms > 20 ms
    set(x, 31)          
    label ('inner_20ms')
    nop() [31]
    jmp(x_dec, 'inner_20ms')
    jmp(y_dec, 'outer_20ms') 
     
    # Look for the device response (~90us low pulse)
    # Add some delay to 'debounce' edges
    set(pindirs, 0)
    wait(1, pin, 0) [1]            # Wait for input to release (high pulse ~12us)
    wait(0, pin, 0) [1]            # Wait for device to drive low (low pulse ~83us)
    wait(1, pin, 0) [1]            # Wait for device to release line (high pulse ~86us)

    wrap_target()                # Read data bits from device

    # (once 40 bits have been read code will block here with input permanently high)
    wait(0,pin,0) [1]            # Wait for start of data + debounce (low pulse ~54us)
    wait(1,pin,0) [24]           # Wait for high signal and then 50us

    # After 50us input will indicate bit value (high pulse zero ~23us, one ~70us)
    in_(pins, 1)                 # Shift the data bit into the isr
    wrap()                       # Loop to read the remaining bits from device
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

实际上,那个无限循环也有点多余,我们只需要读40位的数据,也不用频繁地重启状态机,我用sm.put()后,给DHT发送开始信号,取出40位数据之后,就一直阻塞等待着。

40位(5个字节)数据分别代表湿度、温度的整数、小数部分,第5个字节是校验数据,最开始的处理40位数据的代码有点逻辑不清楚,也一并修改了。
在这里插入图片描述
最后的代码:

import machine
import utime
import rp2 

@rp2.asm_pio(set_init=rp2.PIO.OUT_HIGH, autopush=True, push_thresh=8) 
def DHT11():
    pull(block)
    # 状态机要用 500K hz的频率
    # 给DHT发送信号,保持低电位至少20毫秒
    set(pindirs, 1)
    set(pins, 0)

    mov(y, osr) #如果y为10000,则10000/500k = 20 ms
    label ('loop_20ms')
    jmp(y_dec, 'loop_20ms') 
     
    # Look for the device response (~90us low pulse)
    # Add some delay to 'debounce' edges
    set(pindirs, 0)
    wait(1, pin, 0) [1]            # Wait for input to release (high pulse ~12us)
    wait(0, pin, 0) [1]            # Wait for device to drive low (low pulse ~83us)
    wait(1, pin, 0) [1]            # Wait for device to release line (high pulse ~86us)

    set(y, 4)
    label('read_5bytes')
    set(x, 7)
    label('read_8bits')
    # (once 40 bits have been read code will block here with input permanently high)
    wait(0, pin, 0) [1]            # Wait for start of data + debounce (low pulse ~54us)
    wait(1, pin, 0) [24]           # Wait for high signal and then 50us
    # After 50us input will indicate bit value (high pulse zero ~23us, one ~70us)
    in_(pins, 1)                 # Shift the data bit into the isr
    jmp(x_dec, 'read_8bits')
    jmp(y_dec, 'read_5bytes') 


dht_data = machine.Pin(26) 
sm = rp2.StateMachine(0, DHT11, freq=500_000, set_base=dht_data, in_base=dht_data, jmp_pin=dht_data) 
sm.active(1)    

while True:
    sm.put(20 * 500) #持续20 ms的低电位信号
    
    data=[]
    for i in range(5):         #读40位(5字节)的数据
        data.append(sm.get())  
    print("data: " + str(data))
    
    checksum = (data[0] + data[1] + data[2] + data[3]) & 0xFF;
    if(data[4] == checksum):
        humidity = data[0] + data[1] / 10
        temperature = data[2] + data[3] / 10 #这个代码不严谨,data[2]是有符号的整数,负数代表零下温度
        print("湿度:{:6.1f}%,温度:{:6.1f} ℃".format(humidity, temperature))
    else:
        print("Checksum: failed")

    utime.sleep(1)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

这里sm.put(10000)是为了延迟20ms的低电位,我试着调小了这个数值,传感器仍能正常工作。

参考:

https://forums.raspberrypi.com/viewtopic.php?t=303606

https://www.i-programmer.info/programming/hardware/14572-the-pico-in-micropython-a-pio-driver-for-the-dht22.html?start=2

推荐阅读:
树莓派Pico开发系列文章

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/468157
推荐阅读
相关标签
  

闽ICP备14008679号