GoDm@'s Blog

使用openOCD进行调试

版权信息

warning

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


OpenOCD(Open On-Chip Debugger)是一个极其强大的开源工具,主要用于硬件调试、在线编程(ISP)和边界扫描测试。

1. OpenOCD 的工作逻辑

在动手之前,我们需要理解 OpenOCD 在整个开发链路中扮演的角色:

  1. 主机 (Host PC): 运行 OpenOCD 软件,以及 GDB 调试器。

  2. 调试适配器 (Adapter/Interface): 连接电脑和目标芯片的硬件(例如 ST-Link、J-Link、DAPLink 等)。

  3. 目标芯片 (Target): 你要调试或烧录的微控制器(例如 STM32、ESP32 等)。

OpenOCD 的作用就是作为桥梁。它向左通过网络端口(Telnet/GDB)与你的电脑软件通信,向右通过 USB 驱动控制你的调试器硬件,最终操作芯片。

2. 安装

测试安装: 在终端输入 openocd --version,如果能看到版本号说明安装成功。

3. 启动 OpenOCD 服务

启动 OpenOCD 的核心在于告诉它你用了什么调试器,以及你要连什么芯片。这通过指定配置文件(.cfg)来实现。OpenOCD 自带了大量的标准配置文件(通常存放在它安装目录的 scripts 文件夹下)。

基础启动命令格式:

openocd -f interface/<调试器名称>.cfg -f target/<芯片名称>.cfg

常见组合

如果终端输出 Info : Listening on port 3333 for gdb connectionsInfo : Listening on port 4444 for telnet connections,说明连接成功,服务已经跑起来了。此时不要关闭这个终端窗口。

4. 烧录与控制

OpenOCD 启动后,它会在后台运行并开放了几个网络端口。你可以重新打开一个新的终端窗口,通过 Telnet (端口 4444) 直接发送命令给它。

4.1. 连接到 OpenOCD 控制台

在新终端输入:

telnet localhost 4444

4.2 常用交互指令

连接成功后,你会看到 > 提示符。以下是标准的操作流程:

  1. 暂停芯片运行:

    > halt
  2. 解锁/擦除并写入固件(支持 .hex / .bin / .elf):
    这里假设你要烧录的文件名为 firmware.hex请使用绝对路径或确保在当前工作目录下。

    > flash write_image erase firmware.hex
  3. 复位并运行芯片:

    > reset run
  4. 退出 Telnet:

    > exit

如果你不想每次都敲这么多命令,可以直接在启动 OpenOCD 时附带执行命令:

openocd -f interface/stlink.cfg -f target/stm32f4x.cfg -c "program firmware.hex verify reset exit"

这条命令会自动完成:连接 -> 烧录 -> 校验 -> 复位运行 -> 退出。非常适合自动化脚本!

5. 结合GDB进行代码调试

OpenOCD 最强大的地方在于配合 GDB 进行单步调试。

  1. 保持 OpenOCD 服务运行(参考第三部分)。

  2. 打开新终端,启动 GDB:(需要使用对应架构的 gdb,比如 ARM)

    arm-none-eabi-gdb your_program.elf
  3. 在 GDB 中连接到 OpenOCD:

    (gdb) target remote localhost:3333
  4. 复位并暂停芯片(OpenOCD 特定指令在 GDB 中需要加 monitor 前缀):

    (gdb) monitor reset halt
  5. 加载程序(烧录):

    (gdb) load
  6. 开始常规 GDB 调试:

    • b main (在 main 函数打断点)

    • c (Continue,运行到断点)

    • n (Next,单步步过)

    • s (Step,单步步入)

6. 常见问题排查

错误现象 可能的原因与解决方案
Error: open failed 找不到调试器硬件。请检查 USB 是否插好,虚拟机是否勾选了 USB 直通,或者 Windows 下是否缺少 WinUSB 驱动(可以使用 Zadig 工具安装驱动)。
Error: init mode failed 调试器与芯片通信失败。检查 SWD/JTAG 连线(GND, SWDIO, SWCLK)是否接错、松动,或者芯片是否供电正常。
Error: timed out while waiting for target halted 芯片可能进入了深度休眠或禁用了调试端口。尝试按住开发板上的 Reset 按钮,运行命令,然后瞬间松开 Reset。

7. WSL下的OpenOCD最佳实践

核心思路: 把 OpenOCD 运行在 Windows 上直接接管 USB 硬件,把交叉编译工具链和 GDB 运行在 WSL 中。两者通过 TCP 网络端口进行通信。

由于 OpenOCD 本质上就是一个 GDB Server,这种 C/S(客户端/服务端)架构完美契合 WSL 的网络特性。

