mortenblu
Posts: 3
Joined: Mon Sep 20, 2021 6:49 pm

Pico running multiple stepper

Mon Sep 20, 2021 7:03 pm

Hi,
I am a complete newbie when it comes to Pico and even more so reg. MicroPython. I am basically trying to learn it as I am working on this + other projects.

What I am trying to achieve:
To have 2 stepper motors being controlled by a single Pico. They can either run at the same time (for starters) but ultimately I want to include variables that will allow me to control the exact steps for each motor. Happy to ignore that for the time being though as I do want to learn this step by step.

Hardware:
1 x Pico
2 x 28BYJ-48 Stepper
2 x ULN2003A Driver

What code snippets do I have so far:
With this snippet I can run one stepper.

Code: Select all

from machine import Pin
import utime

#pins = [
#    Pin(0,Pin.OUT),#IN1
#    Pin(1,Pin.OUT),#IN2
#    Pin(2,Pin.OUT),#IN3
#    Pin(3,Pin.OUT),#IN4
#    ]

pins = [
    Pin(14,Pin.OUT),#IN1
    Pin(15,Pin.OUT),#IN2
    Pin(16,Pin.OUT),#IN3
    Pin(17,Pin.OUT),#IN4
    ]

full_step_sequence = [
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [0,0,0,1]
    ]
    
while True:
    for step in full_step_sequence:
        for i in range(len(pins)):
            pins[i].value(step[i])
            utime.sleep(0.001)

I #-tagged out the pins of the first Stepper so you can see the pins I am using.

Can anyone share this code snippet with me or point me in the right direction?
Thank you in advance

Federico
Posts: 22
Joined: Mon Jul 16, 2012 9:10 am

Re: Pico running multiple stepper

Sun Sep 26, 2021 11:01 am

Code: Select all

# Programmable I/O example with 2 unipolar steppers 28BYJ-48 + UNL2003
#  an Half Step sequence is used-  Sept 2021 - federico

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from utime import sleep


# ====== start Input DATA =======
stepper_ratio = 1 / 64     # see stepper data sheet
cog_nr = 8                 # see stepper data sheet
# stepper motor- same colors sequence for pi pico BluePinkYellowOrange - ULN2003 BPYO
start_pin_0 = Pin(10)      # Stepper nr.1 - 1st ( Blue color) of 4 stepper's pins
start_pin_1 = Pin(18)      # Stepper nr.2 - 1st ( Blue color) of 4 stepper's pins
nr_sm_pins = 4

# sm  2 <= kHz <= 50 this trial with unipolar stepper 28BYJ-48
frequency = 30_000         # default state machine frequency
min_frequency = 2_000      # from trials
max_frequency = 50_000     # from trials
rev_angle = 360
degrees = 360
HALF_STEP = [9, 1, 3, 2, 6, 4, 12, 8]  # see forward_PIO() & backwards_PIO()
nop_clock_delays = 32                  # see forward_PIO() & backwards_PIO()

# ====== end  Input DATA =======
steps_rev = int(cog_nr * len(HALF_STEP) * 1/stepper_ratio)
# Run the state machine for sec_rev (360 degrees) sec
sec_rev = (1 / frequency) * 2 * nop_clock_delays * steps_rev
constant = frequency * sec_rev 

# sm3.active(1);sleep(a * steps / steps_rev);sm3.active(0);sm3.exec("set(pins,0)")
k_steps = rev_angle/steps_rev
steps = int(degrees / k_steps)  # steps*k_steps = degree

# ref: GET STARTED WITH MICROPYTHON ON RASPBERRY PI PICO
#      Appendix C Programmable I/O
# program definition - the @asm_pio descriptor tells MicroPython to treat
# it as a PIO program and not a normal method
# there are 4 set pins connected to the SM, and their initial state is set
# when the StateMachine is created. 
# output low for all 4 pins ( 18,19, 20, 21)  - see sm0&sm1 instantiations for the 1st stepper
# output low for all 4 pins ( 10,11, 12, 13)  - see sm2&sm3 instantiations for the 2nd stepper

