半主机

半主机是一种机制,它允许嵌入式设备在主机上执行 I/O,主要用于将消息记录到主机控制台。半主机需要调试会话,几乎不需要其他任何东西(无需额外的线缆!),因此使用起来非常方便。缺点是它非常慢:每个写入操作可能需要几毫秒,具体取决于您使用的硬件调试器(例如 ST-Link)。

cortex-m-semihosting 箱提供了在 Cortex-M 设备上执行半主机操作的 API。下面的程序是“Hello, world!”的半主机版本。

#![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 {}
}

如果您在硬件上运行此程序,您将在 OpenOCD 日志中看到“Hello, world!”消息。

$ openocd
(..)
Hello, world!
(..)

您需要先从 GDB 中的 OpenOCD 中启用半主机。

(gdb) monitor arm semihosting enable
semihosting is enabled

QEMU 理解半主机操作,因此上述程序也可以与 qemu-system-arm 一起使用,无需启动调试会话。请注意,您需要将 -semihosting-config 标志传递给 QEMU 以启用半主机支持;这些标志已包含在模板的 .cargo/config.toml 文件中。

$ # this program will block the terminal
$ cargo run
     Running `qemu-system-arm (..)
Hello, world!

还有一个 exit 半主机操作,可用于终止 QEMU 进程。重要提示:在硬件上不要使用 debug::exit;此函数可能会损坏您的 OpenOCD 会话,您将无法调试更多程序,直到您重新启动它。

#![no_main]
#![no_std]

use panic_halt as _;

use cortex_m_rt::entry;
use cortex_m_semihosting::debug;

#[entry]
fn main() -> ! {
    let roses = "blue";

    if roses == "red" {
        debug::exit(debug::EXIT_SUCCESS);
    } else {
        debug::exit(debug::EXIT_FAILURE);
    }

    loop {}
}
$ cargo run
     Running `qemu-system-arm (..)

$ echo $?
1

最后一个提示:您可以将惊慌行为设置为 exit(EXIT_FAILURE)。这将允许您编写可以在 QEMU 上运行的 no_std 运行通过测试。

为了方便起见,panic-semihosting 箱有一个“exit”功能,当启用时,在将惊慌消息记录到主机 stderr 后调用 exit(EXIT_FAILURE)

#![no_main]
#![no_std]

use panic_semihosting as _; // features = ["exit"]

use cortex_m_rt::entry;
use cortex_m_semihosting::debug;

#[entry]
fn main() -> ! {
    let roses = "blue";

    assert_eq!(roses, "red");

    loop {}
}
$ cargo run
     Running `qemu-system-arm (..)
panicked at 'assertion failed: `(left == right)`
  left: `"blue"`,
 right: `"red"`', examples/hello.rs:15:5

$ echo $?
1

注意:要在 panic-semihosting 上启用此功能,请编辑您的 Cargo.toml 依赖项部分,其中 panic-semihosting 使用以下内容指定:

panic-semihosting = { version = "VERSION", features = ["exit"] }

其中 VERSION 是所需的版本。有关依赖项功能的更多信息,请查看 Cargo 手册的 指定依赖项 部分。