Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Example for reading a PWM signal with PIO #30

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions pio/pio_pwmin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from machine import Pin, PWM
from rp2 import PIO, StateMachine, asm_pio
from time import sleep, ticks_ms, ticks_diff

pwm_out = PWM(Pin(16))

pwm_out.freq(100)
pwm_out.duty_u16((2**16-1)//2)

@asm_pio()
def pwmin():
pull(block) # wait for activation by doing a blocking pull on the input
mov(x, invert(null)) # invert(null) = Max. 32 Bit value

wait(1, pin, 0) # wait for a full PWM cycle to start measurement
wait(0, pin, 0) # wait for pin to be low

label("count_low")
jmp(pin, "out_low") # jump to output if pin is high
jmp(x_dec, "count_low") # jump back to count loop, decrement X
label("out_low")

mov(isr, x) # move x into ISR for outputting low counter of PWM signal
push(noblock) # push into fifo

label("count_high")
jmp(x_dec, "next") # count down X, jump to next instruction
label("next")
jmp(pin, "count_high") # as long as the pin is high, jump back up to continue countdown

mov(isr, x) # move x into ISR for outputting the total period of the signal
push(noblock) # push into fifo
irq(0)

base_frq = 100_000_000
sm = rp2.StateMachine(0, pwmin, freq=base_frq, jmp_pin=Pin(16), in_base=Pin(16))
sm.active(1)

'''
W A R N I N G

This example code will hang, if no PWM signal is present,
e.g. when the PWM is at 0% or 100% duty cycle.

'''
def readPwm(sm):
# Send data to start measurement
sm.put(0)

low = sm.get()
total = sm.get()

# Convert to duration
low = 2**32 - 1 - low
total = 2**32 - 1 - total

# Total is in ticks, based on base_frq.
# Due to the code, it counts by 1 for every 2 clock cycles
period = total / base_frq * 2

return {
"period":period,
"duty_low":low/total,
"duty":1.0-(low/total),
"freq":1/period
}

print("PWMIn Selfcheck")

for f in [100, 200, 500,
1_000, 2_000, 5_000,
10_000, 20_000, 50000,
100_000, 200_000, 500_000]:
for d in [0.1, 0.25, 0.5, 0.75, 0.9]:
# Set new output
pwm_out.freq(f)
pwm_out.duty_u16(int((2**16-1)*d))

# Wait a bit
sleep(0.5)

read = readPwm(sm)
diff_freq = abs(read["freq"]-f)
diff_duty = abs(read["duty"]-d)

if (diff_freq <= f*0.01) and (diff_duty < 0.01):
print("{} Hz / {} duty OK".format(f, d))
else:
print("{} Hz / {} duty OUTSIDE LIMITS ---------".format(f, d))

print("\t{:.2f} Hz / {:.2f} duty cycle measured".format(read["freq"], read["duty"]))

print("\tDiff: {:.2f} Hz".format(diff_freq))
print("\tDiff: {:.2%} of freq".format(abs(1.0-read["freq"]/f)))
print("\tDiff: {:.2%} duty cycle".format(diff_duty))
print("")