@asm_pio(set_init=(PIO.OUT_LOW,) * nr_sm_pins)  # method descriptor
def forward_PIO():   # Half step 1&2 phases
    # Step half a step to alternate between single coil and double coil steps.
    HALF_STEP = [9, 1, 3, 2, 6, 4, 12, 8]
    a = 31
    wrap_target
    # set() Drive stepper's pins as Half Step ON/OFF sequence and then delay for [a] cycles
    set(pins, HALF_STEP[0])[a]
    # nop is a pioasm pseudoinstruction used for extra delay
    nop()[a]
    set(pins, HALF_STEP[1])[a]
    nop()[a]
    set(pins, HALF_STEP[2])[a]
    nop()[a]
    set(pins, HALF_STEP[3])[a]
    nop()[a]
    set(pins, HALF_STEP[4])[a]
    nop()[a]
    set(pins, HALF_STEP[5])[a]
    nop()[a]
    set(pins, HALF_STEP[6])[a]
    nop()[a]
    set(pins, HALF_STEP[7])[a]
    nop()[a]
    wrap()

@asm_pio(set_init=(PIO.OUT_LOW,) * nr_sm_pins)
def backwards_PIO():
    HALF_STEP = [8, 12, 4, 6, 2, 3, 1, 9]
    a = 31
    wrap_target()
    set(pins, HALF_STEP[0])[a]
    nop()[a]
    set(pins, HALF_STEP[1])[a]
    nop()[a]
    set(pins, HALF_STEP[2])[a]
    nop()[a]
    set(pins, HALF_STEP[3])[a]
    nop()[a]
    set(pins, HALF_STEP[4])[a]
    nop()[a]
    set(pins, HALF_STEP[5])[a]
    nop()[a]
    set(pins, HALF_STEP[6])[a]
    nop()[a]
    set(pins, HALF_STEP[7])[a]
    nop()[a]
    wrap()

# 1st stepper
sm0 = StateMachine(0,    forward_PIO, freq=frequency, set_base=start_pin_0)
sm1 = StateMachine(1,  backwards_PIO, freq=frequency, set_base=start_pin_0)

# 2nd stepper
sm2 = StateMachine(2,    forward_PIO, freq=frequency, set_base=start_pin_1)
sm3 = StateMachine(3,  backwards_PIO, freq=frequency, set_base=start_pin_1)

#-------------------------- END of Code------------------------------------

#***** Run examples ******
a = (constant / frequency)
degrees = 360
print("State Machine Hz = ", frequency, " ", a * degrees / rev_angle, " sec for ", degrees, " degrees")

sm0.active(1)  # start the stepper nr1
sm3.active(1)  # start the stepper nr3

# Run the state machine for a * degrees / rev_angle seconds
sleep(a * degrees / rev_angle)

sm0.active(0)  # stop the sm0 - 1st stepper
sm3.active(0)  # stop the sm3 - 2nd stepper

# Turn off the set pins ( 4 pico/stepper pins) via an exec instruction.
sm0.exec("set(pins,0)")
sm3.exec("set(pins,0)")

sleep(1)

sm0.active(1); sleep(512 * constant/(frequency*steps_rev));sm0.active(0); sm0.exec("set(pins,0)")
sm1.active(1); sleep(512 * constant/(frequency*steps_rev));sm1.active(0); sm1.exec("set(pins,0)")
sm2.active(1); sleep(512 * constant/(frequency*steps_rev));sm2.active(0); sm2.exec("set(pins,0)")
sm3.active(1); sleep(512 * constant/(frequency*steps_rev));sm3.active(0); sm3.exec("set(pins,0)")

