DHT22 on Pico
-
- Posts: 51
- Joined: Thu Dec 27, 2018 11:38 pm
- Location: Québec, Canada
DHT22 on Pico
This is my version using PIO function to read the DHT22.
Still try to figure out how the StateMachine works. I can't find good documentation. Just parcel of it all spread out.
The way I made the code is limit the time to wait for the pulse lenght, 'nop() [31]' = clock * 32, if the pulse is to long then just put '1' in the FIFO. This way the return checksum won't be valid.
the sm.get() is nice but I wasn't able to find a function to see if any data is available. This prevent my first idea to create a lock up.
This is my DHT22 class https://github.com/danjperron/PicoDHT22
Daniel
Still try to figure out how the StateMachine works. I can't find good documentation. Just parcel of it all spread out.
The way I made the code is limit the time to wait for the pulse lenght, 'nop() [31]' = clock * 32, if the pulse is to long then just put '1' in the FIFO. This way the return checksum won't be valid.
the sm.get() is nice but I wasn't able to find a function to see if any data is available. This prevent my first idea to create a lock up.
This is my DHT22 class https://github.com/danjperron/PicoDHT22
Daniel
Re: DHT22 on Pico
Thanx! It all worked like a charm. Just... How to set the clock?
Thanx too for the format-of-the-numbers, was looking for it a long time...
Hope to see more ?
Thanx too for the format-of-the-numbers, was looking for it a long time...
Hope to see more ?
Re: DHT22 on Pico
To answer my own question about setting the right time:
https://www.raspberrypi.org/forums/view ... 5#p1810708
Also a brilliant solution, All credits for this champ! (DWiskow)
https://www.raspberrypi.org/forums/view ... 5#p1810708
Also a brilliant solution, All credits for this champ! (DWiskow)
Re: DHT22 on Pico
Looking at the data sheet, I made another version of a PIO script, which takes less care about pulse times, except for the 0/1 distinction of the data. I miss some kind of signalling from the state machine. I did not try IRQ yet. That may be an option.
During my tests I had some strange effects on jmp(not_x, ...) and jmp(not_y, ...) instructions. I'll test that separately.
During my tests I had some strange effects on jmp(not_x, ...) and jmp(not_y, ...) instructions. I'll test that separately.
Code: Select all
# the clock is set to 200 kHz or 5µs per tick
@rp2.asm_pio(
in_shiftdir=rp2.PIO.SHIFT_LEFT,
set_init=rp2.PIO.OUT_HIGH,
autopull=False,
autopush=True,
push_thresh=8
)
def DHT_pio():
pull() # get the number of clock cycles
mov(x, osr) # for the start pulse
set(pindirs, 1) # set to output
set(pins, 0)
# create the start pulses
label("start_pulse")
jmp(x_dec, "start_pulse")
set(pins, 1) [7] # and wait six ticks
set(pindirs, 0) # set back to input mode
nop() [2] # wait two ticks
jmp(pin, "error") # if not 0, not DHT response
wait(1, pin, 0) # wait for the first 1
label("wait_0")
wait(0, pin, 0) # wait for the first or next 0
label("wait_1")
wait(1, pin, 0) # wait for the bit pulse
nop() [7] # wait another 40 µs
in_(pins, 1) # get one bit
jmp(pin, "wait_0") # if bit == 1, wait for 0
jmp("wait_1") # Otherwise wait for the next bit
label("error")
mov(isr, invert(y))
push(noblock)
-
- Posts: 51
- Joined: Thu Dec 27, 2018 11:38 pm
- Location: Québec, Canada
Re: DHT22 on Pico
I didn't want to go on that road because if the sensor failed it is stuck!!!! This is a big problem.Looking at the data sheet, I made another version of a PIO script, which takes less care about pulse times, except for the 0/1
This is why if time out I put 1 . Normally it is good enough to create a checksum error !
The other method is to check the fifo empty like I did with the period counter but that involve mem32 function to check fifo empty register.
About your start pulse. The DHT11 needs at least 20ms compare to the DHT22 .
Re: DHT22 on Pico
Yes, the StateMachine class would need some additional methods for status and aborting. That would allow a more general error handling. Until then, it could be easily implemented using viper code, which makes direct memory access easy and fast.
In the pico-sdk, the following functions are available for peeking the FIFO states and forcing a reset:
static inline void pio_sm_restart(PIO pio, uint sm)
static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm)
static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm)
static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm)
static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm)
static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm)
static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm)
So maybe at least sm.active(1) could also force a reset.
About DHT11: Like in your code, just the initial value to be put into the state machine has to be changed. I did not show that part of the code.
In the pico-sdk, the following functions are available for peeking the FIFO states and forcing a reset:
static inline void pio_sm_restart(PIO pio, uint sm)
static inline bool pio_sm_is_rx_fifo_full(PIO pio, uint sm)
static inline bool pio_sm_is_rx_fifo_empty(PIO pio, uint sm)
static inline uint pio_sm_get_rx_fifo_level(PIO pio, uint sm)
static inline bool pio_sm_is_tx_fifo_full(PIO pio, uint sm)
static inline bool pio_sm_is_tx_fifo_empty(PIO pio, uint sm)
static inline uint pio_sm_get_tx_fifo_level(PIO pio, uint sm)
So maybe at least sm.active(1) could also force a reset.
About DHT11: Like in your code, just the initial value to be put into the state machine has to be changed. I did not show that part of the code.
Re: DHT22 on Pico
Until the statemachine module is eventually expanded, I quickly made four tiny functions to access the FIFO status and ot restart the state machine. The argument is the integer number of the state machine, not the sm object.
Using that, the DHTpio code can be further simplified. The python code can check for a timeout, and if that happens, the current sampling is aborted and the state machine is restarted.
The python code can check for a timeout, and if that happens, the current sampling is aborted and the state machine is restarted. Like below:
Code: Select all
PIO0_BASE = const(0x50200000)
PIO1_BASE = const(0x50300000)
PIO_CTRL = const(0)
PIO_FSTAT = const(1)
PIO_FLEVEL = const(3)
@micropython.viper
def sm_restart(sm: int):
if sm < 4: # PIO 0
pio = ptr32(uint(PIO0_BASE))
else: # PIO1
pio = ptr32(uint(PIO1_BASE))
sm %= 4
pio[PIO_CTRL] = 1 << (sm + 4)
@micropython.viper
def sm_rx_fifo_level(sm: int) -> int:
if sm < 4: # PIO 0
pio = ptr32(uint(PIO0_BASE))
else: # PIO1
pio = ptr32(uint(PIO1_BASE))
sm %= 4
return (pio[PIO_FLEVEL] >> (8 * sm + 4)) & 0x0f
@micropython.viper
def sm_tx_fifo_level(sm: int) -> int:
if sm < 4: # PIO 0
pio = ptr32(uint(PIO0_BASE))
else: # PIO1
pio = ptr32(uint(PIO1_BASE))
sm %= 4
return (pio[PIO_FLEVEL] >> (8 * sm)) & 0x0f
@micropython.viper
def sm_fifo_status(sm: int) -> int:
if sm < 4: # PIO 0
pio = ptr32(uint(PIO0_BASE))
else: # PIO1
pio = ptr32(uint(PIO1_BASE))
return pio[PIO_FSTAT]
Code: Select all
# the clock is set to 200 kHz or 5µs per tick
@rp2.asm_pio(
in_shiftdir=rp2.PIO.SHIFT_LEFT,
set_init=rp2.PIO.OUT_HIGH,
autopull=False,
autopush=True,
push_thresh=8
)
def DHT_pio():
pull() # get the number of clock cycles
mov(x, osr) # for the start pulse
set(pindirs, 1) # set to output
set(pins, 0)
label("start_pulse")
jmp(x_dec, "start_pulse") # test for more bits
set(pins, 1) [7] # and wait six ticks
set(pindirs, 0) # set back to input mode
wait(1, pin, 0)
label("wait_0")
wait(0, pin, 0)
label("wait_1")
wait(1, pin, 0)
nop() [7] # wait another 40 µs
in_(pins, 1) # get one bit
jmp(pin, "wait_0") # if one, wait for 0
jmp("wait_1") # Otherwise wait for the next bit
Code: Select all
pin_base = Pin(10, Pin.OUT, value=1)
sm_freq = 200_000
sm_tick = 1_000_000 // sm_freq
sm = rp2.StateMachine(0, DHT_pio, freq=sm_freq,
set_base=pin_base, in_base=pin_base,
out_base=pin_base, jmp_pin=pin_base)
recv = array.array("H", bytearray(10))
sm.put(1000 // sm_tick) # number of wait cycles for the start pulse
sm.active(1)
index = 0
for i in range(1000):
if sm_rx_fifo_level(0) > 0:
recv[index] = sm.get()
index += 1
if index == 5:
break
else:
time.sleep_ms(1)
else:
print("Acquistion error")
sm_restart(0)
sm.active(0)
print([hex(i) for i in recv])
Re: DHT22 on Pico
I made a PR to integrate three of these functions into the StateMachine module. https://github.com/micropython/micropython/pull/6973
-
- Posts: 51
- Joined: Thu Dec 27, 2018 11:38 pm
- Location: Québec, Canada
Re: DHT22 on Pico
My understanding about StateMachine on PIO is that we have 32 addresses available to put code there and it will run without using the cpu time.
Is putting code outside the StateMachine and adding functions to be controlled by the cpu better? Maybe simpler but we will sacrifice cpu time against a standalone process.
I'm agree that we should have fifo status function.
I.M.O. We should use interrupts and minimize the intervention of the cpu as possible. One function to start the conversion and one callback function to get the data when it is ready. This will let the cpu doing other processes in the mean time.
Is putting code outside the StateMachine and adding functions to be controlled by the cpu better? Maybe simpler but we will sacrifice cpu time against a standalone process.
I'm agree that we should have fifo status function.
I.M.O. We should use interrupts and minimize the intervention of the cpu as possible. One function to start the conversion and one callback function to get the data when it is ready. This will let the cpu doing other processes in the mean time.
Re: DHT22 on Pico
It's not about doing stuff in cpu that the PIO can do better. My aim was to allow clean-up actions to be enforced by the cpu such that the PIO code can be shorter and simpler. The restart() function is not completely what I hoped it would do. It seems not to reset the PC of the state machine.
I did not try interrupts yet. That is probably the good way for the PIO to signal a state to the cpu. Only the opposite seems missing.
Another method of possibly resolving error states is using sm.exec() to run additional statements out-of-band, like a missing push(). I will give it a try.
I did not try interrupts yet. That is probably the good way for the PIO to signal a state to the cpu. Only the opposite seems missing.
Another method of possibly resolving error states is using sm.exec() to run additional statements out-of-band, like a missing push(). I will give it a try.