知识门户

返回

第3章:第一个裸机工程——从零搭建 `#![no_std]` 项目

Part1 了解嵌入式rust

第 3 章:开始上路——第一个裸机工程#

本章要回答的问题:Rust 裸机工程长什么样?每个文件的作用是什么?怎么编译、烧录、调试?


3.1 从 C 到 Rust:工程结构的映射#

如果你用 Keil 或 STM32CubeIDE 开发过 STM32 项目,你一定熟悉这样的工程结构:

Rust 的裸机工程结构要简洁得多

my_rust_project/
├── Cargo.toml                   ← 项目配置 + 依赖声明(≈ Makefile + 库管理)
├── Cargo.lock                   ← 依赖版本锁定(自动生成)
├── memory.x                     ← 内存布局(≈ 链接脚本中的 MEMORY 段)
├── build.rs                     ← 构建脚本(≈ Makefile 中的预处理步骤)
├── .cargo/
│   └── config.toml              ← Cargo 配置(默认 target、runner 等)
└── src/
    └── main.rs                  ← 应用入口(≈ main.c + startup 的 Rust 部分)
plaintext

逐项对照表#

C 工程文件Rust 工程文件说明
main.csrc/main.rs应用入口
startup_stm32f407xx.scortex-m-rt crate向量表 + Reset_Handler(由库提供,无需手写)
STM32F407VGTX_FLASH.ldmemory.x内存布局定义
Makefile / .cprojectCargo.toml + .cargo/config.toml构建配置
system_stm32f4xx.cHAL crate 的初始化代码时钟配置等
stm32f4xx_it.c#[interrupt] 宏标注的函数中断服务函数
CMSIS + HAL 库PAC + HAL crate(从 crates.io 下载)硬件抽象层
手动下载库源码到 Drivers/Cargo.toml 中声明依赖包管理

关键区别

  1. 启动代码不需要手写:C 工程中那个几百行的 startup.s(定义向量表、初始化 .data、清零 .bss),在 Rust 中由 cortex-m-rt crate 自动提供。你不需要写一行汇编。

  2. 库管理是自动的:C 工程中你需要手动下载 HAL 库源码、配置 include 路径、管理版本。Rust 中只需在 Cargo.toml 里写一行依赖声明,cargo build 会自动下载、编译、链接。

  3. 构建系统内置:不需要 Makefile、CMake、Keil 工程文件。cargo build 就是构建命令,cargo run 就是编译+烧录+运行。


3.2 创建项目与配置依赖#

创建项目#

# 创建一个新的 Rust 项目
cargo new my_first_bare_metal
cd my_first_bare_metal
bash

这会生成一个标准的 Rust 项目结构。但默认生成的是”主机程序”(运行在 Linux/Windows 上),我们需要将它改造为”裸机程序”。

Cargo.toml:项目的”身份证”#

打开 Cargo.toml,你会看到:

[package]
name = "my_first_bare_metal"
version = "0.1.0"
edition = "2021"

[dependencies]
toml

现在,添加裸机开发所需的依赖:

关键依赖说明#

Crate作用C 的对应
cortex-m-rt启动代码、向量表、#[entry]startup.s
cortex-m访问 Cortex-M 核心外设(NVIC、SCB)CMSIS Core
panic-haltpanic 时死循环while(1){} in HardFault
defmt高效日志框架printf(但体积小 10 倍以上)
defmt-rttdefmt 的 RTT 传输后端SEGGER RTT 库

Feature Flag:Rust 的 #ifdef#

在 C 中,你用 #ifdef 来条件编译:

// C:条件编译
#ifdef USE_HAL_DRIVER
    HAL_Init();
#endif

#ifdef STM32F407xx
    SystemClock_Config_F407();
#elif defined(STM32F103xB)
    SystemClock_Config_F103();
#endif
c

在 Rust 中,Feature Flag 是 Cargo 的一等公民:

# Cargo.toml 中的 feature 声明
[features]
default = ["stm32f407"]
stm32f407 = []
stm32f103 = []
toml
// Rust 中的条件编译
#[cfg(feature = "stm32f407")]
fn system_clock_config() {
    // F407 的时钟配置
}

#[cfg(feature = "stm32f103")]
fn system_clock_config() {
    // F103 的时钟配置
}
rust

与 C 的 #ifdef 的关键区别