sm0.active(1); sleep(1024 * constant/(frequency*steps_rev));sm0.active(0); sm0.exec("set(pins,0)")
sm1.active(1); sleep(1024 * constant/(frequency*steps_rev));sm1.active(0); sm1.exec("set(pins,0)")
sm2.active(1); sleep(1024 * constant/(frequency*steps_rev));sm2.active(0); sm2.exec("set(pins,0)")
sm3.active(1); sleep(1024 * constant/(frequency*steps_rev));sm3.active(0); sm3.exec("set(pins,0)")

sm0.active(1); sleep(2048 * constant/(frequency*steps_rev));sm0.active(0); sm0.exec("set(pins,0)")
sm1.active(1); sleep(2048 * constant/(frequency*steps_rev));sm1.active(0); sm1.exec("set(pins,0)")
sm2.active(1); sleep(2048 * constant/(frequency*steps_rev));sm2.active(0); sm2.exec("set(pins,0)")
sm3.active(1); sleep(2048 * constant/(frequency*steps_rev));sm3.active(0); sm3.exec("set(pins,0)")

sm0.active(1); sm3.active(1); sleep(512 * constant/(frequency*steps_rev));sm0.active(0);sm3.active(0); sm0.exec("set(pins,0)"); sm3.exec("set(pins,0)")
sm1.active(1); sm2.active(1); sleep(512 * constant/(frequency*steps_rev));sm1.active(0);sm2.active(0); sm1.exec("set(pins,0)"); sm2.exec("set(pins,0)")

#---------------------------------------------------------------------------------------
sleep(1)
print( "to change the stepper speed we change the State Machine Hz with a new State Machine instantiation")

frequency = 50000
sec_rev = (1 / frequency) * 2 * nop_clock_delays * steps_rev
constant = frequency * sec_rev 
a = (constant / frequency)
degrees = 360

sm1 = StateMachine(0,    forward_PIO, freq= frequency, set_base=start_pin_0)
sm3 = StateMachine(3,    forward_PIO, freq= frequency, set_base=start_pin_1)

print("State Machine Hz = ", frequency, " ", a * degrees / rev_angle, " sec for ", degrees, " degrees")

sm1.active(1)  # start the stepper nr1
sm3.active(1)  # start the stepper nr2

# Run the state machine for a * degrees / rev_angle seconds
sleep(a * degrees / rev_angle)

sm1.active(0)  # stop the sm1 - 1st stepper
sm3.active(0)  # stop the sm3 - 2nd stepper

# Turn off the set pins ( 4 pico/stepper pins) via an exec instruction.
sm1.exec("set(pins,0)")
sm3.exec("set(pins,0)")

sleep(1)

print("********** 1st Stepper Forward ****************")
sm0 = StateMachine(0,    forward_PIO, freq=frequency, set_base=start_pin_0)
sm0.active(1);sleep(degrees * a  / rev_angle);sm0.active(0);sm0.exec("set(pins,0)")
print("********** 1st Stepper Backward ****************")
sm1 = StateMachine(1,  backwards_PIO, freq=frequency, set_base=start_pin_0)
sm1.active(1);sleep(degrees * a  / rev_angle);sm1.active(0);sm1.exec("set(pins,0)")

sleep(1)

print("********** 2nd Stepper Forward ****************")
sm2 = StateMachine(2,    forward_PIO, freq=frequency, set_base=start_pin_1)
sm2.active(1);sleep(degrees * a  / rev_angle);sm2.active(0);sm2.exec("set(pins,0)")
print("********** 2nd Stepper Backward ****************")
sm3 = StateMachine(3,  backwards_PIO, freq=frequency, set_base=start_pin_1)
sm3.active(1);sleep(degrees * a  / rev_angle);sm3.active(0);sm3.exec("set(pins,0)")


print(" use of steps in place of degrees")
sm3.active(1);sleep(2048 * a / steps_rev);sm3.active(0);sm3.exec("set(pins,0)")
sleep(1)
sm3.active(1);sleep(1024 * a  / steps_rev);sm3.active(0);sm3.exec("set(pins,0)")
sleep(1)
sm3.active(1);sleep(512 * a  / steps_rev);sm3.active(0);sm3.exec("set(pins,0)")
sleep(1)
sm3.active(1);sleep(512 * a  / steps_rev);sm3.active(0);sm3.exec("set(pins,0)")




