Flashing lights with MicroPython and Programmable I/O part 1

By Ben Everard. Posted

Programmable I/O is a unique feature of Raspberry Pi Pico that lets you handle data going out of, or into the I/O pins without needing CPU intervention. What does this mean? Let's take a look using blinking lights as an example. There's an LED on pin 25 of Pico, so we can get started using this with no extra hardware.

Sometimes a blinking LED is needed to let the user know what's going on with your project. That's easy enough to do, but it's not always visually appealing. A slow fade in can be a better look. While this isn't hard to program on its own, if you've got other processing to do, it can be hard to juggle CPU time to make sure the fade remains smooth. Let's take a look at how to do this with Programmable I/O in MicroPython.

Programmable I/O is based around state machines. These are mini-processors that you can program with a special form of assembly language. You can find out more information on using the full PIO assembly language in MicroPython here, but for our purposes, we're just going to be using one instruction, set. In this case, it'll be used to set a pin as high or low.

Each Pico has 8 state machines, and we're going to use 2 of them. One will continuously turn the LED on, the other will continuously turn it off. We get our fade by running these at slightly different frequencies so they interact with each other in a cycle that creates a fading.

Let's see what this looks like in MicroPython (you can get the latest version to install on your pico at https://pico.raspberrypi.org/getting-started/)

from rp2 import PIO, StateMachine, asm_pio
from machine import Pin
import time

def led_off():
    set(pins, 0)

def led_on():
    set(pins, 1)

sm1 = StateMachine(1, led_off, freq=20000, set_base=Pin(25))
sm2 = StateMachine(2, led_on, freq=20002, set_base=Pin(25))


As you can see, we use the @asm_pio decorator to let MicroPython know that a method is written in PIO assembly. We need to pass a parameter to the PIO assembler to let it know the initial state of the pin. We've set it low in both progams. In this setup, each state machine does one thing: turns an LED on (with a 1) or off (with a 0). These programs automatically loop, so they'll continue turning the LED on and off.

We need to load the programs into state machines before we can use them. The StateMachine class takes a range of parameters, but we only need the most basic ones:

  • the state machine number

  • the program we want it to run

  • the frequency we want to run it at

  • the pin we want the set instruction to operate on

As you can see, we've set them running at slightly different frequencies, so the programs get a little out of sync over time. This means that the proportion of time the LED will be on or off will vary, leading to a rhythmical fading in then turning off.

Finally we need to activate the state machines. This will set them running in the background and leave the main MicroPython program to get on doing whatever you want to do. If you want to disable them, you just need to run:


to stop the state machine turning the led on.

That's the very basics of using PIO, creating a state machine and setting it running. In the next part, we'll take a look at how to send data to our state machines to control the LEDs.

You can get your very own Pico by getting a paper copy of HackSpace mag issue 39 or subscribing to HackSpace magazine from just £10. If you're in the UK head here. For delivery elsewhere in the world, take a look here.


From HackSpace magazine store


Subscribe to our newsletter