🟠 A must-have template for creating a Rust project for microcontrollers.


Step Goal Result
Creating a project Starting with a clean Rust project Creating a `cargo new` structure with `main.rs`
Configuring `.cargo/config.toml` Specifying target and runner (`probe-rs`, `qemu`, etc.) Compiling for STM32 and automatically running the firmware
Creating `rust-toolchain.toml` Lock nightly version and components Unified environment on all machines
Add `memory.x` Detect microcontroller memory map Linking works correctly, firmware is loaded
Add `Embed.toml` Configure `probe-rs` (chip, speed, RTT) You can flash, run, view `defmt` logs
Add dependencies Connect HAL, cortex-m, panic, defmt You can access STM32 peripherals in Rust
Implementation of `#[panic_handler]` Ensure correct handling of `panic!` Error-free compilation, correct debugging
Definition of `#[entry] fn main()` Start point of the program Code runs after reset of the microcontroller
Configuring `RTT / semihosting` Add text output for debugging Messages can be printed via `defmt::info!`
Interrupt handling Add reactions to timers, UART, external events Interrupts work, you can react to events
Compilation and firmware Checking the entire pipeline Working STM32 firmware in Rust

📌 1. Project Setup

✅ Project Creation

cargo new --bin my_project
cd my_project

✅ Add target and toolchain

Create file rust-toolchain.toml:

[toolchain]
channel = "nightly-2022-06-28"   # or stable channel = "1.86"
components = ["rust-src", "rustfmt"]
targets = ["thumbv6m-none-eabi"]

Create file .cargo/config.toml:

[build]
target = "thumbv6m-none-eabi"

[target.thumbv6m-none-eabi]
rustflags = [
  "-C", "link-arg=--nmagic",
  "-C", "link-arg=-Tlink.x",
  "-C", "link-arg=-Tdefmt.x",
  "-C", "no-vectorize-loops",
]
runner = "elf2uf2-rs -d"   # или другой, см. ниже
QEMU Runner
runner = """
  qemu-system-arm \
  -cpu cortex-m3 \
  -machine lm3s6965evb \
  -nographic \
  -semihosting-config enable=on,target=native
"""
probe-rs Runner
runner = "probe-rs run"
🧩 Embed.toml configuration

To run via probe-rs Create Embed.toml file in the project root:

[default]
chip = "nrf52840"   # or other: stm32f103c8, rp2040, atsamd21g18, etc.
probe = "Auto"      # you can specify a specific one (by serial or by name)
speed = 1000        # SWD/JTAG frequency in kHz

[default.rtt]
enabled = true
channels = [{ name = "defmt", up = true, down = false }]
cargo run — flashing and launching
cargo embed — flashing only
cargo embed --reset-halt — stop at startup
cargo embed --list-probes — list of available programmers
cargo embed --chip rp2040 — specify the chip manually

✅ Linking and Memory

create file memory.x file in the project root

MEMORY
{
  FLASH : ORIGIN = 0x08000000, LENGTH = 256K
  RAM   : ORIGIN = 0x20000000, LENGTH = 64K
}

Used by the linker (link.x) to determine the address space of the chip.


📌 2. Panic Handler

Without defining #[panic_handler] the project will not compile.

use core::panic::PanicInfo;

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}

📌 3. Main Function

add dependencies:

cargo add cortex-m --features inline-asm
cargo add cortex-m-rt
use cortex_m_rt::entry;

#[entry]
fn main() -> ! {
    loop {}
}

📌 4. Text output: Semihosting

To output messages to the host (usually via QEMU or debug probe):

cargo add cortex-m-semihosting
use cortex_m_rt::entry;
use cortex_m_semihosting::hprintln;

#[entry]
fn main() -> ! {
    hprintln!("Start program").unwrap();
    loop {}
}

Semihosting is slow, but convenient for debugging. Works only with debugger/QEMU support.


📌 5. Interrupts / Exceptions (e.g. SysTick)

use cortex_m_rt::exception;
use cortex_m_semihosting::hprintln;

#[exception]
fn SysTick() {
    hprintln!("SysTick interrupt").ok();
}

🔄 Summary of steps

  1. Initialize project: cargo new, configure .cargo/config.toml, memory.x, rust-toolchain.toml
  2. Add dependencies: cortex-m, cortex-m-rt, cortex-m-semihosting
  3. Define:
  • #[entry]
  • #[panic_handler]
  • #[exception]
  1. Configure runner: depends on environment (probe-rs, QEMU, elf2uf2, etc.)
  2. Compile: cargo build, cargo run (if runner is configured)