🛠 Exploiter les 3 blocs PIO et 12 machines d'état du RP2350 (avec IRQ dédiées)
Objectives
The DNA of RP2 chips: PIO (Programmable I/O). The RP2350 doubles down compared to the RP2040: 3 PIO blocks instead of 2, 12 state machines (4 per block) instead of 8, and above all 2 dedicated IRQs per block (PIO0/1/2_IRQ_0/1) whereas the RP2040 shared a single pair (RP2350 datasheet, §3.2 interrupt table). You will run an autonomous PIO while the CPU does something else, then understand why 12 SMs open up architectures that the RP2040 did not allow.
Hardware
- 1 × generic RP2350 board + MicroPython rp2 (RP2350 firmware)
- 1 × LED on GP25 (330 Ω resistor) or onboard LED
- 1 × logic analyzer or simply a visible LED — the rest is software
Complete code
# pio_double.py — une PIO autonome + PWM matériel en parallèle
import rp2, time
from machine import Pin, PWM
# --- PIO0/SM0 : clignotement ~1 Hz, 100 % autonome (CPU libre) ---
@rp2.asm_pio(set_init=rp2.PIO.OUT_LOW)
def blink_1hz:
set(pins, 1)
set(x, 31) [6]
label('high')
nop [29]
jmp(x_dec, 'high')
set(pins, 0)
set(x, 31) [6]
label('low')
nop [29]
jmp(x_dec, 'low')
sm = rp2.StateMachine(0, blink_1hz, freq=2000, set_base=Pin(25))
sm.active(1)
# Plan des 12 SM sur RP2350 : indices 0..3 -> PIO0,
# 4..7 -> PIO1, 8..11 -> PIO2. Sur RP2040, l'indice 8 lève une erreur ;
# ici sm2 = rp2.StateMachine(9, ...) est légal.
# --- PWM matériel en parallèle : 50 Hz « servo » sur GP2 ---
# (PWM du RP2350 : 8 tranches/16 canaux sur RP2350A,
# 12 tranches/24 canaux sur RP2350B — une tranche par fréquence servo)
servo = PWM(Pin(2), freq=50, duty_u16=int(65535 * 1.5 / 20))
n = 0
while True:
n += 1 # le CPU ne fait QUE tourner ici : la PIO pilote la LED
time.sleep_ms(500)
Expected verification
The GP25 LED blinks at ~1 Hz while the Python loop runs at 500 ms: the PIO executes its own program, without any CPU instruction. The machine's freq=2000, divided by the ~2 × 32 × 30 cycles of the double counter, yields approximately 1 Hz. Then push the counter further: reuse indices 4..11 with different programs — all 12 slots must activate, proving the existence of the 3 blocks.
Common errors
- Reusing an already active SM index:
ValueError: PIO state machine in use— 12 slots, plan them (0-3 PIO0, 4-7 PIO1, 8-11 PIO2). - Forgetting that each PIO block has its own instruction memory, shared among its 4 SMs: two programs placed on the same block at the same offsets overwrite each other — two programs on different blocks are independent, which is the whole point of the 3rd block.
- PIO frequency vs system frequency:
freq=inStateMachineis the clock you give to the SM, but the ceiling comes from the system — the RP2350 runs at 150 MHz, not the default 125 MHz like the RP2040: recalibrate delays[nop] [29]migrated from RP2040 scripts. - Expecting a PIO IRQ "just like on RP2040": here each block has 2 dedicated IRQs (PIO0_IRQ_0, PIO0_IRQ_1, etc.) — in C, enable
PIO0_IRQ_0_IRQndistinctly fromPIO1_IRQ_0_IRQn; no more handlers unpacking interrupts from all blocks. - WS2812: the 800 kHz timing is critical — keep one dedicated SM per chain, and know that MicroPython already handles this via
neopixel(which sets up a PIO internally). - The 12 SMs do not replace DMA: PIO = generating/measuring signals, DMA (16 channels on RP2350) = moving data; combining them (PIO + FIFO + DMA) is the pattern for high-throughput acquisitions.
- 150 MHz ≠ 200 MHz: a PIO logic analyzer calibrated on an RP2040 overclocked to 200 MHz samples incorrectly on RP2350 — reprogram the sampling frequency.
Other tutorials for this board
- Flasher MicroPython sur une carte RP2350 générique et faire clignoter la première LED
- Console série sur RP2350 : USB CDC natif et UART0 de secours
- Lire l'ADC 12 bits et le capteur de température interne du RP2350
- Pico SDK 2.x sur une carte RP2350 générique : CMake, board header et PICO_BOARD
- Basculer les cœurs de Arm Cortex-M33 à Hazard3 RISC-V avec ARCHSEL
- Secure boot et TrustZone-M sur RP2350 : signer son firmware sans se faire avoir
Code examples for this board
clignotante.pyPythonbouton-interruption.pyPythonpwm-servo.pyPythonlecture-adc.pyPythonneopixel.pyPythonoled-i2c.pyPython