特性C 的 #ifdefRust 的 Feature Flag
定义位置编译器命令行 / 头文件Cargo.toml
依赖传递手动管理自动传递(依赖的 feature 可以联动)
冲突检测Cargo 会报告 feature 冲突
文档化通常没有cargo doc 自动列出所有 feature
组合爆炸容易失控Cargo 有 resolver 处理

在实际的嵌入式 HAL crate 中,feature flag 通常用来选择芯片型号:

# 使用 embassy-stm32 时的典型配置
[dependencies.embassy-stm32]
version = "0.2"
features = ["stm32f407vg", "memory-x"]
toml

3.3 交叉编译与 target triple#

什么是 target triple?#

当你在 x86 电脑上运行 gcc main.c 时,编译器默认生成 x86 可执行文件。但嵌入式 MCU 是 ARM 架构——你需要交叉编译

Rust 用 target triple(目标三元组)来指定编译目标:

thumbv7em-none-eabihf
│     │    │    │
│     │    │    └── 浮点 ABI:hf = 硬件浮点(使用 FPU 寄存器传参)
│     │    └─────── 操作系统:none = 裸机(无 OS)
│     └──────────── 指令集变体:e = 增强(DSP 指令),m = Thumb-2
└────────────────── 架构:ARM Thumb(Cortex-M 系列)
plaintext

完整的 Cortex-M target triple 对照表#

Target Triple对应内核典型芯片
thumbv6m-none-eabiCortex-M0 / M0+STM32F0、STM32L0、RP2040
thumbv7m-none-eabiCortex-M3STM32F1、STM32L1
thumbv7em-none-eabiCortex-M4/M7(无 FPU)STM32F4(不用 FPU 时)
thumbv7em-none-eabihfCortex-M4/M7(有 FPU)STM32F4、STM32F7、STM32H7
thumbv8m.base-none-eabiCortex-M23STM32G0(部分型号)
thumbv8m.main-none-eabiCortex-M33(无 FPU)STM32L5、nRF53
thumbv8m.main-none-eabihfCortex-M33(有 FPU)STM32U5、nRF53
riscv32imac-unknown-none-elfRISC-V(如 ESP32-C3)ESP32-C3/C6/H2

本书主力平台 STM32F407 使用 thumbv7em-none-eabihf(Cortex-M4F,带硬件 FPU)。

与 C 的交叉编译工具链对比#

概念C 工具链Rust 工具链
编译器arm-none-eabi-gccrustc --target thumbv7em-none-eabihf
安装方式下载 ARM GCC 工具链安装包rustup target add thumbv7em-none-eabihf
链接器arm-none-eabi-ld(GCC 自带)rust-lld(Rust 自带,基于 LLVM lld)
标准库newlib / picolibccore(Rust 自带,无需额外安装)
构建命令make / IDE 构建cargo build --target thumbv7em-none-eabihf

安装 target#

# 安装交叉编译目标(只需执行一次)
rustup target add thumbv7em-none-eabihf

# 验证安装
rustc --print target-list | grep thumbv7em
# 输出:thumbv7em-none-eabi
#       thumbv7em-none-eabihf
bash

.cargo/config.toml 中设置默认 target#

每次 cargo build 都手动指定 --target 很麻烦。在 .cargo/config.toml 中设置默认值:

# .cargo/config.toml

[build]
target = "thumbv7em-none-eabihf"
toml

这样,直接 cargo build 就会自动使用正确的 target。


3.4 配置工程文件#

.cargo/config.toml:Cargo 的项目级配置#

这个文件是 Rust 嵌入式项目的”中枢配置”:

逐项解释

