Microcontroller projects

RISC-V with Raspberry Pi Pico 2

last updated: 2025-10-17

Intro

The latest political signals from China (pact with dictators Putin and Kim Jong-un) lead me to believe that it is time to boycott as far as possible Chinese microcontrollers. America is no longer the friend to Europe, with M. Trump as president. American Tech Giants get too greedy.

It is time to change. Linux as OS and RISC-V for our microcontroller. And as far as possible no more chinese or american products.

The new Raspberry Pi Pico 2 processor was designed in Europe and has beneath ARM cores 2 RISC-V (Hazard3, RV32IMAC+) cores. So let's use them.

The hard way install for C/C++ (and Assembler)

VS Code has a cool addon to use the PICO2 but we get Microsoft involved. If you need to use it look in this document: https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

But let's try the hard way. We use a Debian based Linux distro (Kubuntu 25.04). More Infos can be found in Appendix C: Manual toolchain setup of the document above.

Installing the SDK

The SDK (Software Development Kit) provides the headers, libraries and build system needed to write assembler and C/C++ programs. As everything coming from the Raspberry Pi Foundation, the SDK is well documented: https://datasheets.raspberrypi.com/pico/raspberry-pi-pico-c-sdk.pdf.

We will use the latest SDK version 2.2 (https://github.com/raspberrypi/pico-sdk). First we create a directory called pi_pico2 in our home directory and clone the git repos to it and we download some tools needed. To build projects we also need CMake, a cross-platform tool used to build the software, and the ARM GNU Toolchain (for a first test).

mkdir ~/pi_pico2
cd ~/pi_pico2
git clone --recurse-submodules https://github.com/raspberrypi/pico-sdk.git
git clone https://github.com/raspberrypi/pico-examples.git
git clone https://github.com/raspberrypi/pico-extras.git
git clone https://github.com/raspberrypi/pico-playground.git
git clone https://github.com/raspberrypi/picotool.git
sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
sudo apt install g++ libstdc++-arm-none-eabi-newlib libusb-1.0-0-dev

Fixing the environment variables

Your bash session has to find the files, so we create a shell script with the necessary environment variables:

PICODIR=pi_pico2
PICO_SDK_PATH=$HOME/$PICODIR/pico-sdk
PICO_EXAMPLES_PATH=$HOME/$PICODIR/pico-examples
PICO_EXTRAS_PATH=$HOME/$PICODIR/pico-extras
PICO_PLAYGROUND_PATH=$HOME/$PICODIR/pico-playground
PICO_TINYUSB_PATH=$HOME/$PICODIR/pico-sdk/lib/tinyusb

export PICO_SDK_PATH PICO_EXAMPLES_PATH PICO_EXTRAS_PATH
export PICO_PLAYGROUND_PATH PICO_TINYUSB_PATH
export PICO_TOOLCHAIN_PATH PICO_MYLIB_PATH

Create the pico2envvars.sh file with nano:

cd ~/pi_pico2
nano pico2envvars.sh

Copy the text to the file. Save and exit (Ctrl+o, Ctrl+x).

Now we need to source the file in ~/.basrc. Add the following line to your ~/.basrc: source ~/pi_pico2/pico2envvars.sh.

Now if you open a new terminal window the environment variables are known by the system and we don't need to be root to work with the files.

Let`s blink the LED (ARM)

Ok I'm eager to test this. Let'do blink the LED. We change the examples directory and create a directory named build in this directory and enter the directory. After this we use cmake -DPICO_BOARD=pico2 ... CMake is a powerful open-source solution for managing the software build process. It gives us control of the software compilation process using simple independent configuration files named here CMakeLists.txt. The parameter is needed because the default would be a normal pico (RP2040) instead a pico2 (RP2340). You can also use pico2_w for a pico2 with Wifi. The double dot uses the CMakeLists.txt file from the parent directory (pico-examples).

Now the build directory is populated with example directories containing a Makefile. We we change to the folder of the blink example (blink) and use make to create an executable file from the source files ('blink.c'). make needs the makefile (Makefile) created by cmake. The parameter -j4 tells to use 4 cores to do the job, If your PC has 16 cores set it to -j16 :).

cd ~/pi_pico2/pico-examples
mkdir build
cd build
cmake -DPICO_BOARD=pico2 ..
cd blink
make -j4

Cool now we have a blink.hex file and an blink.uf2 file in our blink directory. Power the pico2 while holding the BOOTSEL button. The pico2 will appear as an USB drive on your PC. Copy the blink.uf2 file to the Pico2 and the LED should blink :).

Yes the LED was blinking, but some things were not as wished.

Building and installing picotool

Picotool is a command-line utility for:

We find picotool on github: https://github.com/raspberrypi/picotool.

And more information is in the Appendix B of the Getting starting document.

We will install picotool the existing in a seperate subdirectory of our ~/pi_pico2 directory.

cd ~/pi_pico2/picotool
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$HOME/pi_pico2/picotool_install -DPICOTOOL_FLAT_INSTALL=1 ../
make -j16
make install

We need to add some lines to our path script:

cd ~/pi_pico2
nano pico2envvars.sh

Add the following lines:

PICO_PICOTOOL=$HOME/$PICODIR/picotool_install/picotool
...
PATH="$PICO_PICOTOOL:$PATH"
export PATH

We don't want to be root while using picotool, so we need to create an udev rule in /etc/udev/rules.d:

cd /etc/udev/rules.d
nano 14-all_pico.rules

Copy the following lines to the file and save and exit (Ctrl+o, Ctrl+x):

# Raspberry Pi Pico (RP2040) in BOOTSEL mode (for flashing)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0003", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico in normal mode (serial port)
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000a", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico W (wireless)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0005", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0005", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico 2 (RP2350)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000c", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico 2 W (RP2350 + wireless)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000d", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000d", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico Probe (CMSIS-DAP)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", MODE="0666", GROUP="plugdev", TAG+="uaccess"
SUBSYSTEM=="tty", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0004", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Pico Probe (picotool/picoprobe, USB only)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="0009", MODE="0666", GROUP="plugdev", TAG+="uaccess"
# Raspberry Pi Debug Probe (USB only)
SUBSYSTEM=="usb", ATTRS{idVendor}=="2e8a", ATTRS{idProduct}=="000f", MODE="0666", GROUP="plugdev", TAG+="uaccess"

After adding this execute:

sudo udevadm control --reload-rules

or reboot.

OK let's test. Poer the pico2 while holding the bootsel button an fire this command:

picotool info -a

We get a bunch of information:

Program Information
 name:                   blink
 binary start:           0x10000000
 binary end:             0x10001864
 target chip:            RP2350
 image type:             RISC-V
...
Fixed Pin Information
...
Build Information
...
Metadata Block 1
...
Metadata Block 2
...
Device Information
 chipid:                 0xca5488672658bf77
 current cpu:            ARM
 available cpus:         ARM, RISC-V
 default cpu:            ARM
 ...
 debug enable:           1
 ...

And we see we used the ARM cpu!

Installing the RISC-V toolchain

We need a 32 bit toolchain for the Hazard3 core on the Raspberry Pi Pico 2 (RP2350).

The ISA supported by Hazard3 is rv32imac. The easiest way is to use a pre-built toolchain from the jenkins server of embecosm.com. We download it in our own directory and extract the file:

cd ~/pi_pico2
mkdir risc_v_gcc
cd risc_v_gcc
wget https://buildbot.embecosm.com/job/corev-gcc-ubuntu2204/83/artifact/corev-openhw-gcc-ubuntu2204-20250302.tar.gz
tar -xzf corev-openhw-gcc-ubuntu2204-20250302.tar.gz

Again we need to add a directory (bin) to our PATH.

Add the following lines to the end of the pico2envvars.sh script:

PATH="$HOME/$PICODIR/risc_v_gcc/corev-openhw-gcc-ubuntu2204-20250302/bin:$PATH"
export PATH

Open a new terminal and test with:

riscv32-corev-elf-gcc --version

Let`s blink the LED (RISC-V)

I was cool to use the blink example, but the C code is big (because it is also valid for a Pico2 W, that uses another pin for the LED).

Let's try with our own minimal code:

/* blink.c: blink onboard LED using RISC-V core on Pico2 (not W!!)
   weigu.lu */

#include "pico/stdlib.h"

#define LED_DELAY_MS 100

int main() {
    gpio_init(PICO_DEFAULT_LED_PIN);
    gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
    while (true) {
        gpio_put(PICO_DEFAULT_LED_PIN, 1);
        sleep_ms(LED_DELAY_MS);
        gpio_put(PICO_DEFAULT_LED_PIN, 0);
        sleep_ms(LED_DELAY_MS);
    }
}
cd
mkdir -p ~/pi_pico2/my_pico2_risc_v_examples/blink/src
cd ~/pi_pico2/my_pico2_risc_v_examples/blink/src
nano blink.c

Copy and paste the code and save the file.

Next we create a CMakeLists.txt file for cmake:

cd ~/pi_pico2/my_pico2_risc_v_examples/blink
nano CMakeLists.txt
cmake_minimum_required(VERSION 3.13)

set(PICO_BOARD "pico2")
set(PICO_PLATFORM "rp2350-riscv")

# Include the SDK (must be before project)
include($ENV{PICO_SDK_PATH}/external/pico_sdk_import.cmake)
include($ENV{PICO_EXTRAS_PATH}/external/pico_extras_import.cmake)

set(CMAKE_C_STANDARD 11)  # Best practice
set(CMAKE_CXX_STANDARD 17)
project(blink)            # Project name
pico_sdk_init()           # Initialize the SDK
set(Source_Files ../src/blink.c)
add_executable(${CMAKE_PROJECT_NAME} ${Source_Files})

target_link_libraries(${CMAKE_PROJECT_NAME} # Link libraries
    pico_stdlib
    hardware_gpio
)

pico_add_extra_outputs(${CMAKE_PROJECT_NAME}) # Generate uf2, bin, hex, dis

add_compile_options( # Compiler flags
    -Wall
    -Wno-format
    -Wno-unused-function
)
if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
    add_compile_options(-Wno-maybe-uninitialized)
endif()


set_directory_properties(PROPERTIES # Clean up extra files with 'make clean'
    ADDITIONAL_MAKE_CLEAN_FILES
        "${CMAKE_PROJECT_NAME}.uf2"
        "${CMAKE_PROJECT_NAME}.dis"
        "${CMAKE_PROJECT_NAME}.bin"
        "${CMAKE_PROJECT_NAME}.hex"
        "${CMAKE_PROJECT_NAME}.elf"
)

pico_enable_stdio_usb(${CMAKE_PROJECT_NAME} 1) # Enable stdio (1 enables USB or UART)
pico_enable_stdio_uart(${CMAKE_PROJECT_NAME} 0)

add_custom_target(upload  # Flash target
    COMMAND picotool load -v -f ${CMAKE_PROJECT_NAME}.uf2
    COMMAND picotool reboot
    DEPENDS ${CMAKE_PROJECT_NAME}
    COMMENT "Flashing ${CMAKE_PROJECT_NAME}.uf2 to Pico 2 using picotool"
)

Important for our goal are these two lines:

set(PICO_BOARD "pico2")
set(PICO_PLATFORM "rp2350-riscv")

Finally we use cmake to get a Makefile and make to create our blink program and directly upload it to the pico2 (must be powered with bootsel button pressed):

mkdir -p ~/pi_pico2/my_pico2_risc_v_examples/blink/build
cd ~/pi_pico2/my_pico2_risc_v_examples/blink/build
cmake ../
make -j16
make upload

Check that it actually built for RISC-V with:

file blink.elf

The output should start with:

blink.elf: ELF 32-bit LSB executable, UCB RISC-V, RVC, soft-float ABI ...

No more bootsel button

We will use openOCD with a Debugprobe to program the Pico2.

I have a RP2350-Geek debug probe but you can also use another Pico as debug probe.

Circuit

The Debug probe GP2 (blue) is SWD interface CLK GP3 (green or red) is SWD interface DIO. Don't forget GND.

More infoin Appendix A Debugprobe in Getting started with Pico.

We install openocd with

sudo apt install openocd
cd ~/pi_pico2/my_pico2_risc_v_examples/blink/build
openocd -f interface/cmsis-dap.cfg -f target/rp2350-riscv.cfg -c "adapter speed 5000" \
-c "program blink.elf verify reset exit"

Downloads

Interesting links

http://blog.wolfman.com/articles/2025/5/19/bare-metal-gpio-twiddling-for-risc-v-on-rpi-pico2

https://github.com/wolfmanjm/RISC-V-RP2350-baremetal

https://mcyoung.xyz/2021/11/29/assembly-1/

https://smist08.wordpress.com/2024/10/21/risc-v-on-the-raspberry-pi-pico-2/