### Building the Embedded Rust Example (Cargo) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This command builds the 'hello' example for an embedded target. The output binary will be placed in the standard `target` directory, ready for execution. ```bash cargo build --bin hello ``` -------------------------------- ### Example Output of `cargo run --example hello` Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This shows the typical output when running an embedded Rust example named 'hello' using `cargo run`. It includes details about section loading and memory addresses, followed by a GDB prompt, indicating that the application has been loaded and is ready for debugging. ```text $ cargo run --example hello (..) Loading section .vector_table, size 0x400 lma 0x8000000 Loading section .text, size 0x1e70 lma 0x8000400 Loading section .rodata, size 0x61c lma 0x8002270 Start address 0x800144e, load size 10380 Transfer rate: 17 KB/sec, 3460 bytes/write. (gdb) ``` -------------------------------- ### Running Embedded Example with Cargo Run (Release) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md After configuring the custom runner, this command compiles and runs the 'hello' example in release mode using `cargo run`. The runner automatically executes the compiled binary on QEMU. ```bash cargo run --example hello --release ``` -------------------------------- ### Starting GDB and Connecting to OpenOCD Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This snippet demonstrates how to start GDB with the correct target and connect it to the running OpenOCD instance. It shows the command to launch GDB and the subsequent command to establish a remote debugging connection. ```console gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello (gdb) target remote :3333 Remote debugging using :3333 0x00000000 in ?? () ``` -------------------------------- ### Modify Example for Hardware Execution Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md Adjusts the `examples/hello.rs` file by commenting out the `debug::exit()` call. This call is specific to QEMU and should be removed or commented out when running on actual hardware. ```rust #[entry] fn main() -> ! { hprintln!("Hello, world!").unwrap(); // exit QEMU // NOTE do not run this on hardware; it can corrupt OpenOCD state // debug::exit(debug::EXIT_SUCCESS); loop {} } ``` -------------------------------- ### Install cargo-generate Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Installs the `cargo-generate` tool, which is used to scaffold new Rust projects from templates. This is a prerequisite for using the `app-template`. ```console cargo install cargo-generate ``` -------------------------------- ### Verify OpenOCD Installation (Text) Source: https://github.com/rust-embedded/book/blob/master/src/intro/install/windows.md This command checks if OpenOCD, an on-chip debugger, has been successfully installed and added to the system's PATH on Windows. It displays the OpenOCD version if found. ```text $ openocd -v Open On-Chip Debugger 0.10.0 (..) ``` -------------------------------- ### Verify arm-none-eabi-gdb Installation (Text) Source: https://github.com/rust-embedded/book/blob/master/src/intro/install/windows.md This command verifies the installation of the GNU Debugger for ARM embedded processors on Windows. It checks if the `arm-none-eabi-gdb` executable is in the system's PATH and displays its version information. ```text $ arm-none-eabi-gdb -v GNU gdb (GNU Tools for Arm Embedded Processors 7-2018-q2-update) 8.1.0.20180315-git (..) ``` -------------------------------- ### FFI: Exposing Rust Functions to C Source: https://context7.com/rust-embedded/book/llms.txt Illustrates how to create C-compatible APIs from Rust using `#[no_mangle]` and `extern "C"`. This enables integration with existing C/C++ projects. The example includes Rust library code, Cargo.toml configuration for creating a static or dynamic library, and C header/usage examples. ```rust // Rust library code (lib.rs) #[no_mangle] pub extern "C" fn rust_function() { // Implementation } #[no_mangle] pub extern "C" fn rust_add(a: i32, b: i32) -> i32 { a + b } ``` ```toml # Cargo.toml [lib] name = "your_crate" crate-type = ["staticlib"] # or "cdylib" for dynamic lib ``` ```c // C header file (generated by cbindgen) void rust_function(void); int32_t rust_add(int32_t a, int32_t b); // C usage #include "my-rust-project.h" rust_function(); int result = rust_add(2, 3); ``` -------------------------------- ### Rust to C Type Conversion Example Source: https://github.com/rust-embedded/book/blob/master/src/interoperability/index.md Demonstrates the conversion between Rust primitive types and their corresponding C types using `c_uint` as an example. This showcases how Rust types can be directly used as C types and vice versa, assuming matching bit-widths on the target platform. ```rust fn foo(num: u32) { let c_num: c_uint = num; let r_num: u32 = c_num; } ``` -------------------------------- ### Cargo.toml Placeholders Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Illustrates the placeholders within the `Cargo.toml` file of the `cortex-m-quickstart` template. These placeholders, such as `{{authors}}` and `{{project-name}}`, need to be replaced with actual values during project setup. ```toml [package] authors = ["{{authors}}"] # "{{authors}}" -> "John Smith" edition = "2018" name = "{{project-name}}" # "{{project-name}}" -> "app" version = "0.1.0" # .. [[bin]] name = "{{project-name}}" # "{{project-name}}" -> "app" test = false bench = false ``` -------------------------------- ### Start OpenOCD Debug Server Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md Initiates the OpenOCD server to connect to the ST-LINK debugger on the STM32F3DISCOVERY board. This command requires the `openocd.cfg` configuration file to be present in the project root. ```console openocd ``` -------------------------------- ### Connect GDB to QEMU Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This command launches the GDB debugger and loads the debug symbols for the specified executable. After GDB starts, you connect to the running QEMU instance, which is acting as a GDB server, using the 'target remote' command. ```console gdb-multiarch -q target/thumbv7m-none-eabi/debug/examples/hello ``` ```console target remote :3333 ``` -------------------------------- ### C Header Example Source: https://github.com/rust-embedded/book/blob/master/src/interoperability/c-with-rust.md A sample C header file defining a structure `CoolStruct` and a function `cool_function` that takes an integer, a character, and a pointer to `CoolStruct`. ```c /* File: cool.h */ typedef struct CoolStruct { int x; int y; } CoolStruct; void cool_function(int i, char c, CoolStruct* cs); ``` -------------------------------- ### OpenOCD Configuration and Output Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This snippet shows the OpenOCD configuration files and the expected output when OpenOCD starts. It includes commands for finding interface and target configuration files, and notes on modifying the configuration for older discovery boards. ```text source [find interface/stlink-v2.cfg] source [find target/stm32f3x.cfg] > **NOTE** If you found out that you have an older revision of the discovery > board during the [verify] section then you should modify the `openocd.cfg` > file at this point to use `interface/stlink-v2.cfg`. ``` text $ openocd Open On-Chip Debugger 0.10.0 Licensed under GNU GPL v2 For bug reports, read http://openocd.org/doc/doxygen/bugs.html Info : auto-selecting first available session transport "hla_swd". To override use 'transport select '. adapter speed: 1000 kHz adapter_nsrst_delay: 100 Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD none separate Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : Unable to match requested speed 1000 kHz, using 950 kHz Info : clock speed 950 kHz Info : STLINK v2 JTAG v27 API v2 SWIM v15 VID 0x0483 PID 0x374B Info : using stlink api v2 Info : Target voltage: 2.913879 Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints ``` ``` -------------------------------- ### Build Embedded Project with Cargo Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md Compiles the embedded Rust project, specifically the 'hello' example, using `cargo build`. This command utilizes the configured target and linker script to produce a binary suitable for the target hardware. ```console cargo build --example hello ``` -------------------------------- ### OpenOCD Configuration for STM32F3DISCOVERY Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md An example OpenOCD configuration file (`openocd.cfg`) for the STM32F3DISCOVERY board. It specifies the interface (ST-LINK) and target configuration, allowing OpenOCD to communicate with the microcontroller. ```text # Sample OpenOCD configuration for the STM32F3DISCOVERY development board # Depending on the hardware revision you got you'll have to pick ONE of these # interfaces. At any time only one interface should be commented out. # Revision C (newer revision) source [find interface/stlink.cfg] # Revision A and B (older revisions) ``` -------------------------------- ### Launch QEMU in Debug Mode Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Starts the QEMU emulator with the specified CPU and machine, disables graphics, enables semihosting, and configures it to listen for a GDB connection on TCP port 3333. The '-S' flag freezes the machine at startup, allowing the debugger to attach before execution begins. The program to be debugged is loaded via the '-kernel' flag. ```console qemu-system-arm \ -cpu cortex-m3 \ -machine lm3s6965evb \ -nographic \ -semihosting-config enable=on,target=native \ -gdb tcp::3333 \ -S \ -kernel target/thumbv7m-none-eabi/debug/examples/hello ``` -------------------------------- ### Panic Message Output Example Source: https://github.com/rust-embedded/book/blob/master/src/start/panicking.md Shows the output when an out-of-bounds array access occurs and the `panic-semihosting` handler is active. The message clearly indicates the reason for the panic ('index out of bounds') and the location in the source code. ```text $ cargo run Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb (..) panicked at 'index out of bounds: the len is 3 but the index is 4', src/main.rs:12:13 ``` -------------------------------- ### Rust Iterator Example Source: https://github.com/rust-embedded/book/blob/master/src/c-tips/index.md Illustrates the idiomatic Rust way of iterating over an array using iterators. This method is preferred for safety and potential performance benefits over manual indexing. ```rust let arr = [0u16; 16]; for element in arr.iter() { process(*element); } ``` -------------------------------- ### C Array Iteration Example Source: https://github.com/rust-embedded/book/blob/master/src/c-tips/index.md Demonstrates traditional C-style array iteration using index-based access. This approach is contrasted with Rust's iterator pattern. ```c int16_t arr[16]; int i; for(i=0; i ! { let mut xs = Vec::new(); xs.push(42); assert!(xs.pop(), Some(42)); loop { // .. } } ``` -------------------------------- ### Configure Memory Layout for Embedded Projects Source: https://context7.com/rust-embedded/book/llms.txt Provides an example of a `memory.x` linker script, essential for embedded projects. It defines the memory regions (FLASH and RAM) for a specific microcontroller, like the LM3S6965. ```text MEMORY { /* NOTE 1 K = 1 KiBi = 1024 bytes */ /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ FLASH : ORIGIN = 0x00000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 64K } /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ ``` -------------------------------- ### Instantiate and Use heapless Vec in Rust Source: https://github.com/rust-embedded/book/blob/master/src/collections/index.md Demonstrates how to use `heapless::Vec` for fixed-capacity collections. It requires specifying the capacity upfront (e.g., `U8` for 8 elements) and handling potential errors from operations like `push` which return a `Result` due to the fixed capacity. This example shows basic instantiation, pushing an element, and popping it. ```rust // heapless version: v0.4.x use heapless::Vec; use heapless::consts::*; #[entry] fn main() -> ! { let mut xs: Vec<_, U8> = Vec::new(); xs.push(42).unwrap(); assert_eq!(xs.pop(), Some(42)); loop {} } ``` -------------------------------- ### Rust Example Usage of Type-State GPIO Source: https://github.com/rust-embedded/book/blob/master/src/static-guarantees/design-contracts.md Demonstrates how to use the type-state enforced GPIO configuration. It shows valid state transitions and operations, highlighting how the compiler prevents invalid actions like setting a bit on an input pin. ```rust /* * Example 1: Unconfigured to High-Z input */ let pin: GpioConfig = get_gpio(); // Can't do this, pin isn't enabled! // pin.into_input_pull_down(); // Now turn the pin from unconfigured to a high-z input let input_pin = pin.into_enabled_input(); // Read from the pin let pin_state = input_pin.bit_is_set(); // Can't do this, input pins don't have this interface! // input_pin.set_bit(true); /* * Example 2: High-Z input to Pulled Low input */ let pulled_low = input_pin.into_input_pull_down(); let pin_state = pulled_low.bit_is_set(); /* * Example 3: Pulled Low input to Output, set high */ let output_pin = pulled_low.into_enabled_output(); output_pin.set_bit(true); // Can't do this, output pins don't have this interface! // output_pin.into_input_pull_down(); ``` -------------------------------- ### Configure QEMU Runner for ARM Cortex-M3 Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This configuration sets up the `cargo run` command to use QEMU system emulator for ARM Cortex-M3. It specifies the machine, disables graphics, and enables semihosting for debugging. This is a common setup for embedded development on ARM. ```toml [target.'cfg(all(target_arch = "arm", target_os = "none"))'] runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" ``` -------------------------------- ### Add Cross-Compilation Target to Rust Toolchain Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This command adds a specific compilation target, `thumbv7m-none-eabi` for Cortex-M3, to your Rust toolchain. This is necessary if the target is not installed by default, enabling Cargo to cross-compile for that architecture. Run this command once to set up the target. ```console rustup target add thumbv7m-none-eabi ``` -------------------------------- ### Singleton Pattern for Peripherals in Rust Source: https://context7.com/rust-embedded/book/llms.txt Illustrates the singleton pattern for peripherals, ensuring only one instance exists. This allows the Rust borrow checker to enforce safe access at compile time. The example shows manual implementation and usage of the `cortex_m::singleton!` macro. ```rust use core::mem::replace; struct Peripherals { serial: Option, } impl Peripherals { fn take_serial(&mut self) -> SerialPort { let p = replace(&mut self.serial, None); p.unwrap() // Panics if called twice } } static mut PERIPHERALS: Peripherals = Peripherals { serial: Some(SerialPort), }; fn main() { let serial_1 = unsafe { PERIPHERALS.take_serial() }; // let serial_2 = unsafe { PERIPHERALS.take_serial() }; // Would panic! // Now serial_1 can be used with borrow checker guarantees let _ = serial_1.read_speed(); } // Using cortex_m::singleton! macro use cortex_m::singleton; fn main() { let x: &'static mut bool = singleton!(: bool = false).unwrap(); } ``` -------------------------------- ### Rust SysTick Exception Example with Semihosting Source: https://github.com/rust-embedded/book/blob/master/src/start/exceptions.md Configures the system timer to trigger a SysTick exception approximately every second. The exception handler increments a counter and prints its value to the host console via semihosting. Includes conditional exit for QEMU. ```rust #![deny(unsafe_code)] #![no_main] #![no_std] use panic_halt as _; use core::fmt::Write; use cortex_m::peripheral::syst::SystClkSource; use cortex_m_rt::{entry, exception}; use cortex_m_semihosting::{ debug, hio::{self, HostStream}, }; #[entry] fn main() -> ! { let p = cortex_m::Peripherals::take().unwrap(); let mut syst = p.SYST; // configures the system timer to trigger a SysTick exception every second syst.set_clock_source(SystClkSource::Core); // this is configured for the LM3S6965 which has a default CPU clock of 12 MHz syst.set_reload(12_000_000); syst.clear_current(); syst.enable_counter(); syst.enable_interrupt(); loop {} } #[exception] fn SysTick() { static mut COUNT: u32 = 0; static mut STDOUT: Option = None; *COUNT += 1; // Lazy initialization if STDOUT.is_none() { *STDOUT = hio::hstdout().ok(); } if let Some(hstdout) = STDOUT.as_mut() { write!(hstdout, "{}", *COUNT).ok(); } // IMPORTANT omit this `if` block if running on real hardware or your // debugger will end in an inconsistent state if *COUNT == 9 { // This will terminate the QEMU process debug::exit(debug::EXIT_SUCCESS); } } ``` -------------------------------- ### Global Allocator with `alloc` Crate in Rust Source: https://context7.com/rust-embedded/book/llms.txt Shows how to configure a global allocator and Out-Of-Memory (OOM) handler for dynamic memory allocation using the `alloc` crate in Rust. This example uses a simple bump pointer allocator and requires the `alloc_error_handler` feature. ```rust #![feature(alloc_error_handler)] extern crate alloc; use alloc::vec::Vec; use core::alloc::{GlobalAlloc, Layout}; use cortex_m::asm; // Simple bump pointer allocator struct BumpPointerAlloc { /* ... */ } #[global_allocator] static HEAP: BumpPointerAlloc = BumpPointerAlloc { head: UnsafeCell::new(0x2000_0100), end: 0x2000_0200, }; #[alloc_error_handler] fn on_oom(_layout: Layout) -> ! { asm::bkpt(); loop {} } #[entry] fn main() -> ! { let mut xs = Vec::new(); xs.push(42); assert!(xs.pop(), Some(42)); loop {} } ``` -------------------------------- ### Typestate Programming for Peripheral Safety in Rust Source: https://context7.com/rust-embedded/book/llms.txt Demonstrates typestate programming in Rust to encode peripheral states within types. This approach enforces correct usage and prevents invalid state transitions at compile time. The example defines different states for a GPIO pin (Unconfigured, Input, Output) and provides methods specific to each state. ```rust pub mod gpio { pub struct Unconfigured; pub struct Input; pub struct Output; pub struct GpioPin { pin: u8, _state: core::marker::PhantomData, } impl GpioPin { pub fn new(pin: u8) -> Self { GpioPin { pin, _state: core::marker::PhantomData } } pub fn into_input(self) -> GpioPin { // Configure hardware for input mode GpioPin { pin: self.pin, _state: core::marker::PhantomData } } pub fn into_output(self) -> GpioPin { // Configure hardware for output mode GpioPin { pin: self.pin, _state: core::marker::PhantomData } } } impl GpioPin { pub fn read(&self) -> bool { // Read pin state true } } impl GpioPin { pub fn set_high(&mut self) { // Set pin high } pub fn set_low(&mut self) { // Set pin low } } } fn main() { let pin = gpio::GpioPin::new(5); let mut output = pin.into_output(); output.set_high(); // output.read(); // Compile error! read() not available for Output state } ``` -------------------------------- ### Configure Serial Port with HAL Crate in Rust Source: https://github.com/rust-embedded/book/blob/master/src/start/registers.md This snippet demonstrates configuring a serial port (UART0) using the `tm4c123x-hal` crate. It shows how to initialize peripherals, configure clock settings using a PLL, and set up GPIO pins for UART communication. The example highlights the HAL's approach to ensuring correct peripheral and clock configuration at compile time. ```rust #![no_std] #![no_main] use panic_halt as _; use cortex_m_rt::entry; use tm4c123x_hal as hal; use tm4c123x_hal::prelude::*; use tm4c123x_hal::serial::{NewlineMode, Serial}; use tm4c123x_hal::sysctl; #[entry] fn main() -> ! { let p = hal::Peripherals::take().unwrap(); let cp = hal::CorePeripherals::take().unwrap(); // Wrap up the SYSCTL struct into an object with a higher-layer API let mut sc = p.SYSCTL.constrain(); // Pick our oscillation settings sc.clock_setup.oscillator = sysctl::Oscillator::Main( sysctl::CrystalFrequency::_16mhz, sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz), ); // Configure the PLL with those settings let clocks = sc.clock_setup.freeze(); // Wrap up the GPIO_PORTA struct into an object with a higher-layer API. // Note it needs to borrow `sc.power_control` so it can power up the GPIO // peripheral automatically. let mut porta = p.GPIO_PORTA.split(&sc.power_control); // Activate the UART. let uart = Serial::uart0( p.UART0, // The transmit pin porta .pa1 .into_af_push_pull::(&mut porta.control), // The receive pin porta .pa0 .into_af_push_pull::(&mut porta.control), // No RTS or CTS required (), (), // The baud rate 115200_u32.bps(), // Output handling NewlineMode::SwapLFtoCRLF, // We need the clock rates to calculate the baud rate divisors &clocks, // We need this to power up the UART peripheral &sc.power_control, ); loop { writeln!(uart, "Hello, World!\r\n").unwrap(); } } ``` -------------------------------- ### Download and Extract Template Archive Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Manually downloads the latest snapshot of the `cortex-m-quickstart` template as a ZIP archive and extracts it. This method is an alternative to using `git` or `cargo-generate` and also requires filling in `Cargo.toml` placeholders. ```console curl -LO https://github.com/rust-embedded/cortex-m-quickstart/archive/master.zip unzip master.zip mv cortex-m-quickstart-master app cd app ``` -------------------------------- ### Clone cortex-m-quickstart Repository Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Clones the `cortex-m-quickstart` repository using Git to create a new embedded Rust project. This method requires manually filling in placeholders in the `Cargo.toml` file afterward. ```console git clone https://github.com/rust-embedded/cortex-m-quickstart app cd app ``` -------------------------------- ### Configure GDB Runner for ARM Embedded Debugging Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This configuration enables `cargo run` to start a GDB debugging session for ARM embedded targets. It provides three options for the GDB command, depending on the user's system and installed GDB versions. These runners typically require OpenOCD to be running. ```toml [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # uncomment ONE of these three option to make `cargo run` start a GDB session # which option to pick depends on your system runner = "arm-none-eabi-gdb -x openocd.gdb" # runner = "gdb-multiarch -x openocd.gdb" # runner = "gdb -x openocd.gdb" ``` -------------------------------- ### Log 'Hello, world!' with Semihosting in Rust Source: https://github.com/rust-embedded/book/blob/master/src/start/semihosting.md This Rust code snippet demonstrates how to log a 'Hello, world!' message to the host console using the `cortex-m-semihosting` crate. It requires the `panic-halt` and `cortex-m-rt` crates. The output appears in the OpenOCD logs when run on hardware or in QEMU with the appropriate flags. ```rust #![no_main] #![no_std] use panic_halt as _; use cortex_m_rt::entry; use cortex_m_semihosting::hprintln; #[entry] fn main() -> ! { hprintln!("Hello, world!").unwrap(); loop {} } ``` -------------------------------- ### Rust SerialPort Method Example Source: https://github.com/rust-embedded/book/blob/master/src/peripherals/singletons.md An example of a `SerialPort` method that reads from a memory-mapped register. The method requires a reference to `self`, enforcing that hardware access is tied to an owned or borrowed instance of the `SerialPort`, thus working with the borrow checker. ```rust impl SerialPort { const SER_PORT_SPEED_REG: *mut u32 = 0x4000_1000 as _; fn read_speed( &self // <------ This is really, really important ) -> u32 { unsafe { ptr::read_volatile(Self::SER_PORT_SPEED_REG) } } } ``` -------------------------------- ### Array Out-of-Bounds Panic Example in Rust Source: https://github.com/rust-embedded/book/blob/master/src/start/panicking.md Demonstrates a panic caused by accessing an array out of its bounds. This example uses the `panic-semihosting` crate to print the panic message to the host console via semihosting. It highlights how runtime checks in Rust prevent memory unsafety by triggering a panic. ```rust #![no_main] #![no_std] use panic_semihosting as _; use cortex_m_rt::entry; #[entry] fn main() -> ! { let xs = [0, 1, 2]; let i = xs.len(); let _y = xs[i]; // out of bounds access loop {} } ``` -------------------------------- ### Stepping Through Code and Observing Output in GDB Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This snippet demonstrates stepping into a function and observing the output in the OpenOCD console. It shows the GDB 'step' command and the resulting output, including the 'Hello, world!' message. ```console (gdb) step halted: PC: 0x08000496 hello::__cortex_m_rt_main () at examples/hello.rs:13 13 hprintln!("Hello, world!").unwrap(); $ openocd (..) Info : halted: PC: 0x08000502 Hello, world! Info : halted: PC: 0x080004ac Info : halted: PC: 0x080004ae Info : halted: PC: 0x080004b0 Info : halted: PC: 0x080004b4 Info : halted: PC: 0x080004b8 Info : halted: PC: 0x080004bc ``` -------------------------------- ### Loading Program and Enabling Semihosting in GDB Source: https://github.com/rust-embedded/book/blob/master/src/start/hardware.md This snippet illustrates the GDB commands for loading the compiled program onto the microcontroller and enabling semihosting. It shows the output of the 'load' command and the confirmation message after enabling semihosting. ```console (gdb) load Loading section .vector_table, size 0x400 lma 0x8000000 Loading section .text, size 0x1518 lma 0x8000400 Loading section .rodata, size 0x414 lma 0x8001918 Start address 0x08000400, load size 7468 Transfer rate: 13 KB/sec, 2489 bytes/write. (gdb) monitor arm semihosting enable semihosting is enabled ``` -------------------------------- ### Build Project for Target Architecture (Cargo) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md These commands demonstrate how to build a Rust project for a specific target architecture using Cargo. If the target is set in `.cargo/config.toml`, `cargo build` alone will use it. Otherwise, explicitly specify the target using `cargo build --target `. ```console cargo build --target thumbv7m-none-eabi ``` ```console cargo build ``` -------------------------------- ### Running Embedded Binary on QEMU (Manual) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This command manually runs the compiled embedded Rust binary on QEMU. It specifies the CPU, machine, disables the GUI, enables semihosting, and points to the kernel binary. This is useful for understanding QEMU's configuration. ```bash qemu-system-arm \ -cpu cortex-m3 \ -machine lm3s6965evb \ -nographic \ -semihosting-config enable=on,target=native \ -kernel target/thumbv7m-none-eabi/debug/hello ``` -------------------------------- ### Generate New Project with cargo-generate Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Generates a new Rust embedded project using the `app-template` from GitHub. This command clones the template and prompts the user for project details, creating a new project directory. ```console cargo generate --git https://github.com/knurling-rs/app-template ``` -------------------------------- ### Running Embedded Binary with qemu-run (defmt) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This command uses the `qemu-run` tool to execute the embedded binary, which is necessary when using `defmt` for logging. It requires cloning the `defmt` repository and then running `cargo run` within the `qemu-run` directory, specifying the machine and the path to the compiled binary. ```bash git clone git@github.com:knurling-rs/defmt.git cd defmt/qemu-run/ cargo run -- --machine lm3s6965evb ../qemu-rs/target/thumbv7m-none-eabi/debug/hello ``` -------------------------------- ### Rust Global Mutable Variable Example Source: https://github.com/rust-embedded/book/blob/master/src/peripherals/singletons.md Demonstrates a basic, unsafe implementation of a mutable global variable in Rust. This approach is discouraged due to its inherent unsafety and lack of borrow checker support for managing shared mutable state. ```rust static mut THE_SERIAL_PORT: SerialPort = SerialPort; fn main() { let _ = unsafe { THE_SERIAL_PORT.read_speed(); }; } ``` -------------------------------- ### Create Minimal Bare Metal Rust Application Source: https://context7.com/rust-embedded/book/llms.txt Demonstrates the basic structure for an embedded Rust application using `#![no_std]` and `#![no_main]` with the `cortex-m-rt` crate for a custom entry point. This is the foundation for any bare metal project. ```rust #![no_std] #![no_main] use panic_halt as _; use cortex_m_rt::entry; #[entry] fn main() -> ! { // Application code here loop { // Main loop - embedded programs never exit } } ``` -------------------------------- ### Mutable Hardware Access with Rust Source: https://github.com/rust-embedded/book/blob/master/src/peripherals/singletons.md Demonstrates a Rust function `setup_spi_port` that takes mutable references to `SpiPort` and `GpioPin`, indicating that it can modify hardware settings. This pattern leverages Rust's ownership to ensure safe mutable access. ```rust fn setup_spi_port( spi: &mut SpiPort, cs_pin: &mut GpioPin ) -> Result<()> { // ... } ``` -------------------------------- ### RTIC Framework for Real-Time Concurrency in Rust Source: https://context7.com/rust-embedded/book/llms.txt Provides an example of the RTIC (Real-Time Interrupt-driven Concurrency) framework for embedded Rust. RTIC uses static priority scheduling to enable safe and efficient concurrency, managing tasks and shared resources. ```rust // cortex-m-rtic v0.5.x #[rtic::app(device = lm3s6965, peripherals = true)] const APP: () = { #[init] fn init(cx: init::Context) { static mut X: u32 = 0; // Cortex-M peripherals let core: cortex_m::Peripherals = cx.core; // Device specific peripherals let device: lm3s6965::Peripherals = cx.device; } }; ``` -------------------------------- ### Rust Default Exception Handler Example Source: https://github.com/rust-embedded/book/blob/master/src/start/exceptions.md Demonstrates the default exception handler provided by the `cortex-m-rt` crate, which loops indefinitely. Shows how to override this default handler to implement custom logic for unhandled exceptions, including receiving the exception number. ```rust fn DefaultHandler() { loop {} } ``` ```rust #[exception] fn DefaultHandler(irqn: i16) { // custom default handler } ``` -------------------------------- ### Basic no_std Rust Program Structure Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md Presents the fundamental structure of a `no_std` Rust program for embedded systems. It includes essential attributes like `#![no_std]` and `#![no_main]`, a panic handler, and the `#[entry]` macro to mark the program's entry point. ```rust #![no_std] #![no_main] use panic_halt as _; use cortex_m_rt::entry; #[entry] fn main() -> ! { loop { // your code goes here } } ``` -------------------------------- ### Declare Interrupt Handler in Rust Source: https://context7.com/rust-embedded/book/llms.txt Illustrates how to declare a device-specific interrupt handler using the `#[interrupt]` attribute. This example shows a handler for a TIMER2A interrupt, which increments a static counter. Similar to exception handlers, it supports safe static mutable state. ```rust use lm3s6965::interrupt; // Re-exported from device crate #[interrupt] fn TIMER2A() { static mut COUNT: u32 = 0; // COUNT has type `&mut u32` and is safe to use *COUNT += 1; // Clear the interrupt source (device-specific) // ... } ``` -------------------------------- ### Configuring Cargo Runner for QEMU (TOML) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This snippet shows how to uncomment and configure a custom runner in `.cargo/config.toml`. This runner automatically invokes QEMU with specified parameters whenever `cargo run` is executed for the `thumbv7m-none-eabi` target, simplifying the execution process. ```toml [target.thumbv7m-none-eabi] # uncomment this to make `cargo run` execute programs on QEMU runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel" ``` -------------------------------- ### Handling Out Of Memory Errors in Rust with Alloc Error Handler Source: https://github.com/rust-embedded/book/blob/master/src/collections/index.md This snippet demonstrates how to define a custom Out Of Memory (OOM) handler using the unstable `alloc_error_handler` attribute. When an allocation fails, this function is called, which in this example triggers a breakpoint and enters an infinite loop. ```rust #![feature(alloc_error_handler)] use cortex_m::asm; #[alloc_error_handler] fn on_oom(_layout: Layout) -> ! { asm::bkpt(); loop {} } ``` -------------------------------- ### Configure Rust Crate for Dynamic or Static Library Source: https://github.com/rust-embedded/book/blob/master/src/interoperability/rust-with-c.md Configure your Rust project's `Cargo.toml` to build a system library (dynamic or static) instead of a standard Rust library. This allows external build systems to link against it and specifies the output name. ```toml [lib] name = "your_crate" crate-type = ["cdylib"] # Creates dynamic lib # crate-type = ["staticlib"] # Creates static lib ``` -------------------------------- ### Volatile Memory Access in C Source: https://github.com/rust-embedded/book/blob/master/src/c-tips/index.md Illustrates volatile memory access in C using the `volatile` keyword. This pattern is common in embedded programming for memory-mapped registers that can be modified by external hardware. The example shows a simple interrupt-driven signaling mechanism. ```c volatile bool signalled = false; void ISR() { // Signal that the interrupt has occurred signalled = true; } void driver() { while(true) { // Sleep until signalled while(!signalled) { WFI(); } // Reset signalled indicator signalled = false; // Perform some task that was waiting for the interrupt run_task(); } } ``` -------------------------------- ### Rust Hard Fault Handler Example Source: https://github.com/rust-embedded/book/blob/master/src/start/exceptions.md This Rust code demonstrates a hard fault handler. It triggers a hard fault by reading from a nonexistent memory location and then prints the `ExceptionFrame` to the console for debugging. This handler is crucial for diagnosing system instability in embedded Rust applications. ```rust #![no_main] #![no_std] use panic_halt as _; use core::fmt::Write; use core::ptr; use cortex_m_rt::{entry, exception, ExceptionFrame}; use cortex_m_semihosting::hio; #[entry] fn main() -> ! { // read a nonexistent memory location unsafe { ptr::read_volatile(0x3FFF_0000 as *const u32); } loop {} } #[exception] fn HardFault(ef: &ExceptionFrame) -> ! { if let Ok(mut hstdout) = hio::hstdout() { writeln!(hstdout, "{:#?}", ef).ok(); } loop {} } ``` -------------------------------- ### Set Breakpoint and Continue Execution in GDB Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md These GDB commands are used to control program execution. 'list main' displays the source code around the 'main' function. 'break 13' sets a breakpoint on line 13 of the current file. 'continue' resumes execution until a breakpoint is hit or the program terminates. 'next' executes the current line and stops at the next line. ```console list main ``` ```console break 13 ``` ```console continue ``` ```console next ``` ```console quit ``` -------------------------------- ### Manage State in Rust Interrupt Handler Source: https://github.com/rust-embedded/book/blob/master/src/start/interrupts.md This example shows how to safely manage mutable state within a Rust interrupt handler using a `static mut` variable. The `COUNT` variable is declared and mutated within the `TIMER2A` interrupt handler, demonstrating safe state keeping. ```rust #[interrupt] fn TIMER2A() { static mut COUNT: u32 = 0; // `COUNT` has type `&mut u32` and it's safe to use *COUNT += 1; } ``` -------------------------------- ### Switching defmt Transport Dependencies (Cargo) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This snippet demonstrates how to remove the `defmt-rtt` dependency and add `defmt-semihosting` using Cargo commands. This is necessary to switch the defmt logging transport from RTT to semihosting for QEMU execution. ```bash cargo remove defmt-rtt ``` ```bash cargo add defmt-semihosting ``` -------------------------------- ### Write TM4C123 PWM Register using Rust PAC Source: https://github.com/rust-embedded/book/blob/master/src/start/registers.md Shows how to write to the `ctl` register of the PWM0 peripheral on a TM4C123 microcontroller using the `tm4c123x` PAC crate. This example clears the `globalsync0` bit. Note that this operation overwrites existing register content. ```rust pwm.ctl.write(|w| w.globalsync0().clear_bit()); ``` -------------------------------- ### Define Memory Layout for Microcontroller (linker script) Source: https://github.com/rust-embedded/book/blob/master/src/start/qemu.md This linker script defines the memory regions (FLASH and RAM) for a target microcontroller, such as the LM3S6965. It's crucial for the build process to succeed by correctly allocating memory for code and data. Adjust the ORIGIN and LENGTH values to match your specific device's memory map. ```text MEMORY { /* NOTE 1 K = 1 KiBi = 1024 bytes */ /* TODO Adjust these memory regions to match your device memory layout */ /* These values correspond to the LM3S6965, one of the few devices QEMU can emulate */ FLASH : ORIGIN = 0x00000000, LENGTH = 256K RAM : ORIGIN = 0x20000000, LENGTH = 64K } /* This is where the call stack will be allocated. */ /* The stack is of the full descending type. */ /* You may want to use this variable to locate the call stack and static variables in different memory regions. Below is shown the default value */ /* _stack_start = ORIGIN(RAM) + LENGTH(RAM); */ /* You can use this symbol to customize the location of the .text section */ /* If omitted the .text section will be placed right after the .vector_table section */ /* This is required only on microcontrollers that store some configuration right after the vector table */ /* _stext = ORIGIN(FLASH) + 0x400; */ /* Example of putting non-initialized variables into custom RAM locations. */ /* This assumes you have defined a region RAM2 above, and in the Rust sources added the attribute `#[link_section = ".ram2bss"]` to the data you want to place there. */ /* Note that the section will not be zero-initialized by the runtime! */ /* SECTIONS { .ram2bss (NOLOAD) : ALIGN(4) { *(.ram2bss); . = ALIGN(4); } > RAM2 } INSERT AFTER .bss; */ ``` -------------------------------- ### Read TM4C123 PWM Register using Rust PAC Source: https://github.com/rust-embedded/book/blob/master/src/start/registers.md Demonstrates how to read the `ctl` register of the PWM0 peripheral on a TM4C123 microcontroller using the `tm4c123x` PAC crate. This example checks if the `globalsync0` bit is set. It assumes the `pwm` variable is already initialized. ```rust if pwm.ctl.read().globalsync0().is_set() { // Do a thing } ``` -------------------------------- ### Configure System Clock and UART using HAL in Rust Source: https://context7.com/rust-embedded/book/llms.txt Demonstrates configuring the system clock and a UART peripheral using a HAL crate (tm4c123x-hal). It initializes peripherals, sets up clock frequencies, and configures GPIO pins for UART communication, providing compile-time safety for pin assignments and peripheral usage. ```rust #![no_std] #![no_main] use panic_halt as _; use cortex_m_rt::entry; use tm4c123x_hal as hal; use tm4c123x_hal::prelude::*; use tm4c123x_hal::serial::{NewlineMode, Serial}; use tm4c123x_hal::sysctl; use core::fmt::Write; #[entry] fn main() -> ! { let p = hal::Peripherals::take().unwrap(); let cp = hal::CorePeripherals::take().unwrap(); // Configure system clock via HAL let mut sc = p.SYSCTL.constrain(); sc.clock_setup.oscillator = sysctl::Oscillator::Main( sysctl::CrystalFrequency::_16mhz, sysctl::SystemClock::UsePll(sysctl::PllOutputFrequency::_80_00mhz), ); let clocks = sc.clock_setup.freeze(); // Configure GPIO port with type-safe pin modes let mut porta = p.GPIO_PORTA.split(&sc.power_control); // Create UART with compile-time verified pin configuration let uart = Serial::uart0( p.UART0, porta.pa1.into_af_push_pull::(&mut porta.control), // TX porta.pa0.into_af_push_pull::(&mut porta.control), // RX (), // No RTS (), // No CTS 115200_u32.bps(), NewlineMode::SwapLFtoCRLF, &clocks, &sc.power_control, ); loop { writeln!(uart, "Hello, World!\r\n").unwrap(); } } ``` -------------------------------- ### Accessing Peripheral via Mutex in Critical Section Source: https://github.com/rust-embedded/book/blob/master/src/concurrency/index.md Shows how to safely access and modify a shared peripheral (GPIOA) from within a critical section. It involves borrowing the `Mutex`, then borrowing the `RefCell`, converting the `Option<&T>` to `Option<&T>` using `as_ref()`, and finally unwrapping to get the peripheral reference for modification. ```rust interrupt::free(|cs| { let gpioa = MY_GPIO.borrow(cs).borrow(); gpioa.as_ref().unwrap().odr.modify(|_, w| w.odr1().set_bit()); }); ``` -------------------------------- ### Frequency Counter with `static mut` and Interrupts (Rust) Source: https://github.com/rust-embedded/book/blob/master/src/concurrency/index.md Illustrates a frequency counter example using a `static mut` variable in Rust for embedded systems. This code is intentionally unsafe and demonstrates a potential race condition between the main loop and a timer interrupt, highlighting the dangers of unprotected shared mutable state. ```rust static mut COUNTER: u32 = 0; #[entry] fn main() -> ! { set_timer_1hz(); let mut last_state = false; loop { let state = read_signal_level(); if state && !last_state { // DANGER - Not actually safe! Could cause data races. unsafe { COUNTER += 1 }; } last_state = state; } } #[interrupt] fn timer() { unsafe { COUNTER = 0; } } ``` -------------------------------- ### Enable Semihosting in OpenOCD Source: https://github.com/rust-embedded/book/blob/master/src/start/semihosting.md This console command enables semihosting operations within an OpenOCD debug session. After executing this command in GDB, semihosting I/O operations from the embedded device will be directed to the host console. ```console (gdb) monitor arm semihosting enable semihosting is enabled ```