操作步骤:

  1. 在 Windows 端启动 OpenOCD:
  1. 在 WSL 端获取 Windows 主机 IP:
    WSL2 和 Windows 不在同一个网段。你需要在 WSL 终端中获取 Windows 宿主机的 IP 地址(通常是虚拟网关):
    # 获取 Windows 宿主机的 IP
    cat /etc/resolv.conf | grep nameserver | awk '{print $2}' 
    # 假设输出为 172.28.16.1

如果你使用的是 Windows 11 且开启了 WSL 的 networkingMode=mirrored,你可以直接使用 localhost)

  1. 在 WSL 端启动 GDB 并连接:
    编译出 .elf 固件后,在 WSL 中启动 GDB:

    arm-none-eabi-gdb your_firmware.elf

    在 GDB 命令行中,连接到 Windows 上的 OpenOCD:

    (gdb) target remote 172.28.16.1:3333
    (gdb) load   
    (gdb) monitor reset halt
    (gdb) continue

通过 GDB 的 load 命令,固件数据是通过 TCP 传给 OpenOCD 的,完美规避了跨系统的文件路径问题。

还有一种方案是将USB透传给WSL,这样WSL就拥有这个usb设备,可正常使用Linux下的openOCD,透传USB参见:WSL下连接USB设备-GoDm@'s blog

8. 基于 vscode 图形化调试

在 VS Code 中进行图形化调试是目前嵌入式开发最高效的方式。借助强大的 Cortex-Debug 插件,你可以完美地将代码编辑、编译(在 WSL 中)和底层硬件调试结合在一起,甚至可以直接在可视化界面中查看外设寄存器和 RTOS 的任务栈。

在 VS Code 中(如果是WSL,则在WSL环境下)安装了以下插件:

  1. C/C++ (Microsoft) - 提供代码补全和基础 GDB 支持。
  2. Cortex-Debug (marus25) - 最核心的插件,专门针对 ARM Cortex-M 内核优化,支持 OpenOCD 桥接、寄存器查看和 RTOS 线程解析。

8.1. 核心配置:编写 launch.json

launch.json 核心作用是告诉 VS Code 如何启动和配置调试会话。

当你编译出 .elf 文件后,你需要把它烧录进芯片,并且监控程序的运行。这就是 launch.json 要干的。它定义了 VS Code 应该怎么去调用底层的调试工具。

在这个文件里,你需要向 VS Code 交代清楚:

有了 launch.json,VS Code 的图形化调试界面(左侧的变量窗口、调用堆栈、断点控制)才知道要去哪里获取数据。当你打下一个断点时,VS Code 会通过 launch.json 里的配置,把这个“暂停”指令一路传达到物理芯片的硬件断点寄存器上。

在你的工程根目录下,创建或打开 .vscode/launch.json 文件。针对我们在上一回提到的两种 WSL 开发架构,这里提供两套配置。你可以直接将以下代码复制进去,根据你的实际架构选择对应的调试配置启动。

{
    "version": "0.2.0",
    "configurations": [
        {
            // 【配置一:混合架构】OpenOCD 运行在 Windows 端,GDB 在 WSL 端
            "name": "Debug (Windows OpenOCD)",
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "external", // 关键:告诉插件 OpenOCD 已经在外部启动了
            "gdbTarget": "172.28.16.1:3333", // 替换为你的 Windows 宿主机 IP
            "executable": "${workspaceFolder}/build/your_program.elf", // 替换为你的实际 elf 路径
            "cwd": "${workspaceRoot}",
            "runToEntryPoint": "main", // 启动后自动在 main 函数停下
            
            // 进阶功能(可选但强烈推荐)
            "svdFile": "${workspaceRoot}/STM32F4xx.svd", // 替换为你的芯片 SVD 文件路径
            "rtos": "FreeRTOS" // 如果你跑了 RTOS,开启此项可以查看所有线程状态
        },
        {
            // 【配置二:USB 穿透】OpenOCD 和 GDB 都运行在 WSL 内部
            "name": "Debug (WSL Local OpenOCD)",
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "openocd", // 关键:由插件全权负责在 WSL 启动和关闭 OpenOCD
            "executable": "${workspaceFolder}/build/your_program.elf",
            "cwd": "${workspaceRoot}",
            "configFiles": [
                "interface/stlink.cfg", // 你的调试器配置文件
                "target/stm32f4x.cfg"   // 你的目标芯片配置文件
            ],
            "runToEntryPoint": "main",
            
            // 进阶功能
            "svdFile": "${workspaceRoot}/STM32F4xx.svd",
            "rtos": "FreeRTOS"
        }
    ]
}