Last edited by Federico on Fri Oct 01, 2021 3:29 pm, edited 4 times in total.

Federico
Posts: 22
Joined: Mon Jul 16, 2012 9:10 am

Re: Pico running multiple stepper

Sun Sep 26, 2021 11:03 am

This video may be useful to you

https://youtu.be/OV7OnMvn-RY
Raspberry Pi Pico - 28BYJ-48 Unipolar Stepper Motor - Micropython Example -

and the previous Micropython + PIO example can be used as a starting point.
https://www.youtube.com/watch?v=nTHXkCcPy2g

Federico
Posts: 22
Joined: Mon Jul 16, 2012 9:10 am

Re: Pico running multiple stepper

Sun Oct 03, 2021 7:25 am

# • Micropython and P I / O example - 28BYJ-48 unipolar stepper
# • Mode, frequency of the state machine and the number of steps or degrees can be changed
# • Modes: Full Step, Full Step_2 and Half Step

The main differences with the 1st version (sleep solution)

>>> sm0.active (1); sleep (degrees * a / rev_angle); sm0.active (0)

is that now (Steps solution) two state machines are used
- one for each stepper - while the 32-bit ON / OFF binary sequence used to define the direction of rotation (clockwise / anticlockwise)
used in the asm PIO program, - as well as the number of steps - is a data that
the asm pio program receives from micropython with the put method of the StateMachine class:

>>> sm0.active (1); sm0.put (backw); sm0.put (steps)

where the steps parameter determines both the degrees corresponding to it and the stop of the stepper. Consequently we don't need to use sm0.active (0) to stop
the stepper as in the first solution.

Code: Select all


#  • Micropython & Programmable I/O example: Unipolar stepper 28BYJ-48
#  • The step mode, State Machine frequency and the steps or degrees
#    number can be changed
#  • Step modes: Full Step, Full Step_2 and Half Step

from machine import Pin
from rp2 import PIO, StateMachine, asm_pio
from utime import sleep

# -------------- START of Stepper Input Data -------------
stepper_ratio = 1 / 64       # see stepper data sheet
teeth_nr = 8                 # teeth number of the cog - stepper hardware
pin_nr_0 = 10                # first rpi pico pin for 1st stepper
pin_nr_1 = 18                # first rpi pico pin for 2nd stepper
nr_sm_pins = 4               # nr of stepper inputs
hz = 40_000                  # 2000 <= state machine frequency in Hz <= 44000

# ********************** Start of STEP Patterns ***************************************
#                              f_fs1_32bit and b_fs1_32bit 
#  • FULL STEP one  Phase coils ON/OFF pattern
FULL_STEP = [1, 2, 4, 8]     # Dubled to have 32 bit
# forward  step pattern       1    2    4    8    1    2    4    8
f_fs1_32bit = 306713160      #0001 0010 0100 1000 0001 0010 0100 1000  32 bit
# backwards step pattern       8    4    2    1     8    4    2    1
b_fs1_32bit = 2216789025     #1000 0100 0010 0001 1000 0100 0010 0001  32 bit

#  • FULL STEP Two  Phases
FULL_STEP_2 = [3, 6, 2, 9]   # Dubled to have 32 bit
# forward  step pattern       3    6    12   9    3    6    12   9
f_fs2_32bit = 919156425      #0011 0110 1100 1001 0011 0110 1100 1001  32 bit
# backwards step pattern       9    12   6    3    9    12   6    3
b_fs2_32bit = 2623773795     #1001 1100 0110 0011 1001 1100 0110 0011  32 bit


