Boards

The Raspberry Pi Pico

An RP2040 on our Cortex-M0+ core: one core, SIO GPIO, the TIMER, UART0, PWM and the ADC.

The overview, with examples running, is on the board page and The Raspberry Pi Pico model (every register).

What it is

The Raspberry Pi Pico, an RP2040 running on our own Cortex-M core in its ARMv6-M profile. Firmware is a Thumb ELF talking to the real RP2040 addresses, so the same image runs on real hardware.

The system clock is 125 MHz from reset, because the clock tree is not modeled and there is nothing to bring up.

The pins

The 40-pin header, in the order the board has it.

Left column, pins 1 to 20: GP0, GP1, GND, GP2 to GP5, GNDb, GP6 to GP9, GNDc, GP10 to GP13, GNDd, GP14, GP15.

Right column, pins 40 down to 21: VBUS, VSYS, GNDe, 3V3_EN, 3V3, ADC_VREF, GP28, AGND, GP27, GP26, RUN, GP22, GNDf, GP21, GP20, GP19, GP18, GNDg, GP17, GP16.

The board silkscreens GND on all seven grounds; here they carry letters so each name is unique. The test points and the SWD header are not pins.

This is a 3.3 volt board and its pins are not 5 V tolerant. 3V3 supplies 3.3 V, VBUS supplies 5 V and VSYS supplies 4.7 V, which is 5 V through the board's own Schottky diode.

GP23, GP24 and GP25 exist in the model but are not on the header, as on the real board. GP25 is the on-board LED, which is why LED_BUILTIN is 25.

What is modeled

SIO GPIO, the 64 bit TIMER, UART0, UART1, PWM and the ADC. Every PWM slice drives channel A on an even pin and channel B on an odd one, which is how the on-board LED on GP25 can be faded.

Serial (and Serial1, the same port) is UART0, which is the serial monitor. Serial2 is UART1, as in arduino-pico, and its bytes are on its pins rather than the monitor: TX on GP4 and RX on GP5, or another UART1 pair (GP8 and GP9, GP20 and GP21) chosen with Serial2.setTX() and Serial2.setRX() before begin(). A GPS module, an HC-05 or a second board wired there hears it bit by bit, at the baud rate the divider really gives.

The ADC is the RP2040's twelve-bit converter. GP26, GP27 and GP28 are A0 to A2, and analogRead() on one of them reads the voltage on the net against 3.3 V: ten bits by default, as in arduino-pico, or twelve after analogReadResolution(12). analogReadTemp() reads the on-chip temperature sensor, which sits at 27 C here. The converter has no noise and no error in it, and its FIFO, DMA and interrupt are not modeled.

That is the set pinMode, digitalWrite, digitalRead, analogRead, analogWrite, millis, micros, delay, Serial, Serial2 and attachInterrupt need, at their real addresses.

What is not

A long list, and it is worth reading before you plan a project around one of them: PIO, DMA, SPI, I2C, USB, the RTC, the clock tree, the QSPI flash interface, the second core, the on-chip ROM and the interpolators. The watchdog is modeled: watchdog_enable() and rp2040.wdt_begin() reboot a sketch that stops feeding them.

Their address blocks are unmapped, so touching one is a bus fault rather than a silent zero. That is deliberate: a program that quietly reads nothing is harder to debug than one that stops.

A Pico SDK program is not built in the editor, but its prebuilt .uf2 or .elf runs: the Pico boots it through its boot ROM, with the clocks, I2C, SPI, DMA and USB blocks there as for MicroPython (see Run a prebuilt firmware), and so does PIO there. The second core stops such a program with a sentence saying so.

SPI.h and Wire.h still work, in software on ordinary pins (below), so the missing SPI and I2C blocks matter only to code that writes their registers. A Pico running MicroPython or a prebuilt firmware is the exception: it boots the way silicon does, and the I2C, SPI, DMA and PIO blocks are there for machine.I2C, machine.SPI and rp2.StateMachine (see MicroPython on the Pico).

