硬件
到目前为止,您应该对工具和开发过程有所了解。在本节中,我们将切换到真实的硬件;该过程将基本保持不变。让我们深入了解一下。
了解您的硬件
在开始之前,您需要确定目标设备的一些特性,因为这些特性将用于配置项目
-
ARM 内核。例如,Cortex-M3。
-
ARM 内核是否包含 FPU?Cortex-M4F 和 Cortex-M7F 内核包含。
-
目标设备具有多少闪存和 RAM?例如,256 KiB 闪存和 32 KiB RAM。
-
闪存和 RAM 在地址空间中映射到哪里?例如,RAM 通常位于地址
0x2000_0000
。
您可以在设备的数据手册或参考手册中找到此信息。
在本节中,我们将使用我们的参考硬件 STM32F3DISCOVERY。该板包含一个 STM32F303VCT6 微控制器。该微控制器具有
-
一个包含单精度 FPU 的 Cortex-M4F 内核
-
位于地址 0x0800_0000 的 256 KiB 闪存。
-
位于地址 0x2000_0000 的 40 KiB RAM。(还有一个 RAM 区域,但为简单起见,我们将忽略它)。
配置
我们将从一个新的模板实例开始。有关如何不使用 cargo-generate
执行此操作的复习,请参阅 有关 QEMU 的上一节。
$ cargo generate --git https://github.com/rust-embedded/cortex-m-quickstart
Project Name: app
Creating project called `app`...
Done! New project created /tmp/app
$ cd app
第一步是在 .cargo/config.toml
中设置默认编译目标。
tail -n5 .cargo/config.toml
# Pick ONE of these compilation targets
# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
# target = "thumbv7m-none-eabi" # Cortex-M3
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
我们将使用 thumbv7em-none-eabihf
,因为它涵盖了 Cortex-M4F 内核。
注意:您可能还记得上一章,我们必须安装所有目标,这是一个新的目标。因此,不要忘记为该目标运行安装过程
rustup target add thumbv7em-none-eabihf
。
第二步是将内存区域信息输入 memory.x
文件。
$ cat memory.x
/* Linker script for the STM32F303VCT6 */
MEMORY
{
/* NOTE 1 K = 1 KiBi = 1024 bytes */
FLASH : ORIGIN = 0x08000000, LENGTH = 256K
RAM : ORIGIN = 0x20000000, LENGTH = 40K
}
注意:如果您出于某种原因在对特定构建目标进行首次构建后更改了
memory.x
文件,则在cargo build
之前执行cargo clean
,因为cargo build
可能不会跟踪memory.x
的更新。
我们将再次从 hello 示例开始,但首先我们必须进行一个小小的更改。
在 examples/hello.rs
中,确保 debug::exit()
调用已注释掉或删除。它仅用于在 QEMU 中运行。
#[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 {}
}
您现在可以使用 cargo build
交叉编译程序,并使用 cargo-binutils
检查二进制文件,就像以前一样。cortex-m-rt
crate 处理让您的芯片运行所需的所有魔法,因为,很方便的是,几乎所有 Cortex-M CPU 都以相同的方式启动。
cargo build --example hello
调试
调试看起来会有些不同。实际上,第一步可能看起来不同,具体取决于目标设备。在本节中,我们将展示在 STM32F3DISCOVERY 上运行的程序所需的调试步骤。这旨在作为参考;有关调试的设备特定信息,请查看 Debugonomicon。
与以前一样,我们将进行远程调试,客户端将是 GDB 进程。但是,这次服务器将是 OpenOCD。
与在 验证 部分中所做的一样,将发现板连接到您的笔记本电脑/PC,并检查 ST-LINK 标头是否已填充。
在终端上运行 openocd
以连接到发现板上的 ST-LINK。从模板的根目录运行此命令;openocd
将拾取 openocd.cfg
文件,该文件指示要使用哪个接口文件和目标文件。
cat openocd.cfg
# 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)
# source [find interface/stlink-v2.cfg]
source [find target/stm32f3x.cfg]
注意:如果您在 验证 部分中发现您有一个旧版本的发现板,那么您应该在此时修改
openocd.cfg
文件以使用interface/stlink-v2.cfg
。
$ 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 <transport>'.
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
在另一个终端上,从模板的根目录运行 GDB。
gdb-multiarch -q target/thumbv7em-none-eabihf/debug/examples/hello
注意:与以前一样,您可能需要使用其他版本的 gdb 而不是 gdb-multiarch
,具体取决于您在安装章节中安装了哪个版本。这也可以是 arm-none-eabi-gdb
或只是 gdb
。
接下来,将 GDB 连接到 OpenOCD,OpenOCD 正在端口 3333 上等待 TCP 连接。
(gdb) target remote :3333
Remote debugging using :3333
0x00000000 in ?? ()
现在使用 load
命令将程序闪存(加载)到微控制器上。
(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.
程序现在已加载。该程序使用半主机,因此在我们进行任何半主机调用之前,我们必须告诉 OpenOCD 启用半主机。您可以使用 monitor
命令向 OpenOCD 发送命令。
(gdb) monitor arm semihosting enable
semihosting is enabled
您可以通过调用
monitor help
命令查看所有 OpenOCD 命令。
与以前一样,我们可以使用断点和 continue
命令跳过所有内容,直到 main
。
(gdb) break main
Breakpoint 1 at 0x8000490: file examples/hello.rs, line 11.
Note: automatically using hardware breakpoints for read-only addresses.
(gdb) continue
Continuing.
Breakpoint 1, hello::__cortex_m_rt_main_trampoline () at examples/hello.rs:11
11 #[entry]
注意:如果您在发出上面的
continue
命令后,GDB 阻塞了终端而不是命中断点,您可能需要仔细检查memory.x
文件中的内存区域信息是否正确设置了您的设备(包括开始和长度)。
使用 step
进入 main 函数。
(gdb) step
halted: PC: 0x08000496
hello::__cortex_m_rt_main () at examples/hello.rs:13
13 hprintln!("Hello, world!").unwrap();
在使用 next
推进程序后,您应该在 OpenOCD 控制台中看到“Hello, world!”以及其他内容。
$ 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
该消息仅显示一次,因为程序即将进入第 19 行中定义的无限循环:loop {}
您现在可以使用 quit
命令退出 GDB。
(gdb) quit
A debugging session is active.
Inferior 1 [Remote target] will be detached.
Quit anyway? (y or n)
调试现在需要更多步骤,因此我们将所有这些步骤打包到一个名为 openocd.gdb
的单个 GDB 脚本中。该文件是在 cargo generate
步骤中创建的,应该无需任何修改即可使用。让我们看一看
cat openocd.gdb
target extended-remote :3333
# print demangled symbols
set print asm-demangle on
# detect unhandled exceptions, hard faults and panics
break DefaultHandler
break HardFault
break rust_begin_unwind
monitor arm semihosting enable
load
# start the process but immediately halt the processor
stepi
现在运行 <gdb> -x openocd.gdb target/thumbv7em-none-eabihf/debug/examples/hello
将立即将 GDB 连接到 OpenOCD,启用半主机,加载程序并启动进程。
或者,您可以将 <gdb> -x openocd.gdb
变成一个自定义运行器,以使 cargo run
构建程序并启动 GDB 会话。此运行器包含在 .cargo/config.toml
中,但已注释掉。
head -n10 .cargo/config.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"
[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"
$ 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)