The RP2040-Zero is a fantastic piece of hardware. These little boards pack the dual-core RP2040 MCU into an incredibly small and affordable package, making them ideal for any small electronics project where space is critical.
If you’re looking to pair this tiny hardware with the safety and performance of Rust, you’re in luck! The RP2040 has excellent support in the embedded Rust ecosystem, but there are a few key dependencies and setup steps we need to cover before you can start coding.
Setting Up the Rust Toolchain
First, we need to ensure we have the correct cross-compilation tools and some essential Cargo extensions.
The RP2040 is built around the Cortex-M0+ architecture, which means we need the thumbv6m-none-eabi target. We’ll also install two crucial utilities: flip-link (for zero-cost stack overflow protection) and elf2uf2-rs (for flashing our compiled binary).
Run the following commands in your shell:
sudo apt-get install -y libudev-dev
rustup target add thumbv6m-none-eabi
cargo install flip-link
cargo install elf2uf2-rs
Creating Your Project
The fastest way to get your project scaffolding set up is by using the community-maintained rp2040-project-template. This template handles all the boilerplate for you.
First, install the cargo-generate utility and clone the template repository:
cargo install cargo-generate
git clone https://github.com/rp-rs/rp2040-project-template.git
Now, generate your new project by running the command and following the prompts:
cargo generate rp-rs/rp2040-project-template
When the template prompts you for the flashing method, we have to account for the RP2040-Zero’s biggest limitation: it lacks a dedicated debug port. This means we cannot use standard debugging tools like probe-rs.
Instead, we use the lightweight elf2uf2-rs loader we installed earlier to flash the device over USB. When asked, choose the custom method and enter the following run command:
elf2uf2-rs -d
Note: The template uses the Raspberry Pi Pico BSP, which works perfectly with the RP2040-Zero since they share the same core chip. If you prefer, you can modify the project to use the raw rp2040-hal directly.
Building and Running the Project
Once everything is set up, you can just build your project with Cargo:
cargo build
For running it on the hardware, you first need to bring the device into USB boot mode. Do this by holding the BOOT button pressed while briefly tapping the RESET button.
Since we configured the project to use elf2uf2-rs -d as the custom run command, you can now flash the device directly using:
cargo run
The Debugging Workaround: UART Logging
Without a debug port, you need a different way to view logging messages. We will rely on the reliable workhorse of embedded development: UART (Universal Asynchronous Receiver-Transmitter).
This setup requires you to connect a USB-to-UART serial adapter to your computer and two specific GPIO pins on the RP2040-Zero.
Add the necessary use clauses to access formatting and the HAL’s UART peripheral:
use core::fmt::Write;
use bsp::hal::{
uart,
fugit::RateExtU32,
};
Inside your main function (or initialization routine), you can configure UART0 on GPIO 0 (TX) and GPIO 1 (RX) with a standard baud rate of 115200:
let uart_pins = (
pins.gpio0.into_function(), // tx
pins.gpio1.into_function(), // rx
);
let mut uart = uart::UartPeripheral::new(pac.UART0, uart_pins, &mut pac.RESETS)
.enable(
uart::UartConfig::new(115200.Hz(), uart::DataBits::Eight, None, uart::StopBits::One),
clocks.peripheral_clock.freq(),
)
.unwrap();
Finally, you can write your first log message to the UART port:
let _ = uart.write_fmt(format_args!("Hello"));
Final Notes
It might be inconvenient to write to the UART from different functions, as the uart object is needed and the Rust Borrow Checker might make your life difficult. That could be a problem for larger projects that must be kept in mind.
For deeper reading on embedded Rust concepts, I highly recommend checking out The Embedded Rust Book.
Konstantin Schauwecker