RP2040 PIO: Script fails on some GPIO numbers
Port, board and/or hardware
RP Pico (RP2040)
MicroPython version
MicroPython v1.26.1 on 2025-09-11; Raspberry Pi Pico with RP2040
Reproduction
Link GPIO 1-17, attach a scope/LA to GPIO 2, and run the following:
from machine import Pin, PWM
import rp2
import time
inp = Pin(0, Pin.IN)
jmp = Pin(1, Pin.IN)
opp = Pin(2, Pin.OUT, value=1)
pwm = PWM(Pin(17)) # Link GPIO 17 to GPIO 1
pwm.freq(1000)
pwm.duty_u16(0xFFFF // 2)
@rp2.asm_pio(out_init=rp2.PIO.OUT_LOW)
def sm_test():
set(x, 0)
wrap_target()
wait(0, pins, 1) # Wait for GPIO 1 to go low
mov(osr, x) # Ouput==0 while input==0
out(pins, 1)
label("loop") # Loop until jump pin goes high
jmp(pin, "pulse")
jmp("loop")
label("pulse")
mov(osr, invert(x)) # Emit a pulse
out(pins, 1)
mov(osr, x)
out(pins, 1)
wrap()
sm0 = rp2.StateMachine(0, sm_test, freq=100_000, out_base=opp , in_base=inp, jmp_pin=jmp)
sm0.restart()
sm0.active(1)
while True:
time.sleep(1)
print("Running")
Expected behaviour
Expected to output a single pulse on GPIO 2 after every positive going edge of GPIO 17.
Observed behaviour
As written, works as designed.
If the link is changed to link GPIO 17 and 14, and the code changed to read
inp = Pin(13, Pin.IN)
jmp = Pin(14, Pin.IN)
pulses are emitted all the time the input is high. If GPIO 1 is strapped to 3V3, pulses stop. Note GPIO 1 is not referenced in the code.
If GPIO 1 and GPIO 14 are linked it runs as designed. This behaviour continues after a power cycle!
Additional Information
RP2350 behaves similarly except that the sensitivity to GPIO 1 is lacking.
The decorator arg is necessary for the code to work. If this is intended it should be documented as this seems unexpected.
Code of Conduct
Yes, I agree
RP2350: PIO2 State Machines do not work
Port, board and/or hardware
ports-rp2 Pico 2
MicroPython version
MicroPython v1.26.0-preview.251.g5ade8b705 on 2025-06-16; Raspberry Pi Pico2 with RP2350
Reproduction
pio_1hz.py from examples does not blink the LED or trigger the interrupt when switched to StateMachine IDs 8, 9, 10, or 11.
Expected behaviour
No response
Observed behaviour
What I've found so far:
PIO2_BASE + SM0_EXECCTRL is set incorrectly
PIO2 instruction memory is not loaded
Instruction start address is not set
Additional Information
This code mostly works.
The IRQ handler only triggers once, not repeatedly as expected.
import time
from machine import Pin
import rp2
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz():
# fmt: off
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
irq(rel(0))
set(pins, 1)
set(x, 31) [5]
label("delay_high")
nop() [29]
jmp(x_dec, "delay_high")
# Cycles: 1 + 1 + 6 + 32 * (30 + 1) = 1000
nop()
set(pins, 0)
set(x, 31) [5]
label("delay_low")
nop() [29]
jmp(x_dec, "delay_low")
# fmt: on
# Create the StateMachine with the blink_1hz program, outputting on Pin(25).
sm = rp2.StateMachine(8, blink_1hz, freq=2300, set_base=Pin(25))
# Set the IRQ handler to print the millisecond timestamp.
sm.irq(lambda p: print(time.ticks_ms()))
# -------- MANUAL REGISTER SETTING START -----------
import machine
PIO2_BASE = 0x50400000
SM0_EXECCTRL = 0xCC
INSTR_MEM0 = 0x48
SM0_INSTR = 0xD8
machine.mem32[PIO2_BASE + SM0_EXECCTRL] = 129920
instrs = [49168, 57345, 58687, 48450, 89, 41026, 57344, 58687, 48450, 94]
instr_start = 0x48 + (4 * (32-len(instrs)))
for idx,instr in enumerate(instrs):
machine.mem32[PIO2_BASE + instr_start + (idx*4)] = instr
machine.mem32[PIO2_BASE + SM0_INSTR]=32-len(instrs)
# -------- MANUAL REGISTER SETTING END -----------
# Start the StateMachine.
sm.active(1)
while(True):
pass
Code of Conduct
Yes, I agree