prettyprint

2022年10月14日 星期五

Raspberry Pi Pico PIO(Programmable IO) Episode 3: DHT11 (Humidity and Temperature sensor)

本文介紹使用Raspberry Pi Pico PIO功能來讀取DHT11溫濕度資料,顯示在1602A LCD上。


一、硬體接線:

LCD 1602A:     Raspberry Pi Pico

VSS:                GND
VDD:              Pin40(VBUS), 5V
VO:                10k可變電阻
RS:                 Pin 11
RW:                GND
E:                  Pin 10
D4:               Pin 6
D5:               Pin 7
D6:               Pin 8
D7:               Pin 9
A:                5V
K:                GND

DHT11        Raspberry Pi Pico
Pin2:             Pin 16

VDD:         5V


二、PIO程式碼與DHT11 datasheet對照說明:







設定autopush=True, push_thresh=32所以前4bytes共32bits,StateMachine自動push到RX_FIFO中。
最後第五個byte(CRC)因為未滿threshold所以需要下push指定到RX_FIFO。

三、成果影片:



四、程式碼:
 DHT11.py
from machine import Pin
import rp2
from utime import sleep_ms
class DHT11():
    """ output humidity(rh) and temperature(temp), call get_data to get values """
    @rp2.asm_pio(set_init=rp2.PIO.OUT_HIGH, autopush=True,  push_thresh=32, in_shiftdir=rp2.PIO.SHIFT_LEFT)
    def dht11():
        wrap_target()
        set(pindirs,1)                # set pin as output
        set(pins,0)                   # set low for 20ms (at least 18ms)
        set(x,29)                     #freq=500000, 1 cycle = 2us (30*30*11*2 = 19.8ms)
        label("start_loop_1")
        set(y,29)
        label("start_loop_2")
        jmp(y_dec, "start_loop_2") [10]
        jmp(x_dec, "start_loop_1")
        set(pins,1) [14]             # pull-up for 2us*15 = 30us (20~40 us)
        set(pindirs,0)               # set pin as input
        wait(0,pin,0) [19] 
        nop() [19]                   # wait for low 2*(20+20)=80us
        wait(1,pin,0) [19] 
        nop()  [19]                  # wait for high 2*(20+20)=80us
        
        set(x,4)                     # 5 bytes
        label("bytes")
        set(y,7)                     # each byte: 8 bits
        label("bits")
        wait(0,pin,0) [14]           # LOW:  wait for 2us*15=30us (50us)  
        wait(1,pin,0) [19]           # HIGH: wait at least 2*20=40 us (26~28us: 0, 70us: 1)
        in_(pins,1)   
        jmp(y_dec,"bits")            # autopush and push_thresh=32, auto push 4 data byte
        jmp(x_dec,"bytes")
        push()                       # push last one CRC byte
        nop() [31]                   #transmition completed, low for 50us and pull-high
        wait(0,pin,0)                # block for next
        
        wrap()
    
    def __init__(self, pin_num, smid=0):
        self.pin = Pin(pin_num, Pin.IN, Pin.PULL_UP)
        self.sm=rp2.StateMachine(smid, self.dht11, freq=500000, in_base=self.pin, set_base=self.pin)
        self.sm.active(1)
        self.rh=0.0
        self.temp=0.0
    
    def get_data(self):
        self.sm.restart()
        data=self.sm.get()
        checksum=self.sm.get()
        bytes=[]
        for i in range(3,-1,-1):
            bytes.append((data>>8*i)&0xff)
        if ((bytes[0]+bytes[1]+bytes[2]+bytes[3]) == checksum):
            self.rh = float('{}.{}'.format(bytes[0], bytes[1]))
            self.temp = float('{}.{}'.format(bytes[2], bytes[3]))
        else:
            self.rh=0.0
            self.temp=0.0
        del bytes
        sleep_ms(1120) #Sampling period at intervals should be no less than 1 second.
LCD1602A.py
# base on https://github.com/wjdp/micropython-lcd/blob/master/lcd.py

from machine import Pin
from utime import sleep_us