配置项作用C 的对应
[build] target默认交叉编译目标Makefile 中的 CROSS_COMPILE=arm-none-eabi-
runnercargo run 时执行的命令Keil 的”Download”按钮 / OpenOCD 脚本
rustflags传递给 rustc 的额外参数CFLAGS / LDFLAGS
-Tlink.x使用 cortex-m-rt 提供的链接脚本链接器命令中的 -T STM32F407.ld
-Tdefmt.x使用 defmt 的链接脚本片段无对应(C 中 printf 不需要链接脚本)
DEFMT_LOG日志级别过滤无对应(C 中通常用 #ifdef DEBUG

memory.x:内存布局定义#

/* memory.x - STM32F407VGTx 的内存布局 */

MEMORY
{
    /* 1024KB Flash,起始地址 0x08000000 */
    FLASH : ORIGIN = 0x08000000, LENGTH = 1024K

    /* 128KB SRAM,起始地址 0x20000000 */
    RAM : ORIGIN = 0x20000000, LENGTH = 128K

    /* 64KB CCM RAM(仅 CPU 可访问,DMA 不可达) */
    CCMRAM : ORIGIN = 0x10000000, LENGTH = 64K
}
plaintext

与 C 链接脚本的关系

在 C 工程中,链接脚本(如 STM32F407VGTX_FLASH.ld)通常有几百行,包含 MEMORYSECTIONSENTRY 等完整定义。

在 Rust 中,cortex-m-rt crate 已经提供了一个通用的链接脚本(link.x),其中包含了 SECTIONS 定义(.text.data.bss.vector_table 等)。你只需要提供 memory.x 来告诉它你的芯片有多少 Flash 和 RAM

详细的 memory.x 解析和多 Bank Flash 处理,见第 12 章。

build.rs:构建脚本#

build.rs 是 Cargo 在编译前自动执行的脚本,类似于 Makefile 中的预处理步骤:

它做了什么?

  1. memory.x 复制到构建输出目录
  2. 将该目录加入链接器搜索路径
  3. 这样 cortex-m-rtlink.x 就能通过 INCLUDE memory.x 找到你的内存布局

在 C 中,这些工作由 IDE(Keil/CubeIDE)或 Makefile 隐式完成。在 Rust 中,build.rs 让你可以精确控制构建过程的每一步。


3.5 cortex-m-rt 的简要说明#

它做了什么?#

cortex-m-rt 是 Cortex-M 微控制器的最小运行时。它提供了从”芯片上电”到”你的 main 函数被调用”之间的一切:

芯片上电


┌─────────────────────────────────────────┐
│  cortex-m-rt 提供的 Reset_Handler:      │
│                                         │
│  1. 从向量表读取初始 SP(栈指针)          │
│  2. 将 .data 段从 Flash 复制到 RAM       │
│  3. 将 .bss 段清零                      │
│  4. 调用用户定义的 main 函数              │
│  5. 如果 main 返回,进入死循环            │
└─────────────────────────────────────────┘


你的 #[entry] fn main()
plaintext

与 C 的 startup.s 的对应关系#

在 Rust 中,这整段汇编你都不需要写cortex-m-rt 已经用 Rust + 少量内联汇编实现了完全相同的功能,并且:

  • 向量表是自动生成的(通过 #[interrupt]#[exception] 宏注册)
  • .data 初始化和 .bss 清零是自动完成的
  • 你只需要用 #[entry] 宏标记你的入口函数

cortex-m-rt 的深度解析(向量表生成机制、链接脚本细节、中断注册原理),见第 12 章。


3.6 异常与中断的简要说明#

C 的中断服务函数#

在 C 中,中断服务函数(ISR)通常定义在 stm32f4xx_it.c 中:

// C:中断服务函数
void USART2_IRQHandler(void) {
    if (USART2->SR & USART_SR_RXNE) {
        uint8_t data = USART2->DR;
        // 处理接收数据...
    }
    // 清除中断标志(如果硬件不自动清除)
}

void SysTick_Handler(void) {
    HAL_IncTick();
}
c

这些函数名必须与向量表中的名称完全匹配——这是由链接脚本和启动文件约定的。

Rust 的中断处理:#[interrupt]#[exception]#

与 C 的对比#

方面CRust
函数名约定必须精确匹配(USART2_IRQHandler由宏和 PAC 定义(USART2
注册方式链接脚本中的向量表#[interrupt] 宏自动注册
并发安全无保护(程序员自己管)编译器要求 unsafe 或安全抽象
优先级配置NVIC_SetPriority()NVIC 的 Rust 封装
拼写错误链接通过但中断不触发(静默失败)编译错误(PAC 中找不到该名称)

最后一行是关键区别:在 C 中,如果你把 USART2_IRQHandler 拼错为 USART2_IRQHander,代码会正常编译和链接,但中断永远不会被触发——这是一个极难排查的 bug。在 Rust 中,#[interrupt] 宏会检查名称是否存在于 PAC 中,拼写错误直接编译失败。

优先级配置#

// Rust:配置中断优先级
use cortex_m::peripheral::NVIC;

// 在 #[entry] 中配置
let mut nvic = cp.NVIC;
unsafe {
    nvic.set_priority(stm32f4xx_hal::pac::Interrupt::USART2, 1);
    nvic.set_priority(stm32f4xx_hal::pac::Interrupt::TIM2, 2);
    NVIC::unmask(stm32f4xx_hal::pac::Interrupt::USART2);
}
rust

中断优先级与 Embassy 的抢占式调度的关系,见第 9 章。


3.7 查看编译产物#

编译#

# Debug 编译(快速,不优化)
cargo build

# Release 编译(优化,用于实际部署)
cargo build --release
bash

编译产物位于:

target/thumbv7em-none-eabihf/
├── debug/
│   └── my_first_bare_metal      ← Debug ELF 文件
└── release/
    └── my_first_bare_metal      ← Release ELF 文件
plaintext

查看 Flash 和 RAM 占用:cargo size#

# 安装 cargo-binutils(只需一次)
cargo install cargo-binutils
rustup component add llvm-tools

# 查看各段大小
cargo size --release
bash

输出示例:

   text    data     bss     dec     hex filename
   2847      12    1024    3883     f2b my_first_bare_metal
plaintext
含义存储位置C 的对应
text代码 + 常量Flash.text + .rodata
data有初始值的全局/静态变量Flash → 启动时复制到 RAM.data
bss零初始化的全局/静态变量RAM(启动时清零).bss
dec总大小(text + data + bss)

与 C 的对比

# C:使用 arm-none-eabi-size
arm-none-eabi-size build/my_project.elf
# 输出格式完全相同
bash

反汇编查看:cargo objdump#

# 反汇编整个程序
cargo objdump --release -- --disassemble

# 只看某个函数
cargo objdump --release -- --disassemble --symbol=main

# 查看特定地址范围
cargo objdump --release -- --disassemble --start-address=0x08000200 --stop-address=0x08000300
bash

输出示例(LED 闪烁的核心循环):

08000200 :
  8000200: push {r7, lr}
  8000202: mov  r7, sp
  8000204: bl   #0x8000240    ; 初始化 GPIO
  8000208: bl   #0x8000260    ; 设置高电平
  800020c: bl   #0x8000280    ; 延时
  8000210: bl   #0x80002a0    ; 设置低电平
  8000214: bl   #0x8000280    ; 延时
  8000218: b    #-0x14        ; 跳回 0x8000208(循环)
asm

与 C 的对比

# C:使用 arm-none-eabi-objdump
arm-none-eabi-objdump -d build/my_project.elf
bash

命令格式几乎相同——因为底层都是 LLVM/ELF 工具链。

生成 HEX / BIN 文件#

如果你需要将固件交给生产部门烧录:

# 生成 Intel HEX 格式
cargo objcopy --release -- -O ihex firmware.hex

# 生成原始二进制格式
cargo objcopy --release -- -O binary firmware.bin
bash

这与 C 中 arm-none-eabi-objcopy -O ihex / -O binary 完全对应。


3.8 烧录与 RTT 输出#

probe-rs:新一代调试探针工具#

probe-rs 是用 Rust 编写的嵌入式调试工具集,它一个工具替代了 C 工具链中的三个工具

C 工具链probe-rs 对应功能
OpenOCDprobe-rs(核心库)与调试探针通信(SWD/JTAG)
GDBprobe-rs(VSCode 插件)断点、单步、变量查看
SEGGER RTT Viewerprobe-rs run实时日志输出
ST-Link Utility / STM32CubeProgrammerprobe-rs flash烧录固件

支持的调试探针

  • ST-Link(V2/V2.1/V3)——STM32 开发板自带
  • J-Link(SEGGER)
  • CMSIS-DAP / DAPLink
  • ESP-Prog(乐鑫)

安装 probe-rs#

cargo run:一键编译 + 烧录 + 日志#

这是 Rust 嵌入式开发最优雅的体验——一条命令完成所有操作

cargo run --release
bash

这条命令做了什么?

cargo run --release

    ├── 1. 编译项目(rustc + linker)

    ├── 2. 调用 runner(.cargo/config.toml 中配置的)
    │       └── probe-rs run --chip STM32F407VGTx

    ├── 3. probe-rs 通过 SWD 连接芯片

    ├── 4. 擦除 Flash + 烧录固件

    ├── 5. 复位芯片,程序开始运行

    └── 6. 通过 RTT 实时输出日志到终端
plaintext

终端输出示例:

     Running `probe-rs run --chip STM32F407VGTx target/thumbv7em-none-eabihf/release/my_first_bare_metal`
      Erasing sectors ✔ [00:00:00] [#################################] 16.00 KiB/16.00 KiB @ 54.23 KiB/s
  Programming pages   ✔ [00:00:00] [#################################] 16.00 KiB/16.00 KiB @ 23.11 KiB/s
    Finished in 0.789s
 INFO  Hello from Rust! (src/main.rs:15)
 INFO  Counter: 0 (src/main.rs:22)
 INFO  Counter: 1 (src/main.rs:22)
 INFO  Counter: 2 (src/main.rs:22)
^C
plaintext

对比 C 的工作流

C 的工作流(至少 3 步):
1. make / IDE 编译          → 生成 .elf
2. OpenOCD 烧录             → 需要单独的配置文件和命令
3. 打开串口终端 / RTT Viewer → 需要另一个窗口

Rust 的工作流(1 步):
1. cargo run               → 编译 + 烧录 + 日志,全部完成
plaintext

RTT(Real-Time Transfer)原理#

RTT 是 SEGGER 发明的调试数据传输技术,现在已成为嵌入式调试的事实标准。probe-rs 完整实现了 RTT 协议。

核心思想:利用调试接口(SWD/JTAG)的内存访问能力,在芯片 RAM 中开辟一块”共享内存区域”,主机通过调试接口直接读写这块内存。

RTT 的优势

特性RTT串口(UART)
需要额外引脚❌ 不需要(复用调试口)✅ 需要 TX/RX
传输速度~1 MB/s通常 115200 bps(~11 KB/s)
对实时性影响极小(写入 RAM 缓冲区)较大(等待 UART 发送完成)
双向通信✅ 支持✅ 支持
需要额外硬件❌ 只需调试器✅ 需要 USB-TTL 模块
量产时可用❌ 需要调试器连接✅ 可以

注意:RTT 是调试工具,不是产品通信接口。量产产品中如果需要日志,仍然使用 UART 或 SWO。

与 C 的 OpenOCD + GDB 流程对比#

步骤C 工具链Rust + probe-rs
编译make / IDE 构建cargo build
烧录openocd -f board/stm32f4discovery.cfg -c "program build/main.elf verify reset exit"probe-rs run --chip STM32F407VGTx(或 cargo run
调试启动 OpenOCD → 启动 GDB → target remote :3333loadbreak maincontinueVSCode 中按 F5(probe-rs 插件)
日志打开串口终端(PuTTY / minicom)自动输出到 cargo run 的终端
配置文件openocd.cfg(需要知道芯片型号、接口类型、频率)只需 --chip STM32F407VGTx

probe-rs 的最大优势:它内置了所有主流芯片的 Flash 算法和内存映射,不需要你手动编写配置文件。


3.9 工具链层次全景图#

从源码到芯片的完整链路#

各层工具的职责#

层次工具职责可替换性
编译器前端rustcRust → LLVM IR不可替换
编译器后端LLVM优化 + 代码生成不可替换(rustc 内置)
链接器rust-lld合并目标文件、应用链接脚本可替换为 arm-none-eabi-ld
烧录/调试probe-rsFlash 编程、调试、RTT可替换为 OpenOCD + GDB
日志defmt + defmt-rtt高效日志传输可替换为 UART printf
运行时cortex-m-rt启动代码、向量表可替换为 riscv-rt(RISC-V)

每一层的详细工作原理,见第 12 章。


3.10 Rust 库生态与使用方式#

crates.io:Rust 的包管理器#

crates.io 是 Rust 官方的包注册中心(类似于 C 世界中”所有开源库的集合”),截至 2026 年已有超过 17 万个 crate。

# 搜索 crate
cargo search embassy
# 输出:
# embassy-executor = "0.7.0"    # Async executor for embedded systems
# embassy-time = "0.4.0"        # Timer and timing primitives
# embassy-stm32 = "0.2.0"       # STM32 HAL for Embassy
# embassy-sync = "0.6.0"        # Synchronization primitives

# 添加依赖(自动修改 Cargo.toml)
cargo add cortex-m-rt
cargo add defmt
cargo add embassy-stm32 --features stm32f407vg
bash

与 C 的”手动下载源码 + 加入工程”对比#

方面C 的做法Rust 的做法
获取库从 GitHub 下载 ZIP / git clonecargo add <crate>
版本管理手动记录版本号 / git submoduleCargo.toml 中声明,Cargo.lock 锁定
依赖解析手动处理(A 依赖 B,B 依赖 C…)Cargo 自动解析整棵依赖树
编译集成手动添加到 Makefile / IDE 工程自动编译和链接
更新手动下载新版本、处理 API 变更cargo update(受 semver 约束)
文档README + 源码注释cargo doc --open(自动生成 HTML 文档)
示例通常没有 / 散落在 wiki很多 crate 有 examples/ 目录

版本管理与 Cargo.lock#

# Cargo.toml 中的版本声明(语义化版本)
[dependencies]
cortex-m-rt = "0.7"      # 兼容 0.7.x 的任何版本
defmt = "0.3"            # 兼容 0.3.x 的任何版本
toml
# Cargo.lock(自动生成,精确锁定版本)

name = "cortex-m-rt"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abc123..."


name = "defmt"
version = "0.3.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "def456..."
toml

最佳实践

  • Cargo.toml 提交到版本控制(声明”我需要什么”)
  • Cargo.lock 也提交到版本控制(锁定”我实际用了什么版本”)
  • 这确保了团队中每个人、CI 服务器、生产构建都使用完全相同的依赖版本

嵌入式常用的 crate 一览#

Crate功能成熟度
cortex-m / cortex-m-rtCortex-M 核心支持⭐⭐⭐⭐⭐ 非常成熟
embassy-executor异步任务执行器⭐⭐⭐⭐⭐ 生产级
embassy-time异步定时器⭐⭐⭐⭐⭐ 生产级
embassy-sync任务间通信⭐⭐⭐⭐⭐ 生产级
embassy-stm32STM32 HAL⭐⭐⭐⭐ 覆盖广,持续完善
defmt / defmt-rtt日志框架⭐⭐⭐⭐⭐ 生产级
heapless无堆数据结构⭐⭐⭐⭐⭐ 非常成熟
critical-section临界区抽象⭐⭐⭐⭐⭐ 非常成熟
embedded-hal硬件抽象接口⭐⭐⭐⭐⭐ 1.0 已稳定
probe-rs调试/烧录工具⭐⭐⭐⭐⭐ 生产级

3.11 本章小结#

完整的最小裸机工程#

让我们把本章的所有内容汇总为一个完整的最小工程:

文件结构

hello_bare_metal/
├── Cargo.toml
├── memory.x
├── build.rs
├── .cargo/
│   └── config.toml
└── src/
    └── main.rs
plaintext

Cargo.toml

.cargo/config.toml

[build]
target = "thumbv7em-none-eabihf"

[target.thumbv7em-none-eabihf]
runner = "probe-rs run --chip STM32F407VGTx"
rustflags = [
    "-C", "link-arg=-Tlink.x",
    "-C", "link-arg=-Tdefmt.x",
]

[env]
DEFMT_LOG = "debug"
toml

memory.x

MEMORY
{
    FLASH : ORIGIN = 0x08000000, LENGTH = 1024K
    RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
plaintext

build.rs

use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;

fn main() {
    println!("cargo:rerun-if-changed=memory.x");
    let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
    File::create(out.join("memory.x"))
        .unwrap()
        .write_all(include_bytes!("memory.x"))
        .unwrap();
    println!("cargo:rustc-link-search={}", out.display());
}
rust

src/main.rs

运行#

# 一条命令:编译 + 烧录 + 运行 + 日志
cargo run --release
bash

本章要点回顾#

  1. Rust 裸机工程比 C 简洁得多:不需要手写启动汇编、不需要手动管理库、不需要复杂的构建脚本
  2. cortex-m-rt 替代了 startup.s:向量表、.data 初始化、.bss 清零全部自动完成
  3. target triple 指定编译目标thumbv7em-none-eabihf = Cortex-M4/M7 + 硬件 FPU + 裸机
  4. probe-rs 一个工具替代 OpenOCD + GDB + RTT Viewercargo run 一键完成编译+烧录+日志
  5. Cargo 是包管理器 + 构建系统Cargo.toml 声明依赖,cargo build 自动下载、编译、链接
  6. Feature Flag 是类型安全的 #ifdef:由 Cargo 管理,支持依赖传递和冲突检测

下一步#

在第 4 章中,我们将深入探讨 Rust 最让 C 工程师”又爱又恨”的特性——零成本抽象。你会看到:

  • GPIO 的类型状态模式如何在编译期阻止非法操作
  • 反汇编对比证明 Rust 的抽象层不产生任何额外机器码
  • unsafe 在嵌入式中的正确角色
  • 单例模式如何保证”一个芯片只有一个 GPIOA”

这些概念是理解后续 Embassy HAL 设计的基础。


下一章:第 4 章——零成本抽象:当类型系统遇见寄存器。