实战背景

在传统的嵌入式开发中,调试代码通常需要经历:连接开发板 → 编译烧录 → 等待复位 → 设置断点 → 运行。每次改动一行代码都意味着重复这一整套流程,大幅拖慢了迭代速度。更棘手的是,硬件并不总是随手可得——出差在外、USB 端口紧缺、开发板被同事借用,甚至因硬件故障而阻塞整个调试过程。

Renode 是由 Antmicro 开发的开源硬件仿真平台,能够完整模拟 Cortex-M 系列微控制器(包括 CPU、内存及多种外设),并且内置了 GDB Server。配合 VS Code 的 Cortex-Debug 扩展,开发者无需实物开发板即可在仿真环境中进行断点设置、单步执行、变量查看等所有常规调试操作。

本文以 STM32F407 搭配 CubeMX 生成项目和 CMake 构建系统为例,记录从零搭建这一无硬件调试环境的完整过程。

环境准备
组件列表:

  • Renode:硬件仿真器,github.com/renode/renode 下载

  • VS Code 扩展:搜索安装 Cortex-Debug(作者 marus25)

  • arm-none-eabi-gdb:调试客户端,CubeMX 工具链自带

  • 固件 ELF 文件:编译产物,必须带 Debug 符号(-g 编译)

STM32F407 仿真脚本(.resc)

第一步:Renode 启动脚本

创建一个 .resc 脚本,创建机器、加载芯片平台描述、启动 GDB Server、加载固件、启动串口:

:name: STM32F407VGT6 Discovery
:description: Test script for 4WD_PTZ firmware on STM32F4 Discovery

using sysbus


mach create "STM32F407"
machine LoadPlatformDescription @platforms/boards/stm32f4_discovery-kit.repl

cpu PerformanceInMips 125

#加载测试固件
sysbus LoadELF @myProjectTest/binaries/debug/renode_test.elf

#打开串口1
showAnalyzer sysbus.usart1

#打开GDB服务(端口3333)
machine StartGdbServer 3333 true

start

VS Code配置文件

在项目根目录 .vscode/ 下创建两个文件。

launch.json — 调试启动配置

按 F5 时 VS Code 读到它,知道:用 Cortex-Debug 扩展,开 arm-none-eabi-gdb,连 localhost:3333(Renode 的 GDB Server),加载 renode_test.elf,程序自动停在 main()。连之前先编译一次(preLaunchTask)。

一句话:怎么连上 Renode 开始调试。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Renode GDB Debug",
            "type": "cortex-debug",
            "request": "attach",
            "servertype": "external",
            "gdbPath": "${env:CUBE_BUNDLE_PATH}/gnu-tools-for-stm32/14.3.1+st.2/bin/arm-none-eabi-gdb",

            // 🔄 换成目标芯片型号
            "device": "STM32F407",

            // 🔄 换成新固件的 ELF 文件路径
            "executable": "${workspaceFolder}/build/Debug/renode_test.elf",

            // 🔄 Renode GDB 端口,如果换了端口号改这里
            "gdbTarget": "localhost:3333",

            "runToEntryPoint": "main",

            // 🔄 必须与 tasks.json 中 Build 任务的 label 一致
            "preLaunchTask": "Build (CMake Debug)",

            "preAttachCommands": [
                "set remotetimeout 30",
                "set print pretty on",
                "set print array on",
                "set print array-indexes on"
            ]
        }
    ]
}

tasks.json — 构建任务

Ctrl+Shift+B 触发 Build 任务:跑 cmake --build --preset Debug,编译输出里的错误/警告会自动解析成可点击跳转的列表。另外提供 Clean(清构建产物)和 Rebuild(清完重编)。

一句话:不用离开编辑器就能编译、清空、重编。