8.2. Cortex-Debug 进阶

配置好 launch.json 并按下 F5 启动调试后,除了常规的单步运行(F10)、进入函数(F11)、打断点,你还可以利用 Cortex-Debug 提供的强大面板:

  1. 外设寄存器可视化 (XPERIPHERALS 面板)

    作用: 你可以直接在图形界面点开 GPIOATIM1 等外设,实时查看甚至动态修改底层寄存器的值(比如直接勾选某个 bit 来翻转引脚电平),这在排查底层驱动或协议栈问题时很好用。

    SVD 文件通常可以从芯片厂商官网(如 STMicroelectronics)或 Keil 的 Pack 包中提取。

  2. RTOS 线程感知 (CALL STACK 面板)

    如果你在项目中使用了 RTOS(如 FreeRTOS、RT-Thread),在 launch.json 中添加 "rtos": "FreeRTOS" 字段。

    作用: Cortex-Debug 会自动解析内存,在左侧的“调用堆栈 (Call Stack)”中列出当前系统中所有的 RTOS 任务(Thread)。你可以随时点击任意一个被挂起或阻塞的任务,查看它在切换上下文之前的局部变量和运行轨迹。这对于排查死锁或时序问题极为有效。

  3. 内存查看器 (Memory View)

    按下 Ctrl+Shift+P,输入 Cortex-Debug: View Memory

    作用: 输入一个内存地址(例如缓冲区的首地址 0x20001000)和长度,你可以像使用 Hex Editor 一样直接观察裸机内存中的数据变化。

补充:从keil pack包获取svd

  1. 访问 Keil Pack 官网: 打开 Keil CMSIS-Pack 搜索页
  2. 下载 Pack 包: 搜索你的芯片系列(例如 STM32G4),找到 Keil.STM32G4xx_DFP 并下载。你会得到一个后缀为 .pack 的文件。
  3. 解压提取: .pack 文件本质上就是一个 .zip 压缩包。 将它的后缀改成 .zip 然后解压。
  4. 找到 SVD: 在解压后的文件夹中,依次进入 CMSIS -> SVD 目录,里面有 .svd 文件。

建议直接把下载好的 .svd 文件扔到你的 VS Code 工程根目录下(和 .vscode 文件夹平级)然后修改 launch.json

8.3 自动化编译任务

当你写完代码后,通常需要在终端里输入类似 make -j8 或者 cmake --build build 的命令。每次修改代码都要敲一次命令太麻烦了。我们可以把这些命令封装为一个Task。实现 “按 F5 自动编译 -> 自动烧录 -> 自动进入调试” 的一键全自动流水线。

在工程的 .vscode 目录下创建一个名为 tasks.json 的文件。根据你的构建工具,选择以下其中一段配置填入:

参数中加了 -j$(nproc)。在 Linux/WSL 环境下,这代表调用 CPU 的所有核心进行并发编译。如果你在工程中引入了像 LVGL 这样的图形库,或者包含了复杂的微内核和协议栈,几百上千个 C 文件的编译时间会非常长。加上这个参数,编译速度能提升好几倍,极大地改善调试体验。

task.json 工作逻辑: 创建一个Build Project 的任务,告诉 VS Code:“当你执行这个任务时,就在终端里帮我自动运行 make 命令”。

不仅仅是编译: 你甚至可以写一个清空编译产物的任务(执行 make clean),或者写一个自动打包固件的任务。只要是能在终端里敲的命令,都可以写进 tasks.json 里让它一键代劳。

现在任务写好了,我们需要告诉 VSCode 在启动 OpenOCD 调试之前,请先运行这个编译任务

打开你之前的 .vscode/launch.json,在你的调试配置项(“configurations”)中,添加一行 preLaunchTask,它的值必须和你刚才在 tasks.json 中写的 label 完全一致。

完成。


共计约3.4k字。于2026/03/14首次发布,最后更新于2026/03/14。

本文章为博主原创文章。遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

  1. 1. OpenOCD 的工作逻辑
  2. 2. 安装
  3. 3. 启动 OpenOCD 服务
  4. 4. 烧录与控制
    1. 4.1. 连接到 OpenOCD 控制台
    2. 4.2 常用交互指令
  5. 5. 结合GDB进行代码调试
  6. 6. 常见问题排查
  7. 7. WSL下的OpenOCD最佳实践
  8. 8. 基于 vscode 图形化调试
    1. 8.1. 核心配置:编写 launch.json
    2. 8.2. Cortex-Debug 进阶
    3. 8.3 自动化编译任务