last updated: 2025-10-17
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.
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.
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
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.
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.
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
-DCMAKE_INSTALL_PREFIX=$HOME/pi_pico2/picotool_install
sets the installation directory for the built files to $HOME/pipico2/picotoolinst. This means all binaries, libraries, and other files will be installed in this folder, not the default system location.-DPICOTOOL_FLAT_INSTALL=1
enables a "flat" installation, meaning all files (binaries, libraries, etc.) are placed directly in the picotool_inst directory, without subdirectories like bin/, lib/, etc.../
specifies the source directory (one level up from the current directory), where the CMakeLists.txt file for picotool is located.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!
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
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 ...
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.
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"
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/