GDB 调试 Rust 嵌入式程序-以国产单片机为例

本篇文章将介绍如何使用GDB命令调试Rust嵌入式代码,以国产的arm单片机py32为例。开发环境为Mac下,使用Jlink作为下载和调试工具。

原理

我们调试单片机的主要用到该单片机编译工具链的gdb工具arm-none-eabi-gdb。与本地gdb工具调试不一样的是,嵌入式gdb工具的目标通常在远端的嵌入式主板上而非本地主机,因此需要一个调试器来连接嵌入式主板和主机,通常可以使用jlink,stlink,cmsis-cap等工具提供服务,通过调试工具将固件烧录到硬件后,再与gdb程序进行通信,解析调试命令,控制远程硬件CPU。

测试程序

#![no_std]
#![no_main]

use embedded_hal::timer::CountDown;
use fugit::ExtU32;
use hal::mode::Blocking;
use hal::timer::advanced_timer::AnyTimer;
use py32f030_hal as hal;

use {defmt_rtt as _, panic_probe as _};

#[cortex_m_rt::entry]
fn main() -> ! {
    defmt::info!("timer counter examples start...");
    let p = hal::init(Default::default());

    let timer = AnyTimer::<_, Blocking>::new(p.TIM1).unwrap();
    let mut counter = timer.as_counter();

    let mut cnt = 0;
    loop {
        // 延时 5s
        defmt::info!("repeat...{} ", cnt);
        let _ = counter.start(5u32.secs());
        let _ = counter.wait();
        cnt += 1;
    }
}

开启GDB

Embed.toml文件配置中开启GDB服务

[default.general]        
chip = "PY32F030x8"        

[default.rtt]        
enabled = false        

[default.gdb]        
enabled = true        

[default.reset]        
halt_afterwards = true        

执行cargo embed命令烧录代码以及自动开启GDB服务,默认端口1337,保持该命令后台继续运行。

gdb调试

在其他终端中执行命令

arm-none-eabi-gdb target/thumbv6m-none-eabi/debug/examples/advanced_timer_block_2

连接GDB服务器target remote:1337即可连接单片机,可查看CPU信息。

如查看arm寄存器:info registers

查看汇编代码disassemble

可以在汇编文件中看到代码的起点是cortex_m_rt::DefaultPreInit函数。然后是进入Reset,然后才进入main函数。

通过命令step单步执行,可以在命令disassemble的左侧箭头查看当前运行的地址。

为了快速执行到main函数,可以用断点命令b main打断点,然后使用continue命令直接运行到断点处。

由于汇编后的函数和变量名可能被重新命令,可以使用命令set print asm-demangle on,让汇编中的函数和变量使用原代码中的命名。

可以使用list命令查看当前代码执行的情况,使用break N命令添加指定行的断点

使用info locals命令查看当前的局部变量,使用print命令打印变量的值



请使用浏览器的分享功能分享到微信等