Skip to content

Rust programming for rp2040

Widely inspired from https://tutoduino.fr/en/tutorials/programing-in-rust-the-xiao-rp2040-board/ (with some modification about install and build)

Install

Rust

  • install Rust : curl --proto '=https' --tlsv1.3 https://sh.rustup.rs -sSf | sh
  • add to PATH : source $HOME/.cargo/env
  • check it works : rustc --version
  • Hello world : rustc hw.rs; ./hw

Rust for embedded Rust

  • install build-essential and libudev-dev (if not already) : sudo apt update && sudo apt upgrade; sudo apt install build-essential libudev-dev;
  • Prepare your Rust environment setup :
rustup target add thumbv6m-none-eabi
cargo install cargo-binutils
rustup component add llvm-tools-preview
  • install elf to UF2 converter : cargo install elf2uf2-rs

Hello World!

  • git clone https://github.com/rp-rs/rp-hal
  • cd rp-hal
  • cargo build --features="critical-section-impl" --example blinky
  • Put your RP2040 in boot mode (keep boot button pressed while pushing and releasing reset button)
  • Once mounted, run : cargo run --features="critical-section-impl" --example blinky

Write your own code examples

  • cargo new 01-blink
  • Edit Cargo.toml :
    [package]
    name = "xiao_rp2040_rs"
    version = "0.1.0"
    edition = "2021"
    description = "XIAO RP2040 RGB LED Blinking"
    authors = [
        "Stephane POTIER <tutoduino@gmx.fr>",
        "Thomas POTIER <d34db4b3@protonmail.com>",
    ]
    homepage = "https://tutoduino.fr/en/tutorials/programing-in-rust-the-xiao-rp2040-board/"
    license = "MIT OR Apache-2.0"
    repository = "https://github.com/rp-rs/rp-hal.git"
    [dependencies]
    cortex-m = "0.7.6"
    seeeduino-xiao-rp2040 = "0.2.0"
    panic-halt = "0.2.0"
    embedded-hal = "0.2.7"
    cortex-m-rt = "0.7"
    rp2040-boot2 = "0.2.0"
    ws2812-pio = "0.4.0"
    smart-leds = "0.3.0"
    
  • create memory.x in same folder :
    MEMORY {
        BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
        FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
        RAM   : ORIGIN = 0x20000000, LENGTH = 256K
    }
    
    EXTERN(BOOT2_FIRMWARE)
    
    SECTIONS {
        /* ### Boot loader */
        .boot2 ORIGIN(BOOT2) :
        {
            KEEP(*(.boot2));
        } > BOOT2
    } INSERT BEFORE .text;
    
  • Create new directory .cargo with new config.toml in it :
    # set default build target for rp2040
    [build]
    target = "thumbv6m-none-eabi"
    # upload binary to rp2040 instead of running on host
    [target.thumbv6m-none-eabi]
    runner = "elf2uf2-rs -d"
    # use appropriate memory layout
    rustflags = ["-C", "link-arg=-Tlink.x"]
    
  • in main directory, create your main file main.rs:
    //! XIAO RP2040 Blinking LED Example
    //!
    //! https://tutoduino.fr/
    //!
    //! Blinks the LED on a Seeed Studio XIAO RP2040 board.
    //!
    #![no_std]
    #![no_main]
    use embedded_hal::digital::v2::OutputPin;
    use panic_halt as _;
    use seeeduino_xiao_rp2040::entry;
    use seeeduino_xiao_rp2040::hal;
    use seeeduino_xiao_rp2040::hal::pac;
    use seeeduino_xiao_rp2040::hal::prelude::*;
    /// Entry point to our bare-metal application.
    ///
    /// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
    /// as soon as all global variables are initialised.
    ///
    /// The function configures the RP2040 peripherals, then blinks the LED in an
    /// infinite loop.
    #[entry]
    fn main() -> ! {
        // Grab our singleton objects
        let mut pac = pac::Peripherals::take().unwrap();
        let core = pac::CorePeripherals::take().unwrap();
        // Set up the watchdog driver - needed by the clock setup code
        let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
        // Configure the clocks
        //
        // The default is to generate a 125 MHz system clock
        let clocks = hal::clocks::init_clocks_and_plls(
            seeeduino_xiao_rp2040::XOSC_CRYSTAL_FREQ,
            pac.XOSC,
            pac.CLOCKS,
            pac.PLL_SYS,
            pac.PLL_USB,
            &mut pac.RESETS,
            &mut watchdog,
        )
        .ok()
        .unwrap();
        // The single-cycle I/O block controls our GPIO pins
        let sio = hal::Sio::new(pac.SIO);
        // Set the pins up according to their function on this particular board
        let pins = seeeduino_xiao_rp2040::Pins::new(
            pac.IO_BANK0,
            pac.PADS_BANK0,
            sio.gpio_bank0,
            &mut pac.RESETS,
        );
        // The delay object lets us wait for specified amounts of time (in
        // milliseconds)
        let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());
        // Configure the pins to operate as a push-pull output
        let mut led_blue_pin = pins.led_blue.into_push_pull_output();
        let mut led_green_pin = pins.led_green.into_push_pull_output();
        let mut led_red_pin = pins.led_red.into_push_pull_output();
        // Set green and red led OFF
        led_green_pin.set_high().unwrap();
        led_red_pin.set_high().unwrap();
        loop {
            // Turn on blue LED for 1s
            led_blue_pin.set_low().unwrap();
            delay.delay_ms(1000);
            // Turn off blue LED for 500ms
            led_blue_pin.set_high().unwrap();
            delay.delay_ms(500);
        }
    }
    
  • Set your board in boot mode and launch cargo run --release
  • The blue LED of the XIAO should now blink
  • see https://github.com/tutoduino/xiao_rp2040_rs/tree/main