PIO measuring time between pin change

RP2040 based microcontroller boards running MicroPython.
Target audience: MicroPython users with an RP2040 boards.
This does not include conventional Linux-based Raspberry Pi boards.
Post Reply
podex
Posts: 10
Joined: Tue Nov 16, 2021 9:03 am

PIO measuring time between pin change

Post by podex » Fri Nov 19, 2021 6:41 pm

Hi,

I would like to measure the time of a pulse in low level. So I used the PWM example in order to generate a pulse on the LED. And I wrote a PIO script that first waits for the pin being low and then decrements scratch register x untkl the pin goes high. At least this was the intention of this script. But it does not work, I hope that somebody can give me a hint what I did wrong or misunderstood.

Many thanks for any help!

KR,
Ch.

Code: Select all


# Example using PWM to fade an LED.

import time
import rp2
from machine import Pin, PWM

# measure time between falling edges
@rp2.asm_pio(set_init=rp2.PIO.IN_HIGH)
def perMeas():
    label('mainloop')
    set(x,0)
    wait(0, pin, 0)
    label('lo_loop')
    jmp(x_dec,'next')
    label('next')
    jmp(pin,'hi')
    jmp('lo_loop')
    label('hi')
    mov(isr,x)
    push()
    jmp('mainloop')
    
# Construct PWM object, with LED on Pin(25).
pwm = PWM(Pin(25))

# Set the PWM frequency.
pwm.freq(10)

# Fade the LED in and out a few times.
duty = 10
direction = 1
# for _ in range(8 * 256):

sm4 = rp2.StateMachine(1, perMeas, freq=200000, in_base=Pin(25)) # pin16 used
sm4.active(1)

while True:
    duty += direction
    if duty > 255:
        duty = 255
        direction = -1
    elif duty < 0:
        duty = 0
        direction = 1
    pwm.duty_u16(duty * duty)
    time.sleep(1.0)

    while sm4.rx_fifo():
        print(2**32-sm4.get(),end=' ')
    else:
        print('empty',end=' ')
    print()    


User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

Re: PIO measuring time between pin change

Post by Roberthh » Fri Nov 19, 2021 9:26 pm

I made scripts to create and time pulses using pio. See the scripts pulses at https://github.com/robert-hh/RP2040-Examples

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: PIO measuring time between pin change

Post by pythoncoder » Sat Nov 20, 2021 10:10 am

This script returns the period between pulses and also the pulse duration (mark) and duty ratio (mark/period):

Code: Select all

from machine import Pin, PWM
from rp2 import PIO, StateMachine, asm_pio
import time

@rp2.asm_pio(set_init=rp2.PIO.IN_LOW, autopush=True, push_thresh=32)
def period():
    wrap_target()
    set(x, 0)
    wait(0, pin, 0)  # Wait for pin to go low
    wait(1, pin, 0)  # Low to high transition
    label('low_high')
    jmp(x_dec, 'next') [1]  # unconditional
    label('next')
    jmp(pin, 'low_high')  # while pin is high
    label('low')  # pin is low
    jmp(x_dec, 'nxt')
    label('nxt')
    jmp(pin, 'done')  # pin has gone high: all done
    jmp('low')
    label('done')
    in_(x, 32)  # Auto push: SM stalls if FIFO full
    wrap()

@rp2.asm_pio(set_init=rp2.PIO.IN_LOW, autopush=True, push_thresh=32)
def mark():
    wrap_target()
    set(x, 0)
    wait(0, pin, 0)  # Wait for pin to go low
    wait(1, pin, 0)  # Low to high transition
    label('low_high')
    jmp(x_dec, 'next') [1]  # unconditional
    label('next')
    jmp(pin, 'low_high')  # while pin is high
    in_(x, 32)  # Auto push: SM stalls if FIFO full
    wrap()

pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
sm0 = rp2.StateMachine(0, period, in_base=pin16, jmp_pin=pin16)
sm0.active(1)
sm1 = rp2.StateMachine(1, mark, in_base=pin16, jmp_pin=pin16)
sm1.active(1)