Two approximations are worth knowing as well. The core runs in 1.024 us chunks rather than one instruction at a time, because a Cortex-M interpreter has no headroom at 125 MHz: timer alarms, PWM edges and UART bytes land on their exact cycle, but a pin change made by software can be up to a microsecond late. And the system clock is always 125 MHz, whatever a sketch programs, because the clock tree is not there.

The full list, and section 8's sorting of what is a data sheet number and what is an approximation, is in the Raspberry Pi Pico model.

Stock Arduino libraries

A tutorial that includes Servo.h, Wire.h, SPI.h, EEPROM.h, LiquidCrystal_I2C.h, Adafruit_GFX.h with Adafruit_SSD1306.h and DHT.h compiles here as it stands. Each is Mokxi's own header under the upstream name, written on the drivers for the parts in the bin, and none of it is the upstream library's code.

  • Wire.begin() puts the I2C bus on GP4 (SDA) and GP5 (SCL).
  • EEPROM.h works, but this board's model has no EEPROM or writable flash, so the bytes live in 4 KB of RAM: they read back within a Run and are gone at a reset.
  • Adafruit_NeoPixel.h drives a WS2812 strip. A real Pico sends the bits from PIO, which is not modeled, so here the core times each one on SysTick, inside the data sheet's windows, with interrupts off for the frame.

The code editor and compiling lists what each one covers and how it differs from the upstream library.

MicroPython

A Pico can run Python instead of a sketch. In the board's Firmware panel, pick MicroPython (main.py): the board's files become a main.py, and Run starts it. What runs is MicroPython v1.29.0, the official Pico firmware from micropython.org, on the simulated RP2040. Run copies your .py files onto the board the way Thonny does, and then the board runs main.py itself. print() goes to the Serial Monitor, and the REPL answers there once main.py ends or you stop it with Ctrl-C.

machine.Pin, machine.PWM, machine.ADC, machine.UART(1), machine.I2C, machine.SPI, SoftI2C and SoftSPI work, so drivers such as micropython-lib's ssd1306.py talk to the parts on the canvas. A traceback marks the line in your file, as a compile error does.

PIO works too: rp2.PIO and rp2.StateMachine run their programs on the simulated PIO blocks, so the WS2812 example from MicroPython's documentation lights a NeoPixel strip. Not simulated: the second core (_thread stops the board with a message), the I2C target mode and the SPI slave mode. The CPU stays at 125 MHz whatever machine.freq() says.

The Pico W runs MicroPython too, with its own build: its CYW43439 radio chip is modeled, so Pin("LED") lights and network.WLAN joins Mokxi's simulated network (see the Pico W). To get started, see MicroPython on the Pico.

CircuitPython

Pick CircuitPython (code.py) in the board's Firmware panel instead and Run boots the official CircuitPython 10.3.1 firmware for the Pico. Your code.py is saved onto the board's CIRCUITPY drive over USB and runs, and the Adafruit libraries it imports go into the drive's lib/ folder by themselves. See CircuitPython on the Pico.

A Pico SDK program

A program built with the Raspberry Pi Pico SDK runs from its build: in the board's Firmware panel choose Upload firmware (.uf2, .elf) and pick the .uf2 or .elf from your build/ folder. The Pico boots it through its boot ROM, as after copying it onto the board's drive, and the Serial Monitor shows UART0 and USB serial both. PIO works; the second core is not simulated. The steps are in Run a prebuilt firmware.

Assembly

The Firmware panel also offers Assembly (no Arduino): a Blink, a Button to LED, a UART hello and a Timer interrupt in ARM Thumb assembly for the Cortex-M0+. The board's files become one main.S, and nothing but that file is linked, so it carries its own vector table: .word _stack_top, then Reset_Handler, then its handlers. The starters take IO_BANK0 and PADS_BANK0 out of RESETS, drive GPIO25 through SIO and time themselves on TIMERAWL. A .S tab beside an ordinary sketch works too. The Pico W has no starters, because its LED is on the radio chip. See Writing assembly.

The examples

Blink, Button, Serial, Fade and Chaser, and the game controller examples: the four Fight stick steps, Fight stick, Leverless controller, Mixbox-style controller and Stop the light.

Elsewhere