pico-micropython-examples/pio/pio_servo/pio_servo.py

88 wiersze
3.0 KiB
Python
Czysty Zwykły widok Historia

# Example of using PIO for Servo control
from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from time import sleep
@asm_pio()
def servo_trigger():
2021-04-08 12:55:52 +00:00
irq(clear, rel(1)) # Clear next relative ISR, allows servo code to run again
mov(y, x) # Counter is stored in x, copy to y for use
label("base")
jmp(y_dec, "base") # wait for programmed time
@asm_pio(sideset_init=PIO.OUT_LOW)
def servo_prog():
wrap_target()
irq(block, rel(0)) .side(0) # Wait here for IRQ to be released by trigger SM
pull(noblock) # pull new pulse length into fifo (pull fifo into OSR, if empty fifo copies X->OSR)
mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock (later)
mov(y, isr) .side(1) # ISR must be preloaded with base length
label("base")
jmp(y_dec, "base") # wait in state 1 for steps in y register (base pulse length)
mov(y, x)
label("var")
jmp(y_dec, "var") # wait in state 1 for steps in x register (variable pulse length)
wrap()
class Servo_Trigger:
'''
2021-04-08 12:55:52 +00:00
Run one statemachine in a loop, that clears IRQ every 20ms as the
base for the servo statemachine.
'''
def __init__(self, sm_idx):
# Trigger SM should output a pulse every 20ms for the servo SM to run
trig_target = 20 # ms
trig_frq = 10_000 #Hz
sm_trig = StateMachine(sm_idx, servo_trigger, freq=trig_frq)
2021-04-08 12:55:52 +00:00
trig_ctr = (trig_frq // 1000 * trig_target) - 3 # 3 instructions to have perfect 20ms on IRQ
sm_trig.put(trig_ctr)
sm_trig.exec("pull()")
sm_trig.exec("mov(x, osr)")
sm_trig.active(1)
class Servo:
'''
Accepts the servo setpoint via FIFO input.
2021-04-08 12:55:52 +00:00
It raises and waits for IRQ after the positive part of the pulse has been output.
The other statemachine should clear IRQ every 20ms so that a new pulse is output cyclically.
Preload the ISR with the base duration (fixed pulse length for position 0°)
Send position data via FIFO into the OSR (variable pulse length for 0°..max)
'''
def __init__(self, sm_idx, pin):
self.baseFrq = 1_000_000 # 1MHz = 1us clock base
self.base_pulse = 1000 # us, base width of pulse
self.free_pulse = 1000 # us, max. additional length set by percent
self.sm = StateMachine(sm_idx, servo_prog, freq=self.baseFrq, sideset_base=Pin(pin))
# Use exec() to load max count into ISR
self.sm.put(self.base_pulse)
self.sm.exec("pull()")
self.sm.exec("mov(isr, osr)")
self.sm.active(1)
def pos(self, n):
'''Set servo position. Range 0.0 to 1.0'''
self.sm.put(int(self.free_pulse*n))
# Trigger needs to be the sm before the servo, so the IRQs set by rel(n) match
trig = Servo_Trigger(0)
s = Servo(1, 16) # phys IO on pin 16
trig2 = Servo_Trigger(2)
s2 = Servo(3, 25) # Builtin LED
2021-04-08 14:33:52 +00:00
while True:
for p in range(10+1):
s.pos(p/10)
sleep(0.5)