pwm = PWM(Pin(17))
pwm.freq(1000)
pwm.duty_u16(0xffff // 3)

# Clock is 125MHz. 3 cycles per iteration, so unit is 24.0ns
def scale(v):
    return (1 + (v ^ 0xffffffff)) * 24e-6  # Scale to ms

while True:
    period = scale(sm0.get())
    mark = scale(sm1.get())
    print(period, mark, mark/period)
    time.sleep(0.2)
Peter Hinch
Index to my micropython libraries.

podex
Posts: 10
Joined: Tue Nov 16, 2021 9:03 am

Re: PIO measuring time between pin change

Post by podex » Mon Nov 22, 2021 7:15 pm

Hi,

Many thanks, that's exactly what I am searching for! :)

My first thought is not true because when I change the pin to 25, the PIO works. I will check and double-check my code to see why it's not working as expected.

KR,
Christof

podex
Posts: 10
Joined: Tue Nov 16, 2021 9:03 am

Re: PIO measuring time between pin change

Post by podex » Fri Jan 07, 2022 12:44 pm

After a while I come back to the time measurement. It took a little bit because of the festival season and I had to get the answer of the sensor correct, I have level-shifters with pull-up resistors and have to switch from drive to open-drain. But now it works, now I can focus on the measurement between falling edges.

I tried to modify my script with your example in mind. There I got a strange effect, I did not expect:

Code: Select all

from machine import Pin
import time,utime,rp2

@rp2.asm_pio(set_init=rp2.PIO.IN_HIGH, autopush=True, push_thresh=16) # push() is done automatically win in_()!
def period():
    set(x, 1023)
    in_(x, 16)  # Auto push : SM stalls if FIFO full
    wrap_target()
    nop()
    wrap()

pin16 = Pin(16, Pin.IN, Pin.PULL_UP) 
sm_meas = rp2.StateMachine(0, period, freq=100000, in_base=pin16, jmp_pin=pin16)

sm_meas.active(1)
while True:
    print("\nrestart ... ",end="")
    sm_meas.restart()
    print("got ",end="")
    while sm_meas.rx_fifo()>0:
        period_ticks=sm_meas.get()
        print(period_ticks,end=" ")
    utime.sleep(1)
With this code I would expect that I get the value 1023 printed, but it's not the case:

Code: Select all

MicroPython v1.17 on 2021-09-02; Raspberry Pi Pico with RP2040
Type "help()" for more information.
>>> %Run -c $EDITOR_CONTENT

restart ... got 0 0 
restart ... got 0 
restart ... got 0 
What's my fault, it also does not work with bit-range 32...

Thanks for any hints!
Christof

podex
Posts: 10
Joined: Tue Nov 16, 2021 9:03 am

Re: PIO measuring time between pin change

Post by podex » Fri Jan 07, 2022 2:13 pm

Maybe I should explain what I would like to do. I have a sensor with SPC interface (for an explaination see https://www.nxp.com/docs/en/application-note/AN4219.pdf) where the time between falling edges of the signal line defines the data transferred.

This is a screenshot of the Master trigger pulse with 33us low time (measurement of channel 1) and the synchronization pulse starting 272us after the start of the trigger pulse ending at 440us. So the unit time of this sensor is 3us.
DS1Z_QuickPrint18.png
DS1Z_QuickPrint18.png (48.27 KiB) Viewed 18012 times
In order to decode the sensor output, I need the times of the falling edges. This with the unit time gives the data nibbles.

Of course I could do it with one of the Cortex-M0+ cores, but I think this is also possible with PIO "in the background".

My concept would be the following:
The register X is set to a predefined value with the trigger pulse and then decremented. At each falling edge the register value is then pulled into the RX-FIFO. At the end from the deltas of the time of the falling edges the data nibbles can be computed with the help of the length of the synchronization pulse.

KR,
Christof

podex
Posts: 10
Joined: Tue Nov 16, 2021 9:03 am

Re: PIO measuring time between pin change

Post by podex » Fri Jan 07, 2022 7:00 pm

Well, maybe I should have read the docu a little bit more carefully. set() works with data up to 31, because the number of bits in the command is limited.

Code: Select all

@rp2.asm_pio(set_init=rp2.PIO.IN_HIGH, autopush=True, push_thresh=32) # push() is done automatically win in_()!
def period():
    set(x, 31)
    in_(x, 32)  # Auto push : SM stalls if FIFO full
    wrap_target()
    nop() [1]
    wrap()
This works as expected.

hippy
Posts: 130
Joined: Sat Feb 20, 2021 2:46 pm
Location: UK

Re: PIO measuring time between pin change

Post by hippy » Sat Jan 08, 2022 6:26 pm

podex wrote:
Fri Jan 07, 2022 7:00 pm
Well, maybe I should have read the docu a little bit more carefully. set() works with data up to 31, because the number of bits in the command is limited.
The "@asm_pio" assembler is unfortunately almost completely lacking when it comes to error reporting and identifying user mistakes, is likely silently creating an instruction which is entirely different to what you wanted.

The other common mistakes which don't get reported are using 'set' with a register source, 'mov' with a numeric constant, and using registers which are not valid for a particular instruction.

If I ever get my version of the assembler which includes error reporting and allows pure text source to be specified completed and tested I will publish that.

Post Reply