本文へ移動
cccskills
無料GitHub で公開

embedded-rust

Use when writing no_std Cortex-M or RISC-V firmware in Rust with cortex-m-rt, probe-rs, defmt, RTIC, or a panic handler. Not for no_std library constraints: use rust-no-std.

インストール方法を見る

含まれるファイル(3)

  • SKILL.md8.4 KB
  • agents/openai.yaml187 B
  • references/embedded-rust-targets.md2.6 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Embedded Rust

Contract

FieldBound contract
TriggerA Rust firmware project needs to be set up or fixed: #![no_std] #![no_main] layout, cortex-m-rt startup, probe-rs flashing and log streaming, defmt logging, an RTIC application, or the choice of panic handler.
AuthorityReversible local: writes only the project files under the directory the user names (Cargo.toml, .cargo/config.toml, memory.x, src/); rollback is deleting that directory or reverting it in version control. No remote mutation.
Side effectNew or edited project files in the named directory. Flashing a board writes the board's flash, which the next cargo run overwrites.
Donecargo build --release produces an ELF for the target triple, cargo run --release flashes it and streams defmt output to the terminal, and exactly one panic handler is linked.

Inputs

  • MCU part and its probe-rs chip name (probe-rs chip list prints the names).
  • Core: which Cortex-M or RISC-V core, and whether it has an FPU. This picks the target triple.
  • Flash and RAM origin and size from the datasheet (for memory.x).
  • Concurrency model: plain #[entry] loop, RTIC, or Embassy.
  • Debug probe on hand, or none (this picks the panic handler and the defmt transport).

Procedure

  1. Pick the target triple from the core and install it. Done when: rustup target add <triple> succeeds and the triple appears in rustc --print target-list. The full table is in references/embedded-rust-targets.md.

    CoreTarget triple
    Cortex-M0, M0+thumbv6m-none-eabi
    Cortex-M3thumbv7m-none-eabi
    Cortex-M4, M7 without FPUthumbv7em-none-eabi
    Cortex-M4F, M7Fthumbv7em-none-eabihf
    Cortex-M33 with FPUthumbv8m.main-none-eabihf
    RISC-V RV32IMACriscv32imac-unknown-none-elf
  2. Write Cargo.toml and .cargo/config.toml. Use edition 2024. The versions below are the current crates.io releases on 2026-09-05; run cargo add <crate> to take the current one rather than copying a number. debug = true in the release profile keeps DWARF for defmt and probe-rs; it does not change the flashed code size because debug info is not loaded to flash. Done when: cargo build --release links.

    # Cargo.toml
    [package]
    name = "my-firmware"
    version = "0.1.0"
    edition = "2024"
    
    [dependencies]
    cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
    cortex-m-rt = "0.7"
    defmt = "1"
    defmt-rtt = "1"
    panic-probe = { version = "1", features = ["print-defmt"] }
    
    [profile.release]
    opt-level = "s"
    lto = true
    codegen-units = 1
    debug = true
    
    # .cargo/config.toml
    [build]
    target = "thumbv7em-none-eabihf"
    
    [target.thumbv7em-none-eabihf]
    runner = "probe-rs run --chip STM32F411CEUx"
    rustflags = ["-C", "link-arg=-Tlink.x"]
    

    link.x is the linker script cortex-m-rt generates; it includes your memory.x:

    MEMORY
    {
      FLASH : ORIGIN = 0x08000000, LENGTH = 512K
      RAM   : ORIGIN = 0x20000000, LENGTH = 128K
    }
    
  3. Write the minimal program. #![no_std] drops the standard library, #![no_main] hands the entry point to cortex-m-rt, and the two as _ imports link the RTT transport and the panic handler without naming them. Done when: the program builds and cortex_m::Peripherals::take() is called at most once.

    #![no_std]
    #![no_main]
    
    use cortex_m_rt::entry;
    use defmt::info;
    use defmt_rtt as _;
    use panic_probe as _;
    
    #[entry]
    fn main() -> ! {
        info!("boot");
        let _core = cortex_m::Peripherals::take().unwrap();
        loop {
            info!("tick");
            cortex_m::asm::delay(8_000_000);
        }
    }
    
  4. Flash and stream logs with probe-rs. probe-rs run flashes, resets, and prints RTT and defmt output; probe-rs attach connects without reset or flash and keeps the running state. Done when: cargo run --release prints the info! lines.

    curl --proto '=https' --tlsv1.2 -LsSf https://github.com/probe-rs/probe-rs/releases/latest/download/probe-rs-tools-installer.sh | sh
    probe-rs list                      # connected probes
    probe-rs chip list | grep -i stm32 # chip names for --chip
    cargo run --release                # build, flash, stream defmt
    probe-rs attach --chip STM32F411CEUx target/thumbv7em-none-eabihf/release/my-firmware
    

    If probe-rs run fails to find a probe or chip, read probe-rs run --help and probe-rs list before changing the config.

  5. Log with defmt. defmt sends an interned string index plus raw arguments; the host decodes them from the ELF, so the ELF that is running must be the one the host reads. Done when: a #[derive(Format)] type prints through info!("{:?}", value).

    use defmt::{Format, error, info, warn};
    
    #[derive(Format)]
    struct Packet { id: u8, len: u16 }
    
    info!("temperature {} C", temp);
    warn!("stack {}/{}", used, total);
    error!("i2c {:?}", err);
    defmt::assert_eq!(result, expected);
    

    Transport: defmt-rtt needs a probe attached and is the default. defmt-semihosting works through a GDB or OpenOCD semihosting channel and is slower; use it when RTT is unavailable.

  6. For interrupt-driven concurrency, use RTIC 2. Tasks with binds are hardware interrupt handlers; software tasks run on the dispatcher interrupts you list. Shared resources are locked, so RTIC proves no data race at compile time. Done when: the RTIC app compiles and the bound ISR fires on the hardware event.

    #[rtic::app(device = stm32f4xx_hal::pac, peripherals = true, dispatchers = [SPI1])]
    mod app {
        use defmt::info;
    
        #[shared]
        struct Shared { counter: u32 }
    
        #[local]
        struct Local {}
    
        #[init]
        fn init(_cx: init::Context) -> (Shared, Local) {
            periodic::spawn().unwrap();
            (Shared { counter: 0 }, Local {})
        }
    
        #[task(shared = [counter])]
        async fn periodic(mut cx: periodic::Context) {
            loop {
                let n = cx.shared.counter.lock(|c| { *c += 1; *c });
                info!("count {}", n);
                rtic_monotonics::systick::Systick::delay(500.millis()).await;
            }
        }
    
        #[task(binds = EXTI0, priority = 2)]
        fn button(_cx: button::Context) {
            info!("button");
        }
    }
    

    Cargo dependencies for this: rtic with the thumbv7-backend feature and rtic-monotonics with the cortex-m-systick feature. Embassy (embassy-executor) is the async alternative; pick one executor per binary.

  7. Pick exactly one panic handler. Two handlers produce a duplicate #[panic_handler] link error. Done when: Cargo.toml lists one of the crates below and the build links.

    CrateBehaviorUse when
    panic-haltInfinite loopProduction without a probe
    panic-probePrints the message through defmt, then a breakpointDevelopment with probe-rs
    panic-semihostingPrints through semihostingDevelopment under GDB or OpenOCD
    panic-resetResets the coreRecovery where a watchdog would reset anyway