{
    "version": "2.0.0",
    "tasks": [
        {
            // 🔄 改了这个 label,launch.json 的 preLaunchTask 也要同步改
            "label": "Build (CMake Debug)",
            "type": "shell",
            "command": "cube-cmake",
            "args": [
                "--build",
                "--preset",
                // 🔄 换成 CMakePresets.json 里对应的 preset 名称
                "Debug"
            ],
            "group": {
                "kind": "build",
                "isDefault": true
            },
            "problemMatcher": {
                "owner": "cpp",
                "fileLocation": [
                    "relative",
                    "${workspaceFolder}"
                ],
                "pattern": {
                    "regexp": "^(.*):([0-9]+):([0-9]+):[0-9]*:\\s*(warning|error|fatal error):\\s*(.*)$",
                    "file": 1,
                    "line": 2,
                    "column": 3,
                    "severity": 4,
                    "message": 5
                }
            },
            "presentation": {
                "reveal": "silent",
                "panel": "shared"
            }
        },
        {
            "label": "Clean",
            "type": "shell",
            "command": "cube-cmake",
            "args": [
                "--build",
                "--preset",
                "Debug",
                "--target",
                "clean"
            ],
            "group": {
                "kind": "build"
            },
            "presentation": {
                "reveal": "always",
                "panel": "shared"
            }
        },
        {
            "label": "Rebuild",
            // 🔄 dependsOn 里的名字必须和上面两个任务的 label 完全一致
            "dependsOn": [
                "Clean",
                "Build (CMake Debug)"
            ],
            "dependsOrder": "sequence",
            "group": {
                "kind": "build"
            },
            "presentation": {
                "reveal": "always",
                "panel": "shared"
            }
        }
    ]
}

启动仿真

固件源码

本文固件仅测试GDB服务器是否接通,所以源码比较简单。

/* 发送一条欢迎消息 */
  char *msg = "UART1 Test Started!\r\n";
  HAL_USART_Transmit(&husart1, (uint8_t *)msg, strlen(msg), 1000);

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
    /* 每隔 1 秒发送一次数据 */
    char *ping = "Hello from STM32F407 UART1!\r\n";
    HAL_USART_Transmit(&husart1, (uint8_t *)ping, strlen(ping), 1000);
    HAL_Delay(1000);
  }
  /* USER CODE END 3 */
}

运行.resc脚本

运行脚本以后先暂停仿真,本次固件只是测试使用。

VS Code开启调试

测试成功,可以进行单步调试等。

总结

本文介绍了一套完全脱离硬件开发板的嵌入式调试方案,使用 VS Code 搭配 Renode 硬件仿真器与 arm-none-eabi-gdb,在电脑上即可对 STM32 进行断点、单步、变量查看等操作。

核心工具包括:

  • Renode:模拟 Cortex-M 芯片的完整硬件环境,并内置 GDB Server。

  • VS Code 扩展 Cortex-Debug:提供图形化调试前端。

  • arm-none-eabi-gdb:作为调试客户端连接 Renode。

配置过程分为三步:

  1. 编写 Renode 启动脚本,加载芯片描述文件、开启 GDB Server(端口 3333)、加载固件 ELF,并启动仿真。

  2. 在 VS Code 中配置 launch.json(指定 GDB 路径、设备型号、可执行文件、连接目标等)与 tasks.json(构建任务),实现按 F5 自动编译并连接调试器。

  3. 终端运行 Renode 脚本后,在 VS Code 中按 F5 启动调试,CPU 会自动停在 main 函数,随后可像普通软件调试一样操作。

额外能力包括通过 SVD 文件查看外设寄存器,以及通过 GDB 命令美化变量显示。

与传统硬件调试相比,该方案无需开发板、无烧录等待、外设状态可见、连接稳定,极大提升了逻辑调试和早期驱动开发的效率。但对复杂外设行为、精细时序模拟等场景,仍需回归真实硬件。整体配置可快速迁移至其他 Cortex-M 芯片,只需调整芯片型号和平台描述文件。

Logo

免费领 150 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