Embedded Basics
10 examples introducing no_std, cross targets, and the embedded Rust toolchain.
Prerequisites
- Rust 1.97.0 with embedded targets
cargo install probe-rs-toolsfor flashing and debug
rustup target add thumbv7em-none-eabihfBasic Examples
1. Embedded Target Triple
rustup target add thumbv7em-none-eabihf # Cortex-M4F
rustup target add thumbv6m-none-eabi # Cortex-M0+- Triple encodes CPU, OS (none), and ABI
- Match triple to your microcontroller datasheet
eabihfmeans hardware floating point- Set
targetin.cargo/config.toml
2. .cargo/config.toml
[build]
target = "thumbv7em-none-eabihf"
[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F429ZITx"- Default target avoids passing
--targetevery build runnerauto-flashes oncargo run- Chip name must match
probe-rsdatabase - Commit config per board crate
3. Memory Layout (memory.x)
MEMORY
{
FLASH : ORIGIN = 0x08000000, LENGTH = 2048K
RAM : ORIGIN = 0x20000000, LENGTH = 192K
}- Linker script defines flash and RAM regions
cortex-m-rtcrate expectsmemory.x- Wrong lengths cause link failures or silent corruption
- Copy from chip HAL examples
4. Minimal no_std Crate
#![no_std]
#![no_main]
use panic_halt as _;
#[no_mangle]
pub extern "C" fn main() -> ! {
loop {}
}#![no_std]removes the standard library#![no_main]replaces Rust's main with embedded entrypanic_haltstops on panic (replace with better handler in prod)- Real apps use HAL init from chip crate
5. defmt Logging
use defmt::info;
info!("system started");
info!("tick {}", 42);- Structured logging over RTT or serial
- Lower overhead than
println!on embedded - Requires
defmtfeature on dependencies - Decode logs with
defmt-printon host
Related: Debugging Firmware
6. HAL Initialization Pattern
let dp = pac::Peripherals::take().unwrap();
let rcc = dp.RCC.constrain();
let clocks = rcc.cfgr.freeze();
let gpioa = dp.GPIOA.split();pac= peripheral access crate (regs)hal= safe wrappers overpactake()enforces singleton peripherals- Pattern varies by
stm32*/rp2040/nrfHAL
7. Cargo Features for Boards
[features]
default = ["stm32f429"]
stm32f429 = ["stm32f4xx-hal/stm32f429"]- One crate can support multiple boards via features
- HAL crates gate chip support behind features
- CI should build all supported features
- Document feature matrix in README
Intermediate Examples
8. probe-rs Flash and Debug
probe-rs list
probe-rs run --chip STM32F429ZITx target/thumbv7em-none-eabihf/debug/app- Replaces OpenOCD + GDB for many workflows
cargo embedconfig uses probe-rs under the hood- Supports RTT and breakpoints
- Works with CMSIS-DAP and ST-Link probes
9. Embassy Async Firmware
#[embassy_executor::main]
async fn main(_spawner: Spawner) {
let p = embassy_stm32::init(Default::default());
// async tasks...
}- Embassy brings async/await to embedded without an OS
- Executor drives tasks cooperatively
- Requires
embassyecosystem crates - See async embedded page for full patterns
Related: Async on Embedded (Embassy)
10. Project Layout
firmware/
.cargo/config.toml
memory.x
build.rs # copy memory.x
src/
main.rs
Cargo.toml # hal + panic + defmt
build.rslinksmemory.xviacortex-m-rt- Keep board support in a separate crate for reuse
- Pin dependency versions for reproducible firmware
- Use
cargo binutilsfor size analysis (cargo size)
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.