#  • HALF STEP (mixed One & Two Phases)
HALF_STEP = [1, 3, 2, 6, 4, 12, 8, 9]  
# forward  step pattern       1    3    2    6    4    12   8    9
f_hs =  321277065            #0001 0011 0010 0110 0100 1100 1000 1001  32 bit

# backwards step pattern       9    8    12   4    6    2    3    1
b_hs = 2563007025            #1001 1000 1100 0100 0110 0010 0011 0001  32 bit
# ********************** End of STEP Patterns ***************************************

mode = FULL_STEP             # we have 3 choices : FULL_STEP, FULL_STEP_2, HALF_STEP
# -------------- END of Stepper Input Data -------------

start_pin_0 = Pin(pin_nr_0)  # state machine start Pin - contiguous pins 10,11,12,13
start_pin_1 = Pin(pin_nr_1)  # state machine start Pin - contiguous pins 18,19,20,21

if mode == FULL_STEP:
    forw = f_fs1_32bit
    backw = b_fs1_32bit
if mode == FULL_STEP_2:
    forw = f_fs2_32bit
    backw = b_fs2_32bit
if mode == HALF_STEP:   
    forw = f_hs
    backw = b_hs

steps_rev = teeth_nr * len(mode) * int(1/stepper_ratio)
steps_rev_offset = 2  # from trials - with no error must be 0
steps = steps_rev  +  steps_rev_offset # default steps

# ref: GET STARTED WITH MICROPYTHON ON RASPBERRY PI PICO
#      Appendix C Programmable I/O
# program definition - the @asm_pio descriptor tells MicroPython to treat
# it as a PIO program and not a normal method
# There are 4 set pins connected to the SM, and their initial state is set
# when the StateMachine is created. 
# Output low for all 4 sm0 pins (10,11,12,13 ) and sm1 pins ( 18,19, 20, 21) 
# see sm0 & sm1 instantiations for the 1st and 2nd stepper

@asm_pio(set_init=(PIO.OUT_LOW,) * nr_sm_pins,
         out_init=(PIO.OUT_HIGH,) * nr_sm_pins,
         out_shiftdir=PIO.SHIFT_LEFT,
         in_shiftdir=PIO.SHIFT_RIGHT
         )
def stepper():
    #  • The pull command takes (32-bit word) data from the input RX FIFO
    #    and places it in the Output Shift Register (OSR).
    pull()                # get (32-bit word) Full or Half Step pattern

    #  • Copy data from Source to Destination.
    mov(y, osr) # from OSR to y register

    #  • Load a 32-bit word from the TX FIFO into the OSR.
    pull()                # steps
    mov(x, osr)           # get steps number from OSR

    jmp(not_x, "end")     # jump to end if steps counter = 0

    label("loop")
    jmp(not_osre, "step") # loop sequence if exhausted
    mov(osr, y)           # store the pattern sequence in OSR

    label("step")
    # • out - Shift Bit count bits out of the Output Shift Register (OSR),
    #         and write them  to destination (pins: 18,19,20,21)
    # • Bit count: how many bits to shift out of the OSR. 1…32 bits, 32 is encoded as 00000.
    # Additionally, increase the output shift count by Bit count, saturating at 32.
    out(pins, 4) [31]     # takes and send 4 bit at time of Step Pattern to output pins
    nop() [31]            # number of clock cycle delays - from 0 to 31
    nop() [31]
    nop() [31]
    # • The jmp instruction tells the code to move directly to a particular label
    jmp(x_dec,"loop")     # decrement the step counter by 1
    label("end")
    set(pins, 0) [31]     # switch LOW the output pins

# state machine 1st stepper
sm0 = StateMachine(0, stepper, freq=hz, set_base=start_pin_0, out_base=start_pin_0)

# state machine 2nd stepper
sm1 = StateMachine(1, stepper, freq=hz, set_base=start_pin_1, out_base=start_pin_1)