Failure and recovery

SymptomCauseFix
can't find crate for coreTarget not installedrustup target add <triple>.
Link error naming memory.x or _stack_startmemory.x missing or not on the linker search pathPut memory.x next to Cargo.toml, or emit its directory from build.rs with cargo:rustc-link-search.
Duplicate #[panic_handler]Two panic crates linkedKeep one.
No defmt outputHost reads a different ELF than the one flashed, or RTT is not linkedRebuild and flash in one cargo run; keep use defmt_rtt as _;.
probe-rs reports no probeUSB permissions or no udev ruleRun probe-rs list; install the udev rules from the probe-rs docs.
HardFault at bootWrong MEMORY origins or FPU triple on a core without FPUCheck memory.x against the datasheet and the triple against the core.

Output

A project directory that builds for the target triple, flashes with cargo run --release, streams defmt logs, and links one panic handler, plus a note naming the target triple and the probe-rs chip name that were used.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when configuring ADC sampling time, DMA-driven ADC, calibration, or DAC channel setup on bare-metal MCUs. Not for the DMA stream itself: use dma-baremetal.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

af-xdp

無料

Use when creating AF_XDP sockets, configuring UMEM and XSK rings, writing an XDP redirect program, or choosing copy versus zero-copy mode. Not for full kernel bypass: use dpdk.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when a completed session needs an agent-environment retrospective. Not for an engineering retrospective from telemetry: use engineering-retrospective.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

Use when a redacted, trimmed agent transcript must be appended to a GitHub PR or issue body, with human approval and preview. Not for automated or model-initiated insertion.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

agents-md

無料

Use when a repo needs agent setup, AGENTS.md added or made lean, CLAUDE.md audited, or agent instructions scored or pruned. Not for remote, credential, publish, deploy, or irreversible changes.

日本語の概要は準備中です。原文の説明を表示しています。

OutlineDriven/odin-claude-plugin372026年9月29日 更新

OutlineDriven のスキルをすべて見る

このスキルの問題を報告する