本篇文章將介紹如何使用GDB命令調(diào)試Rust嵌入式代碼,以國(guó)產(chǎn)的arm單片機(jī)py32
為例。開發(fā)環(huán)境為Mac下,使用Jlink作為下載和調(diào)試工具。
原理
我們調(diào)試單片機(jī)的主要用到該單片機(jī)編譯工具鏈的gdb工具arm-none-eabi-gdb
。與本地gdb工具調(diào)試不一樣的是,嵌入式gdb工具的目標(biāo)通常在遠(yuǎn)端的嵌入式主板上而非本地主機(jī),因此需要一個(gè)調(diào)試器來連接嵌入式主板和主機(jī),通常可以使用jlink,stlink,cmsis-cap等工具提供服務(wù),通過調(diào)試工具將固件燒錄到硬件后,再與gdb程序進(jìn)行通信,解析調(diào)試命令,控制遠(yuǎn)程硬件CPU。
測(cè)試程序
#![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 {
// 延時(shí) 5s
defmt::info!("repeat...{} ", cnt);
let _ = counter.start(5u32.secs());
let _ = counter.wait();
cnt += 1;
}
}
開啟GDB
在Embed.toml
文件配置中開啟GDB服務(wù)
[default.general]
chip = "PY32F030x8"
[default.rtt]
enabled = false
[default.gdb]
enabled = true
[default.reset]
halt_afterwards = true
執(zhí)行cargo embed
命令燒錄代碼以及自動(dòng)開啟GDB服務(wù),默認(rèn)端口1337
,保持該命令后臺(tái)繼續(xù)運(yùn)行。
gdb調(diào)試
在其他終端中執(zhí)行命令
arm-none-eabi-gdb target/thumbv6m-none-eabi/debug/examples/advanced_timer_block_2
連接GDB服務(wù)器target remote:1337
即可連接單片機(jī),可查看CPU信息。
如查看arm寄存器:info registers
查看匯編代碼disassemble
可以在匯編文件中看到代碼的起點(diǎn)是cortex_m_rt::DefaultPreInit
函數(shù)。然后是進(jìn)入Reset
,然后才進(jìn)入main
函數(shù)。
通過命令step
單步執(zhí)行,可以在命令disassemble
的左側(cè)箭頭查看當(dāng)前運(yùn)行的地址。
為了快速執(zhí)行到main
函數(shù),可以用斷點(diǎn)命令b main
打斷點(diǎn),然后使用continue
命令直接運(yùn)行到斷點(diǎn)處。
由于匯編后的函數(shù)和變量名可能被重新命令,可以使用命令set print asm-demangle on
,讓匯編中的函數(shù)和變量使用原代碼中的命名。
可以使用list
命令查看當(dāng)前代碼執(zhí)行的情況,使用break N
命令添加指定行的斷點(diǎn)
使用info locals
命令查看當(dāng)前的局部變量,使用print
命令打印變量的值