# [from rhh] The pio program object 'stepper', is a list.
# The first element of that list is an array of 16 bit numbers, which is the code.
# So the code snipped below after instantiating the state machines will print
# the binary code:
print("Array of "+ str(len(stepper[0])) +" '16 bit numbers', which is the '@asm_pio  stepper()' code")
for i,j in enumerate(stepper[0]):
    print("%2d: %04x" % (i,j))
# ======================= END of code ==================================
sm0.active(1); sm0.put(forw); sm0.put(steps)
sm1.active(1); sm1.put(backw); sm1.put(steps)
sleep(1)
sm0.active(1); sm0.put(backw); sm0.put(steps)
sm1.active(1); sm1.put(forw); sm1.put(steps)

sleep(1)

# steps vs degrees relationsheep
# 360 : (steps_rev  +  steps_rev_offset) = degrees : steps
# steps = degrees * (steps_rev  +  steps_rev_offset)/ 360
k1 = (steps_rev  +  steps_rev_offset)/ 360
degrees = 720
steps1 = int(degrees * k1)
print("Degrees :", degrees, " --> ", steps1, " steps")
sm0.active(1); sm0.put(forw);  sm0.put(steps1)
sm1.active(1); sm1.put(backw); sm1.put(int(720 * (steps_rev + steps_rev_offset)/ 360))

sleep(1)

print(mode, ' - FORWARD/BACKWARDS : 10 revolutions for a visaul ceck of degrees error\n')
sm0.active(1); sm0.put(forw); sm0.put(10*steps)
sm1.active(1); sm1.put(backw); sm1.put(10*steps)


mortenblu
Posts: 3
Joined: Mon Sep 20, 2021 6:49 pm

Re: Pico running multiple stepper

Mon Oct 11, 2021 6:23 pm

Thanks Federico - Your emails went into my spam folder so completely missed your responses. I am looking into your posts now. Thank you very much for responding.

mortenblu
Posts: 3
Joined: Mon Sep 20, 2021 6:49 pm

Re: Pico running multiple stepper

Mon Oct 11, 2021 7:41 pm

Spot on Federico, this is exactly what I was after. This will help me to explore the various options and allow me to delve into the topic more. I already added the 3rd stepper :D

Thank you so much for your AAA support!

Alberto_2018
Posts: 5
Joined: Sat Dec 01, 2018 12:08 am

Re: Pico running multiple stepper

Wed Feb 09, 2022 10:06 am

Ciao Federico, E' interessante la soluzione da tè adottata, ma vorrei segnalarti una limitazione implicita nel tuo codice.

La limitazione sta nel fatto che le funzioni (Forward e Backward) compiono entrambe un ciclo completo di 8 steps (full half step sequence)
e di conseguenza non è possibile muovere il motore con un valore di steps che non sia un multiplo di 8 o per un valore inferiore a 8!
Una soluzione interessante al tuo codice sarebbe quella di poter far muovere il motore anche di un singolo step. Per esempio mandando alla funzione il numero di steps desiderati e far si che la funzione svolga al suo interno il lavoro.

num_Step = 2073
move_motor_F
------------------------------------
def move_motor_F():
global num_step, jj

count = 0
while count<num_step
count = +1
jj = +1
if jj > 7: jj=0
set(pins, half_step[jj] # move moter forward
sleep(x) # x speed control
return jj


def move_motor_B():
global num_step, jj

count = 0
while count<num_step
count = +1
jj = -1
if jj <0: jj=7
set(pins, half_step[jj] # move motor backward
sleep(x) # x speed control
return jj

nell'esempio la funzione muoverebbe il motore di 2073 steps come richiesto.
Ora non so se è possibile dichiarare variabili globali in una funzione assembler ma era giusto per spiegare meglio il concetto.
Importante che la variabile jj sia sempre visibile ad entrambe le funzioni e che la sequenza (i 4 pins di outputs) non vengano mai resettati altrimenti si perdono steps.
Spero che la mia osservazione ti possa essere utile nel migliorare il tuo codice (che spero di poter provare presto).

Cordialità

Alberto

Federico
Posts: 22
Joined: Mon Jul 16, 2012 9:10 am

Re: Pico running multiple stepper

Wed Mar 02, 2022 4:57 am

Ciao Alberto,
Thank for yours evaluations and suggestions (if I am right) relative to my first posted. code solution (forward and backward asm_pio functions).
In a second post on Sun Oct 03, 2021 7:25 am
I have proposed a new or 2nd code solution
where in the asm_pio code the stepper() is
the only defined function where in my opinion doesn't exist the mentioned steps limit.

Code: Select all

label("step")
    # • out - Shift Bit count bits out of the Output Shift Register (OSR),
    #         and write them  to destination (pins: 18,19,20,21)
    # • Bit count: how many bits to shift out of the OSR. 
    # Additionally, increase the output shift count by Bit count, saturating at 32.
    #-----------------------------------------------------------------------------------
    out(pins, 4) [31]     # takes and send 4 bit at time of Step Pattern to output pins
    #-----------------------------------------------------------------------------------
    nop() [31]            # number of clock cycle delays - from 0 to 31
    nop() [31]
    nop() [31]
    # • The jmp instruction tells the code to move directly to a particular label
    jmp(x_dec,"loop")     # decrement the step counter by 1
    #-----------------------------------------------------------------------------------

cleverca22
Posts: 8824
Joined: Sat Aug 18, 2012 2:33 pm

Re: Pico running multiple stepper

Wed Mar 02, 2022 9:49 am

Code: Select all

; https://discord.com/channels/801944326401556512/801948576242597928/810511503161425920
; <LukeW> @ZodiusInfuser  this is the idea I mentioned yesterday for stepper PWM
; <LukeW> so the onus is on software to make sure count0 + count1 + count2 = const, but that calculation only needs to be done once per step change (and could be table-driven) so not too bad. The PIO program gives you predictable timing and hands-free microstep hold

.program stepper

; Dual H-bridge stepper driver. Command format:
; 
; +--------+--------+--------+--------+--------+--------+
; | 31  25 | 24  21 | 20  15 | 14  11 | 10   4 | 3    0 |
; +--------+--------+--------+--------+--------+--------+
; | count2 | phase2 | count1 | phase1 | count0 | phase0 |
; +--------+--------+--------+--------+--------+--------+
; | (7bit) | (4bit) | (6bit) | (4bit) | (7bit) | (4bit) |
; +--------+--------+--------+--------+--------+--------+
; 
; This PIO program accepts step commands from the TX FIFO in the format given
; above, and executes them in order. When the FIFO runs empty, it will hold the
; current step/microstep until a new command is written.
;
; phase0 and phase2 are two step settings 90deg apart, each with only one coil
; active, and count0/count2 are used to interpolate between them, for
; microstepping.
; 
; phase1 is an overlapped point where both coils are active, with its width
; controlled by count1, so that circular cos/sin interpolation can be used to
; reduce torque ripple. The overlap is needed because cos theta + sin theta >
; 1 in general, so simple linear interpolation between phase0 and phase2 is
; insufficient.

    pull noblock
    mov x, osr

    out pins, 4    ; phase0 coil values
    out y, 7
count0:            ; Length of phase0 is count0 + 2
    jmp y-- count0

    out pins, 4    ; phase1 coil values
    out y, 6
count1:            ; Length of phase1 is count1 + 2
    jmp y-- count1

    out pins, 4    ; phase2 coil values
    out y, 7
count2:            ; Length of phase2 is count2 + 4
    jmp y-- count2
i had discussed this idea before on the pico discord, and this is the program LukeW came up with

you just send the PIO a single 32bit word, that represents the phase angle of the output, including the micro-stepping angle
and it will PWM the 2 coils of the stepper motor to hold that microstep
if you give it a stream of these positions, it will step thru them in order, giving you a controllable speed, and then hold position at the last one whenever you stop feeding the FIFO

Return to “MicroPython”