class LCD1602A():
    
    PIN_NAMES = ['RS','E','D4','D5','D6','D7']
    
    # Define some device constants
    LCD_WIDTH = 16    # Maximum characters per line
    # Designation of T/F for character and command modes
    LCD_CHR = True
    LCD_CMD = False

    LINES = {
        0: 0x80, # LCD RAM address for the 1st line
        1: 0xC0, # LCD RAM address for the 2nd line
        # Add more if desired
    }

    # Timing constants
    E_PULSE = 50
    E_DELAY = 50

    def __init__(self, rs, e,*,d4=None, d5=None, d6=None, d7=None):
        self.pins={}
        self.PINS=[rs, e, d4, d5, d6, d7]
        # Initialise pins
        for pin, pin_name in zip(self.PINS, self.PIN_NAMES):
            self.pins['LCD_'+pin_name] = Pin(pin, Pin.OUT)
        # Initialise display
        self.lcd_byte(0x33,self.LCD_CMD)
        self.lcd_byte(0x32,self.LCD_CMD)
        self.lcd_byte(0x28,self.LCD_CMD)
        self.lcd_byte(0x0C,self.LCD_CMD)
        self.lcd_byte(0x06,self.LCD_CMD)
        self.lcd_byte(0x01,self.LCD_CMD)
        

    def clear(self):
        # Clear the display
        self.lcd_byte(0x01,self.LCD_CMD)

    def set_line(self, line):
        # Set the line that we're going to print to
        self.lcd_byte(self.LINES[line], self.LCD_CMD)

    def set_string(self, message):
        # Pad string out to LCD_WIDTH
        # message = message.ljust(LCD_WIDTH," ")
        m_length = len(message)
        if m_length < self.LCD_WIDTH:
            short = self.LCD_WIDTH - m_length
            blanks=str()
            for i in range(short):
                blanks+=' '
            message+=blanks
        for i in range(self.LCD_WIDTH):
            self.lcd_byte(ord(message[i]), self.LCD_CHR)

    def lcd_byte(self, bits, mode):
        # Send byte to data pins
        # bits = data
        # mode = True  for character
        #        False for command

        self.pin_action('LCD_RS', mode) # RS

        # High bits
        self.pin_action('LCD_D4', False)
        self.pin_action('LCD_D5', False)
        self.pin_action('LCD_D6', False)
        self.pin_action('LCD_D7', False)
        if bits&0x10==0x10:
            self.pin_action('LCD_D4', True)
        if bits&0x20==0x20:
            self.pin_action('LCD_D5', True)
        if bits&0x40==0x40:
            self.pin_action('LCD_D6', True)
        if bits&0x80==0x80:
            self.pin_action('LCD_D7', True)

        # Toggle 'Enable' pin
        self.udelay(self.E_DELAY)
        self.pin_action('LCD_E', True)
        self.udelay(self.E_PULSE)
        self.pin_action('LCD_E', False)
        self.udelay(self.E_DELAY)

        # Low bits
        self.pin_action('LCD_D4', False)
        self.pin_action('LCD_D5', False)
        self.pin_action('LCD_D6', False)
        self.pin_action('LCD_D7', False)
        if bits&0x01==0x01:
            self.pin_action('LCD_D4', True)
        if bits&0x02==0x02:
            self.pin_action('LCD_D5', True)
        if bits&0x04==0x04:
            self.pin_action('LCD_D6', True)
        if bits&0x08==0x08:
            self.pin_action('LCD_D7', True)

        # Toggle 'Enable' pin
        self.udelay(self.E_DELAY)
        self.pin_action('LCD_E', True)
        self.udelay(self.E_PULSE)
        self.pin_action('LCD_E', False)
        self.udelay(self.E_DELAY)

    def udelay(self, us):
        # Delay by us microseconds, set as function for portability
        #pyb.udelay(us)
        sleep_us(us)

    def pin_action(self, pin, high):
        # Pin high/low functions, set as function for portability
        if high:
            self.pins[pin].on()
        else:
            self.pins[pin].off()
            
            

main.py
from machine import Pin
from DHT import DHT11
from LCD1602A import LCD1602A
import utime


lcd=LCD1602A(11,10, d4=6,d5=7,d6=8,d7=9)
dht=DHT11(16)

while True:
    dht.get_data()
    lcd.set_line(0)
    lcd.set_string("Temp:"+str(dht.temp)+"C")
    lcd.set_line(1)
    lcd.set_string("RH:  "+str(dht.rh)+"%")
    #utime.sleep(0.5)

沒有留言:

張貼留言