常见问题解答

这里是一些未排序的常见问题及解答。

如果你在聊天中得到了问题的答案,欢迎随时在 此页面 添加内容!

如何在没有 debugging probe 的情况下部署到 RP2040

安装 elf2uf2-rs,用于将生成的 elf 二进制文件转换为 uf2 文件。

配置 runner 使用该工具,在 .cargo/config.toml 中添加如下内容:

[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "elf2uf2-rs --deploy --serial --verbose"

命令行参数 --deploy 会自动检测你的设备并上传二进制文件,--serial 会启动串口连接。更多信息请查阅文档。

缺少 main 宏

如果你看到如下错误:

#[embassy_executor::main]
|                   ^^^^ could not find `main` in `embassy_executor`

你很可能缺少了 embassy-executor crate 的某些特性。

对于 Cortex-M 目标,请确保在 Cargo.tomlembassy-executor crate 中启用了以下所有特性:

  • arch-cortex-m

  • executor-thread

对于 ESP32,请考虑使用对应 HAL crate 提供的 executors 和 #[main] 宏。

为什么我的二进制文件这么大?

管理二进制体积的第一步是设置 profiles

[profile.release]
lto = true
opt-level = "s"
incremental = false
codegen-units = 1
# 注意:设置 debug = true 没问题,调试信息不会被烧录到设备!
debug = true

这些参数的详细说明见上面链接的 Rust Book 页面。

我的二进制依然很大,充满了 std::fmt 相关内容!

这说明你的代码足够复杂,panic! 的格式化需求没有被优化掉,即使你用了 panic-haltpanic-reset

你可以在 .cargo/config.toml 中添加如下内容来解决:

[unstable]
build-std = ["core"]
build-std-features = ["panic_immediate_abort"]

这会将所有 panic 替换为 UDF(未定义)指令。

具体行为取决于芯片型号。

请查阅芯片手册,对于 thumbv6m,这会导致 hardfault。可以这样配置:

#[exception]
unsafe fn HardFault(_frame: &ExceptionFrame) -> ! {
    SCB::sys_reset() // 你也可以做其他操作
}

更多信息请查阅 cortex-m 的 异常处理

embassy-time 报链接错误

如果你看到如下链接错误:

  = note: rust-lld: error: undefined symbol: _embassy_time_now
          >>> referenced by driver.rs:127 (src/driver.rs:127)
          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::now::hefb1f99d6e069842) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib

          rust-lld: error: undefined symbol: _embassy_time_allocate_alarm
          >>> referenced by driver.rs:134 (src/driver.rs:134)
          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::allocate_alarm::hf5145b6bd46706b2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib

          rust-lld: error: undefined symbol: _embassy_time_set_alarm_callback
          >>> referenced by driver.rs:139 (src/driver.rs:139)
          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm_callback::h24f92388d96eafd2) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib

          rust-lld: error: undefined symbol: _embassy_time_set_alarm
          >>> referenced by driver.rs:144 (src/driver.rs:144)
          >>>               embassy_time-846f66f1620ad42c.embassy_time.4f6a638abb75dd4c-cgu.0.rcgu.o:(embassy_time::driver::set_alarm::h530a5b1f444a6d5b) in archive Devel/Embedded/pogodyna/target/thumbv7em-none-eabihf/debug/deps/libembassy_time-846f66f1620ad42c.rlib

你可能需要为 HAL 启用时间驱动(不是在 embassy-time 里!)。比如 embassy-stm32,你需要启用 time-driver-any

[dependencies.embassy-stm32]
version = "0.1.0"
features = [
    # ...
    "time-driver-any", # 添加这一行!
    # ...
]

如果你在项目早期阶段还没用到 HAL 的任何内容,请确保 HAL 被显式使用,防止链接器将其当作无用代码移除,在源码中加上:

use embassy_stm32 as _;

你的依赖树中有多个版本的同一个 crate。这意味着有些 embassy crate 来自 crates.io,有些来自 git,导致依赖不一致。

解决方法:确保所有 embassy crate 都来自同一个源! 你可以用 [patch.crates.io] 和(如有需要)[patch.'https://github.com/embassy-rs/embassy.git'] 来统一依赖。

示例:

[patch.crates-io]
embassy-time-queue-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
embassy-time-driver = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }
# embassy-time = { git = "https://github.com/embassy-rs/embassy.git", rev = "e5fdd35" }

注意 git 版本号要和你用的其他 embassy 依赖一致!

如何优化我的 embassy-stm32 程序的速度?

  • 确保 RCC 设置为最高速

  • 确保启用了 flash cache

  • --release 构建

  • Cargo.toml 的 release 配置中设置:

    • opt-level = "s"

    • lto = "fat"

  • .cargo/config.toml[unstable] 部分设置:

    • build-std = ["core"]

    • build-std-features = ["panic_immediate_abort"]

  • 启用特性 embassy-time/generic-queue,禁用 embassy-executor/integrated-timers

  • 使用 InterruptExecutor 时:

    • 禁用 executor-thread

    • main 启动所有任务,然后启用 SCB.SLEEPONEXITloop { cortex_m::asm::wfi() }

    • 注意:如果需要 2 个优先级,建议用 2 个中断执行器,而不是 1 个线程执行器 + 1 个中断执行器。

如何在 stable 上配置任务 arena?

未启用 embassy-executornightly 特性时,执行器使用 bump allocator,可能需要配置。

如果任务 arena 太大编译时会报错:

rust-lld: error: section '.bss' will not fit in region 'RAM': overflowed by _ bytes
rust-lld: error: section '.uninit' will not fit in region 'RAM': overflowed by _ bytes

如果任务 arena 太小运行时会报错:

ERROR panicked at 'embassy-executor: task arena is full. You must increase the arena size, see the documentation for details: https://docs.embassy.dev/embassy-executor/'

注意:如果所有任务都在启动时被 spawn,这个 panic 会立刻发生。

更多细节见 任务 Arena 文档

可以和 Embassy 一起用手写 ISR 吗?

可以!如果你需要尽可能快地响应事件,而常规的“ISR、唤醒、从 ISR 返回、上下文切换到被唤醒任务”流程延迟太大,可以像 其他嵌入式 Rust 项目 一样,直接定义 #[interrupt] fn INTERRUPT_NAME() {} 处理函数。

如何测量资源使用(CPU、RAM 等)?

CPU 使用率

有几种方法,通常是测量在 idle 或低优先级循环中花费的时间。

我们还需要补充 Embassy 的具体做法,这篇旧文 介绍了通用流程。

如果你有实践经验,欢迎补充具体示例!

静态内存使用

cargo sizecargo nm 等工具可以查看全局或静态变量的大小。重点关注 .data.bss 段,这两者加起来就是全局/静态内存用量。

最大栈使用

可用 cargo-call-stack 静态分析最坏情况栈用量。该工具有一些已知限制,详情见 README