Basic Embedded Rust Flow (Cortex-M)
🟠 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
- Initialize project:
cargo new
, configure.cargo/config.toml
,memory.x
,rust-toolchain.toml
- Add dependencies:
cortex-m
,cortex-m-rt
,cortex-m-semihosting
- Define:
#[entry]
#[panic_handler]
#[exception]
- Configure runner: depends on environment (
probe-rs
,QEMU
,elf2uf2
, etc.) - Compile:
cargo build
,cargo run
(if runner is configured)
Read other posts?