Skip to content

PIO (Programmable I/O) support for RP2040 / RP2350 #1808

Description

@josesimoes

Discussed in https://github.com/orgs/nanoframework/discussions/1807

Originally posted by begeistert June 25, 2026
Hi! I've built PIO (Programmable I/O) support for RP2040 / RP2350 in nanoFramework and would love to contribute it upstream — opening this to gauge interest and figure out the preferred path before a formal feature request.

The gap

Today a Pico app can do GPIO / I2C / SPI / PWM / ADC from C#, but PIO — the chip's headline feature — isn't reachable at all. PIO is the reason people reach for a Pico: WS2812/NeoPixel, custom serial protocols, precise timing, quadrature decoding, DPI/HUB75, etc. There's also no managed way to author PIO programs (other approaches force the external pioasm tool + a raw ushort[], which loses the program metadata the runtime needs).

What I have (working prototype)

  • An in-CLR inline PIO assembler — write PIO programs in C# with a fluent / System.Reflection.Emit-style builder (no external pioasm, no raw ushort[]), byte-exact vs pioasm (golden tests). It returns a rich PioProgram (wrap / side-set / shift metadata) that seeds the state-machine config automatically.
  • A thin runtime API (Pio / PioBlock / PioStateMachine) backed by InternalCall native interop in nf-interpreter, version-aware for RP2350 PIO v1 (3rd block PIO2, FIFO PUTGET join, GPIO base).
  • Validated end-to-end: byte-exact golden tests, behaviour on an RP2040/RP2350 emulator, and on real silicon — a Pico 2 blinking & fading its on-board LED via a C#-assembled PIO program running through the nanoCLR.
// Assemble a PIO program in C# (byte-exact vs pioasm), then run it on a state machine:
PioBlock pio   = Pio.Get(0);                       // 0..1 (RP2040) / 0..2 (RP2350)
uint offset    = pio.AddProgram(ws2812);           // loads instr memory, relocates JMPs
PioStateMachine sm = pio.ClaimStateMachine();
sm.Init(offset, PioStateMachineConfig.FromProgram(ws2812, (int)offset)  // wrap/side-set/shift auto-applied
                    .SideSetPins(2).OutPins(2, 1).ClockDivisor(15.625f));
pio.InitGpio(2);
sm.SetConsecutivePinDirs(2, 1, true);
sm.Enabled = true;
sm.Put(grb << 8);                                  // CPU just feeds the FIFO; the SM bit-bangs in hardware

Proposed shape

A nanoFramework.Hardware.Rp2040 library (same pattern as nanoFramework.Hardware.Esp32.Rmt, a chip-specific programmable-timing peripheral) + the PIO native in nf-interpreter, bound by the usual MDP checksum/version stubs.

Questions for the team

  1. Is there interest in upstreaming PIO support for the RP2040/RP2350 family?
  2. Preferred structure — a new nanoFramework.Hardware.Rp2040 org repo + the native in nf-interpreter?
  3. How would you like the RP2350 PIO-v1 version-awareness handled?

I have a feature request drafted and runnable demo samples (WS2812 / blink / fade), and I'm happy to open the formal issue, split the work into the managed-library PR(s) and the nf-interpreter native PR(s), and demo the silicon run. Thanks! 🙏

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions