Stepper motors with PIO

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
Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Stepper motors with PIO

Post by Leonti » Sat Apr 03, 2021 6:24 am

Hi!

I'm trying to write my first PIO program to control stepper motors.
I'd like to be able to tell it how many steps to make and what would be a delay between steps.
After hours and hour of reading docs and watching Youtube videos I'm still not able to write a working program.

Here is what I have so far:

Code: Select all

import time
import rp2
from machine import Pin

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def stepper():
  pull()
  mov(x, osr) # delay

  pull()
  mov(y, osr) # steps

  label("step")
  set(pins, 1)
  label("delay")
  set(pins, 0)
#  irq(rel(0)) # <- runs continously
  jmp(x_dec, "delay")
#  irq(rel(0)) # <- only a single invocation
  jmp(y_dec, "step")

def turn(sm):
  print("irq")

# at 10_000Hz, 1 tick is 100us
sm = rp2.StateMachine(0, stepper, freq=10000, set_base=Pin(25))

sm.irq(turn)
sm.active(1)
sm.put(2)
sm.put(3)

time.sleep(1)
sm.active(0)
Here is the intention of this code:
1. Read delay between pulses and put it in x
2. Read a number of steps and put it in y
3. Have a loop "step" and iterate until y is zero with "jump(y_dec, "step")"
4. For each step set pin to HIGH and then have a waiting loop for the duration specified in x

I'm invoking an interrupt for debugging and I'm confused by the results.
If I put it inside of the "delay" loop it would just execute continuously until the state machine is stopped. I would expect it to finish after x has become 0.
If I put it inside of the "step" loop it's only executed once. I expected it to be executed 4 times because of sm.put(3)

What am I doing wrong here?
Also, is there a way to keep a copy of x so I can reset it in each "step" back to its original value after it reaches 0?

Cheers,
Leonti

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

Re: Stepper motors with PIO

Post by Roberthh » Sat Apr 03, 2021 7:07 am

You can MOV X to ISR and back. ISR is not used. like:

Code: Select all

import time
import rp2
from machine import Pin

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def stepper():
  pull()
  mov(isr, osr) # delay

  pull()
  mov(y, osr) # steps

  label("step")
  mov(x, isr)
  set(pins, 1)
  label("delay")
  set(pins, 0)
#  irq(rel(0)) # <- runs continously
  jmp(x_dec, "delay")
#  irq(rel(0)) # <- only a single invocation
  jmp(y_dec, "step")

def turn(sm):
  print("irq")

# at 10_000Hz, 1 tick is 100us
sm = rp2.StateMachine(0, stepper, freq=10000, set_base=Pin(25))

sm.irq(turn)
sm.active(1)
sm.put(2)
sm.put(3)

time.sleep(1)
sm.active(0)
You can also swap the pulling of step and delay (delay second), such that you can always reload the X value from the OSR. Just mov(x, osr) does not clear the value of osr. Like:

Code: Select all

import time
import rp2
from machine import Pin

@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def stepper():
  pull()
  mov(y, osr) # steps

  pull()  # get delay, but keep it in osr
 
  label("step")
  mov(x, osr)  # get delay from OSR
  set(pins, 1)
  label("delay")
  set(pins, 0)
#  irq(rel(0)) # <- runs continously
  jmp(x_dec, "delay")
#  irq(rel(0)) # <- only a single invocation
  jmp(y_dec, "step")

def turn(sm):
  print("irq")

# at 10_000Hz, 1 tick is 100us
sm = rp2.StateMachine(0, stepper, freq=10000, set_base=Pin(25))

sm.irq(turn)
sm.active(1)
sm.put(3)  # steps
sm.put(2)  # delay

time.sleep(1)
sm.active(0)

Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Re: Stepper motors with PIO

Post by Leonti » Sat Apr 03, 2021 7:46 am

Thanks Robert!
I used second approach and it works properly now.

Can you explain why it was in a never ending loop without resetting X?

Cheers,
Leonti

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

Re: Stepper motors with PIO

Post by Roberthh » Sat Apr 03, 2021 8:34 am

Of course. Then the delay loop expires teh first time, it left X at a value of 0. Then Y was decremented, and a few steps later the statement `jmp(x_dec, "delay")` was performed the next time. With X being 0 before and X being an unsigned 32bit int, it will be decremented to 0xffffffff, or 4294967295 decimal, which is a pretty large number. Decrementing that to 0 takes a while. At the frequency of 10000 which you have set and 2 PIO clock cycles per loop it takes about 10 days.

Leonti
Posts: 14
Joined: Sun Mar 28, 2021 12:29 pm

Re: Stepper motors with PIO

Post by Leonti » Sat Apr 03, 2021 8:59 am

Thank you Robert!

Now it all makes sense!

Post Reply