Using the PIO to Interface with the DHT11 and DHT22 sensors
The DHT11 and DHT22 are popular sensors for measuring temperature and humidity. While connecting these sensors to a microcontroller is very simple, communicating with them demands generating and measuring times of the order of microseconds. Let's see how the RP2040's PIO can help us.
How the DHT11 and DHT22 Communicates
Communication between these sensors and a microcontroller uses a single bi-directional signal. There is an internal pull-up resistor that forces this signal high when it is not driven by the microcontroller or the sensor. The image bellow shows the data pin when a reading is done:
Left: full reading, Right: zoom on the first byte |
To request a reading, the microcontroller must drives the pin low for at least 18ms (DHT11) or 1ms (DHT22). After that, it reconfigure the pin for input to get the sensor response,
- 2 bytes for humidity
- 2 bytes for temperature
- 1 byte for error check (it should be the sum of the previous four bytes)
The encoding of the values is different for the two models.
The DHT11 can read temperatures form 0 to 50C, with a 2 degree precision and send the values as separated integer and decimal parts (the decimal part for temperature should be zero):
- If the two bytes for humidity are 86 and 2, the humidity is 86.2%
- If the two bytes for temperature are 19 and 0, the temperature is 19.0C
- If the two bytes for humidity are 2 and 86, , the humidity is (2*256+86)*0.1 = 59.8%
- If the two bytes for temperature are 1 and 8, the temperature is (1*256+8)*0.1 = 33.6ºC.
- If the two bytes for temperature are 129 e o 8, the temperature is -(1*256+8)*0.1 = -33.6ºC.
- Put the pin in output mode
- Wait for the processor to write a value
- Keep the pin low by the time defined by this value
- Change the pin for input
- Wait for the acknoledge
- Read bits
- Send back each byte received
- Set a clock of 1.4MHz (so 1 cycle is about 0,7 usec)
- Select the pin connected to the sensor for input, set and jump condition
- Configures the PIO
- Starts the PIO
- Writes the initial pulse value (969 if DHT11 or 54 if DHT22)
- Reads 5 bytes from the PIO
- Stops the PIO
import utime import rp2 from rp2 import PIO, asm_pio from machine import Pin # PIO program # automatically pushes each 8 bits of received data in the FIFO @asm_pio(set_init=(PIO.OUT_HIGH),autopush=True, push_thresh=8) def DHT_PIO(): # wait for a request pull() # keeps pin in 0 for the requested time set(pindirs,1) # set pin to output set(pins,0) # set pin low mov (x,osr) label ('waitx') nop() [25] jmp(x_dec,'waitx') # wait time*26/clock=x # reads the acknoledge set(pindirs,0) # change pin to input wait(1,pin,0) # wait for high level wait(0,pin,0) # wait for initial pulse wait(1,pin,0) wait(0,pin,0) # wait for first bit # read the bits label('readdata') wait(1,pin,0) # wait for pin to go high set(x,20) # register x is the timeout for low label('countdown') jmp(pin,'continue') # count while pin is high # pin went low before timeout -> bit 0 set(y,0) in_(y, 1) # puts a zero in result jmp('readdata') # read next bit # pin is still high label('continue') jmp(x_dec,'countdown') # decrement count # timeout -> bit 1 set(y,1) in_(y, 1) # puts a one in result wait(0,pin,0) # wait for pin get back to low jmp('readdata') # ler o próximo bit DHT11 = 0 DHT22 = 1 class DHT: # Construtor # dataPin: data pin # model: DHT11 ou DHT22 # smID: state machine id def __init__(self, dataPin, model, smID=0): self.dataPin = dataPin self.model = model self.smID = smID self.sm = rp2.StateMachine(self.smID) self.lastreading = 0 self.data=[] # read sensor def read(self): data=[] self.sm.init(DHT_PIO,freq=1400000,set_base=self.dataPin, in_base=self.dataPin,jmp_pin=self.dataPin) self.sm.active(1) if self.model == DHT11: self.sm.put(969) # wait 18 miliseconds else: self.sm.put(54) # wait 1 milisecond for i in range(5): # read 5 bytes data.append(self.sm.get()) self.sm.active(0) total=0 for i in range(4): total=total+data[i] if data[4] == (total & 0xFF): # checksum ok, salvar os dados self.data = data self.lastreading = utime.ticks_ms() return True else: return False # get data by reading or using stored data def getData(self): # make sure we have some data while len(self.data) == 0: if not self.read(): utime.sleep_ms(2000) # only read if more than 2 seconds since previous reading agora = utime.ticks_ms() if self.lastreading > agora: self.lastreading = agora # counter wraped up if (self.lastreading+2000) < agora: self.read() # returns the humidity def umidade(self): self.getData() if self.model == DHT11: return self.data[0] + self.data[1]*0.1 else: return ((self.data[0] << 8) + self.data[1]) * 0.1 # returns temperature def temperature(self): self.getData() if self.model == DHT11: return self.data[2] + self.data[3]*0.1 else: s = 1 if (self.data[2] & 0x80) == 1: s = -1 return s * (((self.data[2] & 0x7F) << 8) + self.data[3]) * 0.1 #main program dht_data = Pin(16, Pin.IN, Pin.PULL_UP) dht = DHT(dht_data, DHT22, 0) while True: print("Humidity: %.1f%%, Temperature: %.1fC" % (dht.umidade(), dht.temperature())) utime.sleep_ms(3000)
Comments
Post a Comment