Embedded Fundamentals
What Is an Embedded System?
An embedded system is a computer designed to perform a dedicated function within a larger system, typically with real-time constraints and limited resources. Unlike general-purpose computers, embedded systems are optimized for specific tasks with strict requirements on power, size, cost, and reliability.
Key Characteristics
| Property | Embedded System | General-Purpose Computer | |---|---|---| | Purpose | Single/few dedicated tasks | Broad, user-defined tasks | | OS | Bare-metal or RTOS | Full OS (Linux, Windows) | | Resources | KB-MB RAM, KB-MB flash | GB RAM, TB storage | | Power | uW to low W | Tens to hundreds of W | | Boot time | Milliseconds | Seconds to minutes | | Reliability | Must run indefinitely | Acceptable to restart |
Microcontrollers vs Microprocessors
Microcontroller (MCU)
A self-contained system-on-chip with CPU, memory, and peripherals integrated:
- CPU core (ARM Cortex-M, RISC-V, AVR, etc.)
- Flash memory (program storage, typically 16 KB - 2 MB)
- SRAM (data memory, typically 2 KB - 512 KB)
- Built-in peripherals: GPIO, UART, SPI, I2C, ADC, timers
- Clock generation (internal oscillators, PLL)
Examples: STM32F4, ESP32, ATmega328P, nRF52840.
Microprocessor (MPU)
A CPU that requires external memory and peripherals:
- Higher clock speeds (hundreds of MHz to GHz)
- MMU for virtual memory support
- Typically runs Linux or another full OS
- Requires external RAM (DDR3/4), storage (eMMC, SD)
Examples: Broadcom BCM2711 (Raspberry Pi 4), i.MX8, AM335x.
System-on-Chip (SoC)
An SoC integrates a microprocessor with peripherals, GPU, DSP, and connectivity on one die. Modern SoCs blur the MCU/MPU line -- the ESP32 is technically an SoC with dual cores, Wi-Fi, and Bluetooth.
Popular Development Boards
Arduino (ATmega328P / ARM-based)
- Beginner-friendly, massive ecosystem
- Arduino framework abstracts hardware details
- Limited for production use, great for prototyping
STM32 (ARM Cortex-M)
- Professional-grade, wide range (M0 to M7)
- STM32CubeIDE, HAL libraries
- Strong Rust support via
stm32-haland PAC crates - Nucleo and Discovery boards for development
ESP32 (Xtensa / RISC-V)
- Built-in Wi-Fi and Bluetooth
- Dual-core, up to 240 MHz
- ESP-IDF framework, also supports Arduino and Rust
- Ideal for IoT applications
Raspberry Pi Pico (RP2040)
- Dual-core ARM Cortex-M0+ at 133 MHz
- 264 KB SRAM, 2 MB flash
- Programmable I/O (PIO) state machines
- Excellent Rust support via
embassy-rp
Cross-Compilation
Embedded targets differ from the host machine architecture. Cross-compilation builds code on a host (x86_64) for a target (ARM Cortex-M).
Rust Cross-Compilation Toolchain
# Install the target for ARM Cortex-M4F (e.g., STM32F4)
rustup target add thumbv7em-none-eabihf
# Build for the target
cargo build --target thumbv7em-none-eabihf --release
# Flash to the board (using probe-rs)
cargo flash --target thumbv7em-none-eabihf --release --chip STM32F411CEUx
Common Rust embedded targets:
| Target Triple | Architecture | FPU |
|---|---|---|
| thumbv6m-none-eabi | Cortex-M0/M0+ | No |
| thumbv7m-none-eabi | Cortex-M3 | No |
| thumbv7em-none-eabi | Cortex-M4/M7 | No (soft float) |
| thumbv7em-none-eabihf | Cortex-M4F/M7F | Yes (hard float) |
| thumbv8m.main-none-eabihf | Cortex-M33 | Yes |
Bare-Metal Programming
Bare-metal means running code directly on hardware without an operating system. The program has full control of the hardware and is responsible for all initialization.
Minimal Bare-Metal Rust Program
// No standard library -- bare-metal environment
// No standard entry point
ENTRY PROCEDURE MAIN() // never returns
// Hardware initialization here
LOOP FOREVER
// Main application loop -- must never return
The no_std Environment
When #![no_std] is declared, the Rust standard library (std) is unavailable. You get:
core-- language primitives, iterators,Option,Result, traits, mathalloc(optional) -- heap allocation (Vec,Box,String) if you provide an allocator
What you lose without std:
- File I/O, networking, threads,
println! HashMap(requiresalloc+ a hasher)- The default panic handler (must provide your own)
Essential Embedded Rust Crates
[dependencies]
cortex-m = "0.7" # Low-level Cortex-M access
cortex-m-rt = "0.7" # Runtime (startup code, vector table)
panic-halt = "0.2" # Panic handler that halts
embedded-hal = "1.0" # Hardware abstraction traits
defmt = "0.3" # Efficient logging for embedded
defmt-rtt = "0.4" # RTT transport for defmt
# Async embedded framework
embassy-executor = "0.7" # Async executor
embassy-time = "0.4" # Async timers and delays
embassy-stm32 = "0.3" # STM32 HAL with async support
Development Workflow
Write Code (Rust / C)
|
Cross-Compile (cargo build --target ...)
|
Flash to MCU (probe-rs / OpenOCD / ST-Link)
|
Debug (GDB + probe-rs / JTAG / SWD)
|
Monitor Output (RTT / UART / defmt)
Flashing and Debugging with probe-rs
# Flash firmware
probe-rs run --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/app
# Attach GDB for debugging
probe-rs gdb --chip STM32F411CEUx &
arm-none-eabi-gdb -ex "target remote :1337" target/thumbv7em-none-eabihf/release/app
Memory Constraints and Optimization
Embedded systems require careful memory management:
// Stack-allocated fixed-size buffer (no heap needed)
buffer ← array of 64 zero bytes
// Use fixed-capacity collections instead of dynamic allocation
data ← FIXED_VEC(max_capacity ← 32) // Max 32 elements, stack-allocated
PUSH(data, 42)
// Use fixed-capacity string instead of heap-allocated string
s ← FIXED_STRING(max_capacity ← 64)
FORMAT_WRITE(s, "temp: {}C", 23)
Build Size Optimization
# Cargo.toml
[profile.release]
opt-level = "z" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Strip debug symbols
Key Takeaways
- Embedded systems trade generality for efficiency, reliability, and determinism.
- Microcontrollers integrate CPU, memory, and peripherals; microprocessors need external components.
- Rust's
no_stdenvironment provides memory safety on bare metal without runtime overhead. - Cross-compilation, flashing, and on-target debugging form the core development loop.
- Every byte of RAM and flash matters -- use stack allocation and fixed-size data structures.