2. JTAG基础

说实话,很多工程师拿到一块新板子,第一件事就是急着点灯、跑系统。但我个人的经验是——先把调试工具链跑通,后面能省下大把时间。你想想看,如果连GDB都连不上目标,出了Bug你只能靠猜,那多痛苦。

这一节,我们就来把OpenOCD、GDB、调试脚本、硬件连接这四样东西,一个一个搞定。

1. OpenOCD配置——让调试器听懂你的话

OpenOCD(Open On-Chip Debugger)是个好东西。它把上位机的GDB命令,翻译成JTAG/SWD时序,发给目标芯片。说白了,它就是个翻译官。

配置OpenOCD,核心就两件事:

  • 接口配置:告诉它你用的是什么调试器(J-Link、ST-Link、CMSIS-DAP等)
  • 目标配置:告诉它你要调试什么芯片(STM32F4、i.MX RT、RISC-V等)

我习惯把配置拆成两个文件,这样复用性高。比如:

# interface.cfg —— 调试器配置
source [find interface/jlink.cfg]
adapter speed 1000
transport select jtag

# target.cfg —— 目标芯片配置
source [find target/stm32f4x.cfg]
reset_config srst_only srst_nogate

然后启动OpenOCD:

openocd -f interface.cfg -f target.cfg

嗯,这里要注意:adapter speed别设太高。我遇到过一上来设10MHz,结果线缆太长信号反射,连都连不上。一般1000kHz起步,稳定了再往上加。

小技巧: 如果你用的是J-Link,可以在OpenOCD里加一句 jlink serial <你的序列号>,避免同时插多个J-Link时连错设备。我吃过这个亏,调试了一下午发现连的是隔壁工位的板子……

2. GDB连接目标——打通上位机到芯片的通道

OpenOCD跑起来后,默认在本地3333端口开了一个GDB服务。这时候,GDB就可以连上去了。

连接命令很简单:

arm-none-eabi-gdb your_firmware.elf
(gdb) target remote localhost:3333

连上之后,我习惯先做几件事验证:

  1. monitor reset halt —— 复位并暂停CPU
  2. info registers —— 看看寄存器值是不是预期的复位值
  3. load —— 把固件下载到Flash

如果info registers出来全是0xFFFFFFFF或者0x00000000,那八成是硬件连接有问题。别急着怀疑OpenOCD配置,先拿万用表量量JTAG/SWD的引脚电平。

注意: 有些芯片默认JTAG引脚被复用成GPIO了。比如STM32F4的PB3/PB4,如果代码里把它们配成了普通IO,那第二次烧录就进不去了。解决办法是:按住复位键,在OpenOCD里敲 reset halt 的瞬间松手。我管这叫「盲连大法」。

3. 调试脚本编写——把重复劳动自动化

每次调试都敲一遍target remote、monitor reset halt、load,你不烦吗?反正我烦了。所以,写个GDB脚本吧。

一个典型的调试脚本长这样:

# debug.gdb
target remote localhost:3333
monitor reset halt
monitor flash write_image erase your_firmware.elf
load
break main
continue

启动时直接:

arm-none-eabi-gdb -x debug.gdb your_firmware.elf

一键连上、下载、停在main函数入口。省心。

我个人还喜欢在脚本里加一些「调试辅助函数」。比如:

define dump_stack
    info registers sp
    x/32wx $sp
end
document dump_stack
    打印当前栈顶的32个字
end

这样在调试现场,敲个dump_stack就能看栈回溯,不用每次都敲一长串命令。

核心思路: 调试脚本不是写给别人看的,是写给自己用的。把高频操作封装成短命令,能让你在跟踪Bug时保持思路连贯,不被命令行打断。

4. 硬件连接验证——别让物理层坑了你

软件配得再好,硬件没连对,一切都是白搭。我见过太多人花一整天调OpenOCD,最后发现是杜邦线松了。

JTAG最少需要4根线:

信号 说明 常见坑点
TMS 模式选择 上拉电阻没接,浮空导致状态机跑飞
TCK 时钟 线太长(>20cm)信号反射,降速可解
TDI 数据输入 与TDO接反,数据流不通
TDO 数据输出 同上,接反了OpenOCD会报invalid IDCODE

我的验证流程是这样的:

  1. 目测:检查排线有没有插反、插歪、插错孔位
  2. 万用表测通断:调试器端的TMS是不是真的连到了芯片的TMS引脚
  3. 测电平:TCK上有没有时钟?TMS在复位后是不是高电平?
  4. 跑OpenOCD:看能不能读到IDCODE

如果OpenOCD报JTAG scan chain interrogation failed,别慌。先检查transport select是不是选对了——JTAG和SWD是两种协议,别混用。

避坑指南: 我曾经在一块国产MCU上折腾了三天,OpenOCD死活连不上。最后发现芯片的JTAG引脚在硬件上被拉到了VDD,但芯片默认是SWD模式。解决办法是在配置里加一句 transport select swd。嗯,有时候问题就这么简单,但你就是想不到。

知识结构总览

下面这张图,把整个工具链的搭建流程串起来了。你可以把它当作一张「调试工具链搭建检查清单」来用。

调试工具链搭建流程 硬件连接 OpenOCD 配置 GDB 连接目标 调试脚本编写 连接验证 验证通过后,你就可以在GDB里单步、断点、看变量了。如果卡在哪一步,回头查硬件。

这张图从左到右、从上到下,就是完整的搭建顺序。我个人建议你按这个顺序来,别跳步。硬件没验证就配OpenOCD,出了问题你都不知道是硬件还是软件的问题。