How PIOs work on the RP23xx family

This was recommended to me by a friend and is a great explanation of how they work, something i hadn’t really dug into in the past.

PIOs are a very significant feature of the RP2350 and RP2040. In my application (grblHAL CNC motion controller), PIOs allow the RP2350 at 150 mhz to perform as well as the iMXRT1062 (an M7 Cortex ARM on the Teensy 4.1) at 600 mhz when generating step pulse trains with acceleration/deceleration. This is with almost no jitter because the PIO doesn’t get interrupted. grblHAL on STM32 and ESP32 have significantly more jitter.

Yeah, PIOs are great. I did a twin AiP33628 seven segment four digit LED drive with PIOs recently, only 13 lines of pioasm, and no flicker no matter what the RP2040’s main CPU is doing.

Any more info on this? Sounds very cool.

Sure. I thought I had posted something to Embedded Slack, but it has got too old and disappeared.

It’s a digital clock in my office, and another in bedroom. They arrived as a cheap “soldering kit”, a PCB with SMT RGB LEDs and a plastic case with diffuser. Search for GY21261 for the kit PDF.

I didn’t like the firmware as described in the kit documentation. It was closed source. The time had to be set, no WiFi. I didn’t have a build chain for the microcontroller. I decoded the protocol with a DSO, and got help from someone on Slack who had found the datasheet (thanks, whoever that was, it’s expired content now!).

So I attached an RP2040 as a Pico W, with four I/O pins leading to the socket of the original microcontroller.

It was a rapid prototyping approach. CircuitPython. A bytearray framebuffer. Two rp2pio.StateMachine instances, one for each AiP33628, bound to different pins. The “draw the segments” function calls background_write on the pipes. The PIO code does all the work. It looks like this;

def init_draw():
    """
    configure rp2040 pio state machines

    emit a start pulse on the data line
    discard two bits of data from msb
    emit 29 bits of data msb first with clocking
    hold the clock high when done
    emit a stop pulse on the data line
    """
    global sm_m, sm_h

    pipe = adafruit_pioasm.assemble(
        """
.program pipe
.side_set 1

    pull           side 0  [8]
    set  pins 1    side 0  [1]
    set  pins 0    side 0
    out  y 2       side 0
    set  y 29      side 0

loop:
    nop            side 0
    out  pins 1    side 0
    nop            side 1
    jmp  y-- loop  side 1
    set  pins 0    side 1  [4]
    set  pins 1    side 1  [1]
    set  pins 0    side 1
    nop            side 0
        """
    )

    # minimum 30khz to prevent flicker
    # maximum 3mhz to prevent latching failure and false lit segments
    sm_h = rp2pio.StateMachine(
        pipe,
        frequency=3_000_000,
        first_sideset_pin=board.GP1,  # pico.2 -- u4.1
        first_set_pin=board.GP0,  # pico.1 -- u4.2
        first_out_pin=board.GP0,
        wait_for_txstall=False,
        out_shift_right=False,
    )

    sm_m = rp2pio.StateMachine(
        pipe,
        frequency=2_900_000,
        first_sideset_pin=board.GP3, # pico.5 -- u4.16
        first_set_pin=board.GP2,  # pico.4 -- u4.5
        first_out_pin=board.GP2,
        wait_for_txstall=False,
        out_shift_right=False,
    )

it has become a general purpose digital display since then, mostly showing the NTP synchronised local time, but during experiments can be scripted from command line of desktop to show any four digit number in red, green, or blue. Automatic recovery from reboot of wireless router was the thing that took the longest to resolve.

2 Likes