Changing between PIO sm programs on the fly #16693
-
Discussed in https://github.com/orgs/micropython/discussions/11722Originally posted by fwblack June 6, 2023 I've successfully created a working program and have a rotary dial w/pushbutton that the dial changes between 18KHz and 25KHz frequency and the pushbutton is supposed to rotate between 4 asm_pio programs on the same statemachine. It kind of works. At first I hit the ENOMEM memory error when changing to the 2nd program, but finally found that I needed to use the PIO(0).remove_program() command. This lets me successfully rotate between the 4 programs in sequence, but the problem is that when going back to the 1st program from the 4th program it fails to actually run the 1st program. It gives no errors and if I was not watching the waveform on the scope I would not know it was not working correctly. I've searched and found lots of great info, but not anything that I've found to fix the issue. It's as if remove_program() does not remove some trace of the program from the PIO memory and when rotating back to the 1st program it thinks it's there but it's still running the code from the 4th program. I tried rotating from the 4th program to the 2nd program and the results are identical. I found one thread that said they fixed a similar issue by padding their programs with nop() until they had 32 instructions, so that all the PIO memory was overwritten each time they loaded in a program. I tried this, but the result was no different than what I was getting before. My code could be better, and there may be a vastly better way of doing what I'm trying to do, but I'll include the relative parts: #HDrive100 drives the output pins on and off with a dead time of 1 cycle between changes
#for 18KHz this is driven at 18x2x100 or 3,600,000Hz, the thinking here is x100 to get smaller slices and then keep the deadtime as small as possible.
@rp2.asm_pio(set_init=(PIO.OUT_LOW, PIO.OUT_LOW))
def HDrive100():
wrap_target()
set(pins, 0b00000) #= deadtime 1 cycle
set(pins, 0b00001) [31] #= total of 33 cycles
nop() [31] #= total of 65
nop() [31] #= total of 97
nop() [2] #= 3 cycles, total of 100 cycles (1 + (32 * 3) + 3 = 100)
set(pins, 0b00000) #= deadtime 1 cycle
set(pins, 0b00010) [31] #= total of 33 cycles
nop() [31] #= total of 65
nop() [31] #= total of 97
nop() [2] #= 3 cycles, total of 100 cycles (1 + (32 * 3) + 3 = 100)
wrap()
#HDrive75 cuts the duty cycle in 3/4 (on for 75 out of 100 cycles)
#for 18KHz this is driven at 18x2x100 or 3,600,000Hz
@rp2.asm_pio(set_init=(PIO.OUT_LOW, PIO.OUT_LOW))
def HDrive75():
wrap_target()
set(pins, 0b00000) [11] #= off 12 cycles
set(pins, 0b00001) [31] #= on 32, total of 44
nop() [31] #= on 32, on total of 64, total of 76
nop() [10] #= in 11, on total of 75, total of 87
set(pins, 0b00000) [12] #= off 13, total of 100
nop() [11] #= off 12 cycles
set(pins, 0b00010) [31] #= on 32, total of 44
nop() [31] #= on 32, on total of 64, total of 76
nop() [10] #= in 11, on total of 75, total of 87
set(pins, 0b00000) [12] #= off 13, total of 100
wrap()
#HDrive50 cuts the duty cycle in half (on for 50 out of 100 cycles)
#for 18KHz this is driven at 18x2x100 or 3,600,000Hz
@rp2.asm_pio(set_init=(PIO.OUT_LOW, PIO.OUT_LOW))
def HDrive50():
wrap_target()
set(pins, 0b00000) [24] #= off 25 cycles
set(pins, 0b00001) [24] #= on 25, total of 50
nop() [24] #= on 50, total of 75
set(pins, 0b00000) [24] #= off 25, total of 100
nop() [24] #= off 25 cycles
set(pins, 0b00010) [24] #= on 25, total of 50
nop() [24] #= on 50, total of 75
set(pins, 0b00000) [24] #= off 25, total of 100
wrap()
#HDrive25 cuts the duty cycle in 1/4 (on for 25 out of 100 cycles)
#for 18KHz this is driven at 18x2x100 or 3,600,000Hz
@rp2.asm_pio(set_init=(PIO.OUT_LOW, PIO.OUT_LOW))
def HDrive25():
wrap_target()
set(pins, 0b00000) [24] #= off 25 cycles
nop() [11] #= off 12, total of 37
set(pins, 0b00001) [24] #= on 25, total of 62
set(pins, 0b00000) [24] #= off 25, total of 87
nop() [12] #= off 13, total of 100
nop() [24] #= off 25 cycles
nop() [11] #= off 12, total of 37
set(pins, 0b00010) [24] #= on 25, total of 62
set(pins, 0b00000) [24] #= off 25, total of 87
nop() [12] #= off 13, total of 100
wrap()
def run_statemachine():
global dutycycle
global sm
global val_new
global adr
sm.active(0)
PIO(0).remove_program()
if dutycycle == 100:
print("Setting to 100")
#sm = rp2.StateMachine(0, HDrive100, freq=val_new * 200, set_base=Pin(2))
sm.init(HDrive100, freq=val_new * 200, set_base=Pin(2))
elif dutycycle == 75:
print("Setting to 75")
#sm = rp2.StateMachine(0, HDrive75, freq=val_new * 200, set_base=Pin(2))
sm.init(HDrive75, freq=val_new * 200, set_base=Pin(2))
elif dutycycle == 50:
print("Setting to 50")
#sm = rp2.StateMachine(0, HDrive50, freq=val_new * 200, set_base=Pin(2))
sm.init(HDrive50, freq=val_new * 200, set_base=Pin(2))
elif dutycycle == 25:
print("Setting to 25")
#sm = rp2.StateMachine(0, HDrive25, freq=val_new * 200, set_base=Pin(2))
sm.init(HDrive25, freq=val_new * 200, set_base=Pin(2))
else:
print("Setting to 100 in catch-all else statement")
#sm = rp2.StateMachine(0, HDrive100, freq=val_new * 200, set_base=Pin(2))
sm.init(HDrive100, freq=val_new * 200, set_base=Pin(2))
sm.active(1)
sm = rp2.StateMachine(0, HDrive100, freq=val_new * 200, set_base=Pin(2))
sm.active(1)
while True:
val_new = r.value()
if speedbutton.value() == 0: # button is pressed!
#print('Button Pressed!')
update_dutycycle()
#time.sleep_ms(50)
run_statemachine()
if val_old != val_new:
val_new = accel_enc_val()
val_old = val_new
r.set(value = val_new)
#print('result =', val_new)
update_display()
run_statemachine()
heartbeat_toggle = not(heartbeat_toggle)
boardled.value(heartbeat_toggle)
time.sleep_ms(50) I've left out the code dealing with the rotary control/button and the TFT Display. I've not found anything that says rotating code into the sm like this should not work, and it appears to work until I try to run code that I've already ran in the sm. Any suggestions? Thanks in advance. Fred UPDATE: Based on this post: https://forum.micropython.org/viewtopic.php?t=10733#p59032 I found that I had to specifically name the program(s) to remove for PIO.remove_program to work. I put 4 specific remove_program commands in my code to replace the one w/o parameters (which the documentation says should remove all programs and obviously does not) and the problem is fixed.
So it appears to be an issue with the remove_program() command when nothing is specified - it does not remove something correctly. The test code from the link I just referred to is shown below and exabits the same issue:
Output using PIO(0).remove_program() is: Output using |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
I am also facing that problem, trying to switch programs. The problem in my case is that the programs are programmatically applied by different program controllers. In a program controller, I am just calling
And my expectation is, that all programs and the associated statemachines are removed/stopped. |
Beta Was this translation helpful? Give feedback.
-
So, I've created a util function to clear the PIO properly. It is to mention, that I had to stop the machine with def clear_programs():
while PIO_PROGRAMS:
pio = 0
(sid, program) = PIO_PROGRAMS.pop()
if (sid > 3): pio = 1
PIO(pio).state_machine(sid % 4).active(0) # didn't worked without stopping..
PIO(pio).remove_program(program) |
Beta Was this translation helpful? Give feedback.
This comment has been hidden.
This comment has been hidden.
-
Arg, my mistake - I missed this was already also a discussion. Closing as a duplicate of https://github.com/orgs/micropython/discussions/11722 |
Beta Was this translation helpful? Give feedback.
Arg, my mistake - I missed this was already also a discussion. Closing as a duplicate of https://github.com/orgs/micropython/discussions/11722