Raspberry Pi Pico Python SDK
Raspberry Pi Pico Python SDK
Colophon
Copyright © 2020 Raspberry Pi (Trading) Ltd.
The documentation of the RP2040 microcontroller is licensed under a Creative Commons Attribution-NoDerivatives 4.0
International (CC BY-ND).
build-date: 2021-02-01
build-version: ef82dc8-clean
Throughout the text "the SDK" refers to our Raspberry Pi Pico SDK. More details about the SDK can be
found in the Raspberry Pi Pico C/C++ SDK book.
Release History
Table 1. Release
Release Date Description
History
RPTL reserves the right to make any enhancements, improvements, corrections or any other modifications to the
RESOURCES or any products described in them at any time and without further notice.
The RESOURCES are intended for skilled users with suitable levels of design knowledge. Users are solely responsible for
Release History 1
Raspberry Pi Pico Python SDK
their selection and use of the RESOURCES and any application of the products described in them. User agrees to
indemnify and hold RPTL harmless against all liabilities, costs, damages or other losses arising out of their use of the
RESOURCES.
RPTL grants users permission to use the RESOURCES solely in conjunction with the Raspberry Pi products. All other use
of the RESOURCES is prohibited. No licence is granted to any other RPTL or other third party intellectual property right.
HIGH RISK ACTIVITIES. Raspberry Pi products are not designed, manufactured or intended for use in hazardous
environments requiring fail safe performance, such as in the operation of nuclear facilities, aircraft navigation or
communication systems, air traffic control, weapons systems or safety-critical applications (including life support
systems and other medical devices), in which the failure of the products could lead directly to death, personal injury or
severe physical or environmental damage (“High Risk Activities”). RPTL specifically disclaims any express or implied
warranty of fitness for High Risk Activities and accepts no liability for use or inclusions of Raspberry Pi products in High
Risk Activities.
Raspberry Pi products are provided subject to RPTL’s Standard Terms. RPTL’s provision of the RESOURCES does not
expand or otherwise modify RPTL’s Standard Terms including but not limited to the disclaimers and warranties expressed
in them.
Table of Contents
Colophon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Release History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
3.2. UART . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.3. ADC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
3.6. I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.7. SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.9.3. UART TX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.9.4. SPI. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
3.9.5. PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Wiring information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
List of Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Bill of Materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Wiring information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
List of Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Bill of Materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Wiring information . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
List of Files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
Bill of Materials . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Table of Contents 3
Raspberry Pi Pico Python SDK
MicroPython is a Python 3 implementation for microcontrollers and small embedded systems. Because MicroPython is
highly efficient, and RP2040 is designed with a disproportionate amount of system memory and processing power for its
price, MicroPython is a serious tool for embedded systems development, which does not compromise on approachability.
For exceptionally demanding pieces of software, you can fall back on the SDK (covered in Getting started with Raspberry
Pi Pico and Raspberry Pi Pico C/C++ SDK), or an external C module added to your MicroPython firmware, to wring out
the very last drop of performance. For every other project, MicroPython handles a lot of heavy lifting for you, and lets you
focus on writing the code that adds value to your project. The accelerated floating point libraries in RP2040’s on-board
ROM storage are used automatically by your Python code, so you should find arithmetic performance quite snappy.
Most on-chip hardware is exposed through the standard machine module, so existing MicroPython projects can be ported
without too much trouble. The second processor core is exposed through the _thread module.
RP2040 has some unique hardware you won’t find on other microcontrollers, with the programmable I/O system (PIO)
being the prime example of this: a versatile hardware subsystem that lets you create new I/O interfaces and run them at
high speed. In the rp2 module you will find a comprehensive PIO library which lets you write new PIO programs at the
MicroPython prompt, and interact with them in real time, to develop interfaces for new or unusual pieces of hardware (or
indeed if you just find yourself wanting an extra few serial ports).
MicroPython implements the entire Python 3.4 syntax (including exceptions, with, yield from, etc., and additionally async
/await keywords from Python 3.5). The following core datatypes are provided: str (including basic Unicode support), bytes,
bytearray, tuple, list, dict, set, frozenset, array.array, collections.namedtuple, classes and instances. Builtin modules
include sys, time, and struct, etc. Note that only a subset of Python 3 functionality is implemented for the data types and
modules.
MicroPython can execute scripts in textual source form (.py files) or from precompiled bytecode, in both cases either
from an on-device filesystem or "frozen" into the MicroPython executable.
Pre-built Binary
A pre-built binary of the latest MicroPython firmware is available from the Pico Getting Started pages.
The fastest way to get MicroPython is to download the pre-built release binary from the Pico Getting Started pages. If you
can’t or don’t want to use the pre-built release — for example, if you want to develop a C module for MicroPython — you
can follow the instructions in Section 1.3 to get the source code for MicroPython, which you can use to build your own
MicroPython firmware binary.
down the BOOTSEL button, and plug in the micro USB cable (which hopefully has the other end plugged into your
computer).
A drive called RPI-RP2 should pop up. Go ahead and drag the MicroPython firmware.uf2 file onto this drive. This programs
the MicroPython firmware onto the flash memory on your Raspberry Pi Pico.
It should take a few seconds to program the UF2 file into the flash. The board will automatically reboot when finished,
causing the RPI-RP2 drive to disappear, and boot into MicroPython.
By default, MicroPython doesn’t do anything when it first boots. It sits and waits for you to type in further instructions.
Chapter 2 shows how you can connect with the MicroPython firmware now running on your board. You can read on to see
how a custom MicroPython firmware file can be built from the source code.
The Getting started with Raspberry Pi Pico book has detailed instructions on getting your Raspberry Pi Pico into
BOOTSEL mode and loading UF2 files, in case you are having trouble. There is also a section going over loading ELF files
with the debugger, in case your board doesn’t have an easy way of entering BOOTSEL, or you would like to debug a
MicroPython C module you are developing.
NOTE
If you are not following these instructions on a Raspberry Pi Pico, you may not have a BOOTSEL button. If this is the
case, you should check if there is some other way of grounding the flash CS pin, such as a jumper, to tell RP2040 to
enter the BOOTSEL mode on boot. If there is no such method, you can load code using the Serial Wire Debug interface.
TIP
If you have already downloaded and installed a prebuilt MicroPython UF2 file, you can skip ahead to Chapter 2 to start
using your board.
IMPORTANT
These instructions for getting and building MicroPython assume you are using Raspberry Pi OS running on a
Raspberry Pi 4, or an equivalent Debian-based Linux distribution running on another platform.
It’s a good idea to create a pico directory to keep all pico-related checkouts in. These instructions create a pico directory
at /home/pi/pico.
$ cd ~/
$ mkdir pico
$ cd pico
Then clone the micropython git repository. These instructions will fetch the latest version of the source code.
Once the download has finished, the source code for MicroPython should be in a new directory called micropython. The
MicroPython repository also contains pointers (submodules) to specific versions of libraries it needs to run on a particular
board, like the SDK in the case of RP2040. We need to explicitly fetch these too:
$ cd micropython
$ git submodule update --init -- lib/pico-sdk
$ cd lib/pico-sdk
$ git submodule update --init
NOTE
The following instructions assume that you are using a Raspberry Pi Pico. Some details may differ if you are building
firmware for a different RP2040-based board. The board vendor should detail any extra steps needed to build firmware
for that particular board. The version we’re building here is fairly generic, but there might be some differences like
putting the default serial port on different pins, or including extra modules to drive that board’s hardware.
To build the RP2040 MicroPython port, you’ll need to install some extra tools. To build projects you’ll need CMake, a cross-
platform tool used to build the software, and the GNU Embedded Toolchain for Arm, which turns MicroPython’s C source
code into a binary program RP2040’s processors can understand. build-essential is a bundle of tools you need to build
code native to your own machine — this is needed for some internal tools in MicroPython and the SDK. You can install all
of these via apt from the command line. Anything you already have installed will be ignored by apt.
To build the port, you first need to change directory into the micropython repository containing the source. If you’ve been
following along with the instructions, you’ll need to go up two directories.
$ cd ../..
First we need to bootstrap a special tool for MicroPython builds, that ships with the source code:
$ make -C mpy-cross
We can now build the port we need for RP2040, that is, the version of MicroPython that has specific support for our chip.
$ cd ports/rp2
$ make
If everything went well, there will be a new directory called build (ports/rp2/build relative to the micropython directory),
which contains the new firmware binaries. The most important ones are:
firmware.uf2 A UF2 binary file which can dragged onto the RPI-RP2 drive that pops up once your Raspberry Pi
Pico is in BOOTSEL mode. The firmware binaries you will find on Pico Getting Started pages are
UF2 files, because they’re the easiest to install.
firmware.elf A different type of binary file, which can be loaded by a debugger (such as gdb with openocd) over
RP2040’s SWD debug port. This is useful for debugging either a native C module you’ve added to
MicroPython, or the MicroPython core interpreter itself. The actual binary contents is the same as
firmware.uf2.
You can take a look inside your new firmware.uf2 using picotool, see the Appendix B in the Getting started with Raspberry
Program Information
name: MicroPython
version: v1.13-288-g3ce8f14e0
features: USB REPL
thread support
frozen modules: _boot, rp2, ds18x20, onewire, uasyncio, uasyncio/core,
uasyncio/event, uasyncio/funcs, uasyncio/lock, uasyncio/stream
binary start: 0x10000000
binary end: 0x10038be4
embedded drive: 0x100a0000-0x10200000 (1408K): MicroPython
Build Information
sdk version: 1.0.0
pico_board: pico
build date: Jan 21 2021
build attributes: MinSizeRel
Read MicroPython waits for you to type in some text, followed by the enter key.
Evaluate Whatever you typed is interpreted as Python code, and runs immediately.
Print Any results of the last line you typed are printed out for you to read.
There are two ways to connect to this REPL, so you can communicate with the MicroPython firmware on your board: over
USB, and over the UART serial port on Raspberry Pi Pico GPIOs.
TIP
You can run ls /dev/tty* to list your serial ports. There may be quite a few, but MicroPython’s USB serial will start with
/dev/ttyACM. If in doubt, unplug the micro USB connector and see which one disappears. If you don’t see anything, you
can try rebooting your Raspberry Pi.
$ minicom -o -D /dev/ttyACM0
Where the -D /dev/ttyACM0 is pointing minicom at MicroPython’s USB serial port, and the -o flag essentially means "just do
it". There’s no need to worry about baud rate, since this is a virtual serial port.
Press the enter key a few times in the terminal where you opened minicom. You should see this:
>>>
This is a prompt. MicroPython wants you to type something in, and tell it what to do.
If you press CTRL-D on your keyboard whilst the minicom terminal is focused, you should see a message similar to this:
This key combination tells MicroPython to reboot. You can do this at any time. When it reboots, MicroPython will print out
a message saying exactly what firmware version it is running, and when it was built. Your version number will be different
from the one shown here.
The MicroPython port for RP2040 does not expose REPL over a UART port by default. However this default can be
changed in the mpconfigport.h source file.
Go ahead and download the MicroPython source (see Section 1.3) and in ports/rp2/mpconfigport.h change
MICROPY_HW_ENABLE_UART_REPL to 1 to enable it.
Then continue to follow the instructions in Section 1.3 to build your own MicroPython UF2 firmware.
This will allow the REPL to be accessed over a UART port, through two GPIOs pin. By default on Raspberry Pi Pico this is
on GPIO0 (TX, MicroPython output) and GPIO1 (RX, MicroPython input), and the speed is 115200 baud. This alternative
interface is handy if you have trouble with USB, if you don’t have any free USB ports, or if you are using some other
RP2040-based board which doesn’t have an exposed USB connector.
NOTE
This initially occupies the UART0 peripheral on RP2040. The UART1 peripheral is free for you to use in your Python code as
a second UART.
To connect, the first thing you’ll need to do is to enable UART serial on the Raspberry Pi. To do so, run raspi-config,
$ sudo raspi-config
and go to Interfacing Options → Serial and select "No" when asked "Would you like a login shell to be accessible over
serial?" and "Yes" when asked "Would you like the serial port hardware to be enabled?" You should see something like
Figure 1.
Figure 1. Enabling a
serial UART using
raspi-config on
the Raspberry Pi.
Leaving raspi-config you should choose "Yes" and reboot your Raspberry Pi to enable the serial port.
You should then wire the Raspberry Pi and the Raspberry Pi Pico together with the following mapping:
GND GND
IMPORTANT
RX matches to TX, and TX matches to RX. You mustn’t connect the two opposite TX pins together, or the two RX pins.
This is because MicroPython needs to listen on the channel that the Raspberry Pi transmits on, and vice versa.
See Figure 2.
Figure 2. A Raspberry
Pi 4 and the Raspberry
Pi Pico with UART0
connected together.
If you press the enter key, MicroPython should respond by prompting you for more input:
>>>
If you don’t already have a Terminal program installed you can install minicom using Homebrew,
NOTE
The machine module is used to control on-chip hardware. This is standard on all MicroPython ports, and you can read more
about it in the MicroPython documentation. Here we are using it to take control of a GPIO, so we can drive it high and low.
If you type this in,
>>> led.value(1)
The LED should turn on. You can turn it off again with
>>> led.value(0)
You can read on to the next chapter, which goes into the specifics of MicroPython on RP2040, and where it differs from
other platforms. Chapter 3 also has some short examples of the different APIs offered to interact with the hardware.
You can learn how to set up an integrated development environment (IDE) in Chapter 4, so you don’t have to type
programs in line by line.
You can dive straight into Appendix A if you are eager to start connecting wires to a breadboard.
This chapter will give a very brief tour of some of the hardware APIs, with code examples you can either type into the
REPL (Chapter 2) or load onto the board using a development environment installed on your computer (Chapter 4).
This is, to put it mildy, quite a convoluted way of turning a light on and off. A light switch would work better. The
machine.Timer class, which uses RP2040’s hardware timer to trigger callbacks at regular intervals, saves a lot of typing if
we want the light to turn itself on and off repeatedly, thus bringing our level of automation from "mechanical switch" to
"555 timer".
Typing this program into the REPL will cause the LED to start blinking, but the prompt will appear again:
>>>
The Timer we created will run in the background, at the interval we specified, blinking the LED. The MicroPython prompt is
still running in the foreground, and we can enter more code, or start more timers.
3.2. UART
USB serial is available from MicroPython, but the REPL is also available over UART0 by default. The default settings for
UARTs are taken from the C SDK.
UART_BAUDRATE 115,200
UART_BITS 8
UART_STOP 1
UART0_TX Pin 0
UART0_RX Pin 1
UART1_TX Pin 4
UART1_RX Pin 5
3.3. ADC
An analogue-to-digital converter (ADC) measures some analogue signal and encodes it as a digital number. The ADC on
RP2040 measures voltages.
An ADC has two key features: its resolution, measured in digital bits, and its channels, or how many analogue signals it
can accept and convert at once. The ADC on RP2040 has a resolution of 12-bits, meaning that it can transform an
analogue signal into a digital signal as a number ranging from 0 to 4095 – though this is handled in MicroPython
transformed to a 16-bit number ranging from 0 to 65,535, so that it behaves the same as the ADC on other MicroPython
microcontrollers.
RP2040 has five ADC channels total, four of which are brought out to chip GPIOs: GP26, GP27, GP28 and GP29. On
Raspberry Pi Pico, the first three of these are brought out to GPIO pins, and the fourth can be used to measure the VSYS
voltage on the board.
The ADC’s fifth input channel is connected to a temperature sensor built into RP2040.
3.2. UART 14
Raspberry Pi Pico Python SDK
You can specify which ADC channel you’re using by pin number, e.g.
or by channel,
An example reading the fourth analogue-to-digital (ADC) converter channel, connected to the internal temperature sensor:
1 import machine
2 import utime
3
4 sensor_temp = machine.ADC(4)
5 conversion_factor = 3.3 / (65535)
6
7 while True:
8 reading = sensor_temp.read_u16() * conversion_factor
9
10 temperature = 27 - (reading - 0.706)/0.001721
11 print(temperature)
12 utime.sleep(2)
3.4. Interrupts
You can set an IRQ like this:
3.4. Interrupts 15
Raspberry Pi Pico Python SDK
5 for i in range(n):
6 led.high()
7 time.sleep(delay)
8 led.low()
9 time.sleep(delay)
10 print('done')
11
12 _thread.start_new_thread(task, (10, 0.5))
Only one thread can be started/running at any one time, because there is no RTOS just a second core. The GIL is not
enabled so both core0 and core1 can run Python code concurrently, with care to use locks for shared data.
3.6. I2C
Example usage:
I2C can be constructed without specifying the frequency, if you just want all the defaults.
WARNING
There may be some bugs reading/writing to device addresses that do not respond, the hardware seems to lock up in
some cases.
3.6. I2C 16
Raspberry Pi Pico Python SDK
3.7. SPI
Example usage:
NOTE
SPI_BAUDRATE 1,000,000
SPI_POLARITY 0
SPI_PHASE 0
SPI_BITS 8
SPI_FIRSTBIT MSB
SPI0_SCK Pin 6
SPI0_MOSI Pin 7
SPI0_MISO Pin 4
SPI1_SCK Pin 10
SPI1_MOSI Pin 11
SPI1_MISO Pin 8
3.8. PWM
Example of using PWM to fade an LED:
3.7. SPI 17
Raspberry Pi Pico Python SDK
8 pwm = PWM(Pin(25))
9
10 # Set the PWM frequency.
11 pwm.freq(1000)
12
13 # Fade the LED in and out a few times.
14 duty = 0
15 direction = 1
16 for _ in range(8 * 256):
17 duty += direction
18 if duty > 255:
19 duty = 255
20 direction = -1
21 elif duty < 0:
22 duty = 0
23 direction = 1
24 pwm.duty_u16(duty * duty)
25 time.sleep(0.001)
The Raspberry Pi Pico MicroPython introduces a new @rp2.asm_pio decorator, along with a rp2.PIO class. The definition of a
PIO program, and the configuration of the state machine, into 2 logical parts:
• The program definition, including how many pins are used and if they are in/out pins. This goes in the @rp2.asm_pio
definition. This is close to what the pioasm tool from the SDK would generate from a .pio file (but here it’s all defined
in Python).
• The program instantiation, which sets the frequency of the state machine and which pins to bind to. These get set
when setting a SM to run a particular program.
The aim was to allow a program to be defined once and then easily instantiated multiple times (if needed) with different
GPIO. Another aim was to make it easy to basic things without getting weighed down in too much PIO/SM configuration.
1 import time
2 from rp2 import PIO, asm_pio
3 from machine import Pin
4
5 # Define the blink program. It has one GPIO to bind to on the set instruction, which is an
output pin.
6 # Use lots of delays to make the blinking visible by eye.
7 @asm_pio(set_init=rp2.PIO.OUT_LOW)
8 def blink():
9 wrap_target()
10 set(pins, 1) [31]
11 nop() [31]
12 nop() [31]
13 nop() [31]
14 nop() [31]
15 set(pins, 0) [31]
16 nop() [31]
17 nop() [31]
18 nop() [31]
19 nop() [31]
20 wrap()
21
22 # Instantiate a state machine with the blink program, at 1000Hz, with set bound to Pin(25)
(LED on the rp2 board)
23 sm = rp2.StateMachine(0, blink, freq=1000, set_base=Pin(25))
24
25 # Run the state machine for 3 seconds. The LED should blink.
26 sm.active(1)
27 time.sleep(3)
28 sm.active(0)
• All program configuration (eg autopull) is done in the @asm_pio decorator. Only the frequency and base pins are set in
the StateMachine constructor.
The idea is that for the 4 sets of pins (in, out, set, sideset, excluding jmp) that can be connected to a state machine, there’s
the following that need configuring for each set:
1. base GPIO
In the design of the Python API for PIO these 4 items are split into "declaration" (items 2-4) and "instantiation" (item 1). In
other words, a program is written with items 2-4 fixed for that program (eg a WS2812 driver would have 1 output pin) and
item 1 is free to change without changing the program (eg which pin the WS2812 is connected to).
So in the @asm_pio decorator you declare items 2-4, and in the StateMachine constructor you say which base pin to use (item
1). That makes it easy to define a single program and instantiate it multiple times on different pins (you can’t really
change items 2-4 for a different instantiation of the same program, it doesn’t really make sense to do that).
And the same keyword arg (in the case about it’s sideset_pins) is used for both the declaration and instantiation, to show
that they are linked.
To declare multiple pins in the decorator (the count, ie item 2 above), you use a tuple/list of values. And each item in the
tuple/list specified items 3 and 4. For example:
In this example:
• there are 3 set pins connected to the SM, and their initial state (set when the StateMachine is created) is: output low,
output high, input low (used for open-drain)
3.9.1. IRQ
There is support for PIO IRQs, e.g.
1 import time
2 import rp2
3
4 @rp2.asm_pio()
5 def irq_test():
6 wrap_target()
7 nop() [31]
8 nop() [31]
9 nop() [31]
10 nop() [31]
11 irq(0)
12 nop() [31]
13 nop() [31]
14 nop() [31]
15 nop() [31]
16 irq(1)
17 wrap()
18
19
20 rp2.PIO(0).irq(lambda pio: print(pio.irq().flags()))
21
22 sm = rp2.StateMachine(0, irq_test, freq=1000)
23 sm.active(1)
24 time.sleep(1)
25 sm.active(0)
An example program that blinks at 1Hz and raises an IRQ at 1Hz to print the current millisecond timestamp,
1 # Example using PIO to wait for a pin change and raise an IRQ.
2 #
3 # Demonstrates:
4 # - PIO wrapping
5 # - PIO wait instruction, waiting on an input pin
6 # - PIO irq instruction, in blocking mode with relative IRQ number
7 # - setting the in_base pin for a StateMachine
8 # - setting an irq handler for a StateMachine
9 # - instantiating 2x StateMachine's with the same program and different pins
10
11 import time
12 from machine import Pin
13 import rp2
14
15
16 @rp2.asm_pio()
17 def wait_pin_low():
18 wrap_target()
19
20 wait(0, pin, 0)
21 irq(block, rel(0))
22 wait(1, pin, 0)
23
24 wrap()
25
26
27 def handler(sm):
28 # Print a (wrapping) timestamp, and the state machine object.
29 print(time.ticks_ms(), sm)
30
31
32 # Instantiate StateMachine(0) with wait_pin_low program on Pin(16).
33 pin16 = Pin(16, Pin.IN, Pin.PULL_UP)
34 sm0 = rp2.StateMachine(0, wait_pin_low, in_base=pin16)
35 sm0.irq(handler)
36
37 # Instantiate StateMachine(1) with wait_pin_low program on Pin(17).
38 pin17 = Pin(17, Pin.IN, Pin.PULL_UP)
39 sm1 = rp2.StateMachine(1, wait_pin_low, in_base=pin17)
40 sm1.irq(handler)
41
42 # Start the StateMachine's running.
43 sm0.active(1)
44 sm1.active(1)
45
46 # Now, when Pin(16) or Pin(17) is pulled low a message will be printed to the REPL.
16 wrap_target()
17 label("bitloop")
18 out(x, 1) .side(0) [T3 - 1]
19 jmp(not_x, "do_zero") .side(1) [T1 - 1]
20 jmp("bitloop") .side(1) [T2 - 1]
21 label("do_zero")
22 nop() .side(0) [T2 - 1]
23 wrap()
24
25
26 # Create the StateMachine with the ws2812 program, outputting on Pin(22).
27 sm = rp2.StateMachine(0, ws2812, freq=8_000_000, sideset_base=Pin(22))
28
29 # Start the StateMachine, it will wait for data on its FIFO.
30 sm.active(1)
31
32 # Display a pattern on the LEDs via an array of LED RGB values.
33 ar = array.array("I", [0 for _ in range(NUM_LEDS)])
34
35 # Cycle colours.
36 for i in range(4 * NUM_LEDS):
37 for j in range(NUM_LEDS):
38 r = j * 100 // (NUM_LEDS - 1)
39 b = 100 - j * 100 // (NUM_LEDS - 1)
40 if j != i % NUM_LEDS:
41 r >>= 3
42 b >>= 3
43 ar[j] = r << 16 | b
44 sm.put(ar, 8)
45 time.sleep_ms(50)
46
47 # Fade out.
48 for i in range(24):
49 for j in range(NUM_LEDS):
50 ar[j] >>= 1
51 sm.put(ar, 8)
52 time.sleep_ms(50)
3.9.3. UART TX
A UART TX example,
NOTE
You need to specify an initial OUT pin state in your program in order to be able to pass OUT mapping to your SM
instantiation, even though in this program it is redundant because the mappings overlap.
3.9.4. SPI
An SPI example.
NOTE
This SPI program supports programmable frame sizes (by holding the reload value for X counter in the Y register) but
currently this can’t be used, because the autopull threshold is associated with the program, instead of the SM
instantiation.
3.9.5. PWM
A PWM example,
1 # Example of using PIO for PWM, and fading the brightness of an LED
2
3 from machine import Pin
4 from rp2 import PIO, StateMachine, asm_pio
5 from time import sleep
6
7
8 @asm_pio(sideset_init=PIO.OUT_LOW)
9 def pwm_prog():
10 pull(noblock) .side(0)
11 mov(x, osr) # Keep most recent pull data stashed in X, for recycling by noblock
12 mov(y, isr) # ISR must be preloaded with PWM count max
13 label("pwmloop")
14 jmp(x_not_y, "skip")
15 nop() .side(1)
16 label("skip")
17 jmp(y_dec, "pwmloop")
18
19
20 class PIOPWM:
21 def __init__(self, sm_id, pin, max_count, count_freq):
22 self._sm = StateMachine(sm_id, pwm_prog, freq=2 * count_freq, sideset_base=Pin(pin))
23 # Use exec() to load max count into ISR
24 self._sm.put(max_count)
25 self._sm.exec("pull()")
26 self._sm.exec("mov(isr, osr)")
27 self._sm.active(1)
28 self._max_count = max_count
29
30 def set(self, value):
31 # Minimum value is -1 (completely turn off), 0 actually still produces narrow pulse
32 value = max(value, -1)
33 value = min(value, self._max_count)
34 self._sm.put(value)
35
36
37 # Pin 25 is LED on Pico boards
38 pwm = PIOPWM(0, 25, max_count=(1 << 16) - 1, count_freq=10_000_000)
39
40 while True:
41 for i in range(256):
42 pwm.set(i ** 2)
43 sleep(0.01)
For more information on pioasm see the Raspberry Pi Pico C/C++ SDK book which talks about the C/C++ SDK.
Alternatively if you are working on a Raspberry Pi you should install Thonny using apt from the command line,
this will add a Thonny icon to the Raspberry Pi desktop menu. Go ahead and select Raspberry Pi → Programming →
Thonny Python IDE to open the development environment.
NOTE
When opening Thonny for the first time select "Standard Mode." For some versions this choice will be made via a
popup when you first open Thonny. However for the Raspberry Pi release you should click on the text in the top right of
the window to switch to "Regular Mode."
Download the Pico backend wheel from Github, https://github.com/raspberrypi/thonny-pico/releases/latest. This wheel
file can be installed into Thonny version 3.3.0b6 or later.
Start Thonny and navigate to "Tools → Manage plug-ins" and click on the link to "Install from local file" in the right hand
panel, and select the Pico backend wheel (see Figure 3). Hit the "Close" button to finish. Afterwards you should quit and
restart Thonny.
Hit "OK". If your Raspberry Pi Pico is plugged in and running MicroPython Thonny should automatically connect to the
REPL.
If this doesn’t happen go to Tools → Options menu item, and select your serial port in the drop down on the "Interpreter"
tab. On the Raspberry Pi the serial port will be "Board in FS Mode — Board CDC (/dev/ttyACM0)" this should automatically
connect you to the REPL of your Raspberry Pi Pico. Afterwards go to the "View" menu and select the "Variables" option to
open the variables panel.
NOTE
In the rare case where you can’t connect to Raspberry Pi Pico you may have to reboot your Raspberry Pi.
You can now access the REPL from the Shell panel,
see Figure 5.
Enter the code in the main panel, then click on the green run button. Thonny will present you with a popup, click on
"MicroPython device" and enter "test.py" to save the code to the Raspberry Pi Pico, see Figure 6.
NOTE
If you "save a file to the device" and give it the special name main.py, then MicroPython starts running that script as
soon as power is supplied to Raspberry Pi Pico in the future.
The program should uploaded to the Raspberry Pi Pico using the REPL, and automatically start running. You should see
the onboard LED start blinking, connected to GPIO pin 25, and the variables change in the Thonny variable window, see
Figure 7.
Figure 7. Blinking an
LED using a timer from
the Thonny
environment.
Wiring information
See Figure 8 for wiring instructions.
List of Files
A list of files with descriptions of their function;
i2c_1306oled_using_defaults.py
The example code.
13
14 oled = SSD1306_I2C(WIDTH, HEIGHT, i2c) # Init oled display
15
16 # Raspberry Pi logo as 32x32 bytearray
17 buffer = bytearray(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00|?\x00\x01\x86
@\x80\x01\x01\x80\x80\x01\x11\x88\x80\x01\x05\xa0\x80\x00\x83\xc1\x00\x00C\xe3\x00\x00
~\xfc\x00\x00L'\x00\x00\x9c\x11\x00\x00\xbf\xfd\x00\x00\xe1\x87\x00\x01\xc1\x83\x80\x02A
\x82@\x02A\x82@\x02\xc1\xc2@\x02\xf6>\xc0\x01\xfc
=\x80\x01\x18\x18\x80\x01\x88\x10\x80\x00\x8c!\x00\x00\x87\xf1\x00\x00\x7f\xf6\x00\x00
8\x1c\x00\x00\x0c \x00\x00\x03\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")
18
19 # Load the raspberry pi logo into the framebuffer (the image is 32x32)
20 fb = framebuf.FrameBuffer(buffer, 32, 32, framebuf.MONO_HLSB)
21
22 # Clear the oled display in case it has junk on it.
23 oled.fill(0)
24
25 # Blit the image from the framebuffer to the oled display
26 oled.blit(fb, 96, 0)
27
28 # Add some text
29 oled.text("Raspberry Pi",5,5)
30 oled.text("Pico",5,15)
31
32 # Finally update the oled display so the image & text is displayed
33 oled.show()
i2c_1306oled_with_freq.py
The example code, explicitly sets a frequency.
24
25 # Blit the image from the framebuffer to the oled display
26 oled.blit(fb, 96, 0)
27
28 # Add some text
29 oled.text("Raspberry Pi",5,5)
30 oled.text("Pico",5,15)
31
32 # Finally update the oled display so the image & text is displayed
33 oled.show()
Bill of Materials
Table 5. A list of
Item Quantity Details
materials required for
the example
Breadboard 1 generic part
Wiring information
See Figure 8 for wiring instructions.
List of Files
A list of files with descriptions of their function;
i2c_1106oled_using_defaults.py
The example code.
i2c_1106oled_with_freq.py
The example code, explicitly sets a frequency.
sh1106.py
SH1106 Driver Obtained from https://github.com/robert-hh/SH1106
1 #
2 # MicroPython SH1106 OLED driver, I2C and SPI interfaces
3 #
4 # The MIT License (MIT)
5 #
6 # Copyright (c) 2016 Radomir Dopieralski (@deshipu),
7 # 2017 Robert Hammelrath (@robert-hh)
8 #
9 # Permission is hereby granted, free of charge, to any person obtaining a copy
10 # of this software and associated documentation files (the "Software"), to deal
11 # in the Software without restriction, including without limitation the rights
12 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 # copies of the Software, and to permit persons to whom the Software is
14 # furnished to do so, subject to the following conditions:
15 #
16 # The above copyright notice and this permission notice shall be included in
17 # all copies or substantial portions of the Software.
18 #
19 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
149 self.write_cmd(_HIGH_COLUMN_ADDRESS | 0)
150 self.write_data(self.buffer[
151 self.width * page:self.width * page + self.width
152 ])
153
154 def reset(self, res):
155 if res is not None:
156 res(1)
157 time.sleep_ms(1)
158 res(0)
159 time.sleep_ms(20)
160 res(1)
161 time.sleep_ms(20)
162
163
164 class SH1106_I2C(SH1106):
165 def __init__(self, width, height, i2c, res=None, addr=0x3c,
166 external_vcc=False):
167 self.i2c = i2c
168 self.addr = addr
169 self.res = res
170 self.temp = bytearray(2)
171 if res is not None:
172 res.init(res.OUT, value=1)
173 super().__init__(width, height, external_vcc)
174
175 def write_cmd(self, cmd):
176 self.temp[0] = 0x80 # Co=1, D/C#=0
177 self.temp[1] = cmd
178 self.i2c.writeto(self.addr, self.temp)
179
180 def write_data(self, buf):
181 self.i2c.writeto(self.addr, b'\x40'+buf)
182
183 def reset(self):
184 super().reset(self.res)
185
186
187 class SH1106_SPI(SH1106):
188 def __init__(self, width, height, spi, dc, res=None, cs=None,
189 external_vcc=False):
190 self.rate = 10 * 1000 * 1000
191 dc.init(dc.OUT, value=0)
192 if res is not None:
193 res.init(res.OUT, value=0)
194 if cs is not None:
195 cs.init(cs.OUT, value=1)
196 self.spi = spi
197 self.dc = dc
198 self.res = res
199 self.cs = cs
200 super().__init__(width, height, external_vcc)
201
202 def write_cmd(self, cmd):
203 self.spi.init(baudrate=self.rate, polarity=0, phase=0)
204 if self.cs is not None:
205 self.cs(1)
206 self.dc(0)
207 self.cs(0)
208 self.spi.write(bytearray([cmd]))
209 self.cs(1)
210 else:
211 self.dc(0)
212 self.spi.write(bytearray([cmd]))
213
214 def write_data(self, buf):
215 self.spi.init(baudrate=self.rate, polarity=0, phase=0)
216 if self.cs is not None:
217 self.cs(1)
218 self.dc(1)
219 self.cs(0)
220 self.spi.write(buf)
221 self.cs(1)
222 else:
223 self.dc(1)
224 self.spi.write(buf)
225
226 def reset(self):
227 super().reset(self.res)
Bill of Materials
Table 6. A list of
Item Quantity Details
materials required for
the example
Breadboard 1 generic part
Wiring information
See Figure 10 for wiring instructions.
List of Files
A list of files with descriptions of their function;
neopixel_ring.py
The example code.
53
54 def color_chase(color, wait):
55 for i in range(NUM_LEDS):
56 pixels_set(i, color)
57 time.sleep(wait)
58 pixels_show()
59 time.sleep(0.2)
60
61 def wheel(pos):
62 # Input a value 0 to 255 to get a color value.
63 # The colours are a transition r - g - b - back to r.
64 if pos < 0 or pos > 255:
65 return (0, 0, 0)
66 if pos < 85:
67 return (255 - pos * 3, pos * 3, 0)
68 if pos < 170:
69 pos -= 85
70 return (0, 255 - pos * 3, pos * 3)
71 pos -= 170
72 return (pos * 3, 0, 255 - pos * 3)
73
74
75 def rainbow_cycle(wait):
76 for j in range(255):
77 for i in range(NUM_LEDS):
78 rc_index = (i * 256 // NUM_LEDS) + j
79 pixels_set(i, wheel(rc_index & 255))
80 pixels_show()
81 time.sleep(wait)
82
83 BLACK = (0, 0, 0)
84 RED = (255, 0, 0)
85 YELLOW = (255, 150, 0)
86 GREEN = (0, 255, 0)
87 CYAN = (0, 255, 255)
88 BLUE = (0, 0, 255)
89 PURPLE = (180, 0, 255)
90 WHITE = (255, 255, 255)
91 COLORS = (BLACK, RED, YELLOW, GREEN, CYAN, BLUE, PURPLE, WHITE)
92
93 print("fills")
94 for color in COLORS:
95 pixels_fill(color)
96 pixels_show()
97 time.sleep(0.2)
98
99 print("chases")
100 for color in COLORS:
101 color_chase(color, 0.01)
102
103 print("rainbow")
104 rainbow_cycle(0)
Bill of Materials
Table 7. A list of
Item Quantity Details
materials required for
the example
Breadboard 1 generic part