VSCode + Renode:打造现代化的嵌入式仿真开发环

在传统的嵌入式开发中,我们往往需要依赖实体开发板、仿真器(ST-Link/J-Link)、各种驱动、复杂的 IDE(如 IAR、Keil),不仅环境搭建繁琐,还会受到硬件数量、条件、测试风险的限制。

但随着 Renode(Antmicro) 的成熟,现在我们终于可以:

  • 使用 VSCode + GCC 做现代化的编辑体验
  • 使用 Renode 模拟 **CPU + 外设 **
  • 使用 GDB Server 实现调试
  • 甚至可以在服务器(树莓派/OrangePi)上远程跑 Renode

过去几天,我一步步搭好了从 VSCode → Renode → GDB 的完整链路,并解决了一堆坑。 这篇文章记录完整的流程、关键配置、常见错误与解决方式。

📌 一、为什么选择 Renode + VSCode?

现代嵌入式开发已经从这样的方式:

❌ IAR + 实物板子 + 烧录器 + 串口线 + 硬件依赖 ❌ 调试断点和步进完全依赖物理板子 ❌ 一个芯片换平台就要重装一堆东西

逐渐走向:

✔ VSCode + GCC:轻量现代化开发体验 ✔ Renode:模拟 STM32/ESP32 等设备,包含 UART、SPI、I2C、GPIO、DMA ✔ GDB Remote:跨平台、可远程调试 ✔ 自动化、可脚本化

这套方式在国外公司非常常见,原因是:

  • 更高的开发效率
  • 测试可自动化
  • CI/CD 可跑仿真
  • 对硬件依赖更低

而在国内还不太普及,所以我自己实践后写下这篇文章,希望帮助更多人。

步骤:

  1. 安装Renode
  2. 配置Renode(编辑Renode的脚本)
  3. 配置VScode(.vscode/tasks.josn/.vscode/launch.josn)
  4. 测试

目标:

  1. 实现编写代码控制renode模拟外设工作
  2. 可以使用vscode配合renode对代码进行仿真

📌 二、安装 Renode

Renode 提供多个版本,你只需要选一个能跑的:

资源下载:[Renode的github仓库](renode/renode: Renode - Antmicro's open source simulation and virtual development framework for complex embedded systems)

我的选择:

复制代码
renode-latest.linux-portable-dotnet.tar.gz

理由:

  • 自带 dotnet runtime
  • 不依赖系统的 dotnet/mono
  • 可在 OrangePi / Raspberry Pi 上运行

解压后直接运行:

bash 复制代码
./renode

📌 三、配置Renode

写一个最基础的Renode.resc文件(STM32F103),这个Renode.resc相当于你要模拟MCU的配置表,Renode加载这个.resc,模拟出你想要的环境

这是我最终可运行的版本(可直接用):

bash 复制代码
using sysbus

mach create "stm32f103"
machine LoadPlatformDescription @platforms/cpus/stm32f103.repl

# 加载固件,renode加载orangepi上.elf文件
machine LoadELF @/home/orangepi/object/renode_portable/firmware.elf

# 打开 UART2
showAnalyzer sysbus.usart2

# 开 GDB 服务(端口 3333),让vscode可以使用GDB配合renode实现可视化debug
machine StartGdbServer 3333 true


start

你可以把它保存为:

复制代码
run_stm32.resc

📌 四、VSCode 调试配置

配置launch.json,这是最关键的一步。

直接贴可运行版本(适用于 ARM Cortex-M3 / STM32F1):

kotlin 复制代码
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Debug STM32F103 on Renode Remote",
            "type": "cppdbg",
            "request": "launch",

            "program": "${workspaceFolder}/build/firmware.elf",
            "cwd": "${workspaceFolder}",

            "miDebuggerPath": "arm-none-eabi-gdb",
            "miDebuggerServerAddress": "xxx.xxx.xxx.xxx:3333",

            "preLaunchTask": "Run Renode Remote",

            "stopAtEntry": true,

            "setupCommands": [
                { "text": "set pagination off" },
                { "text": "set confirm off" },
                { "text": "set print pretty on" }
            ],

            /* On restart: pause, reload ELF, reset CPU (mimic your manual sequence) */
            "postRemoteConnectCommands": [
                { "text": "monitor machine Pause" },
                { "text": "monitor sysbus LoadELF @/home/orangepi/object/renode_portable/firmware.elf" },
                { "text": "monitor cpu Reset" }
            ],

            "externalConsole": false,

            "targetArchitecture": "arm"
        }
    ]
}

核心点:

json 复制代码
"miDebuggerPath": "arm-none-eabi-gdb",
"miDebuggerServerAddress": "xxx.xxx.xxx.xxx:3333",

解释:

  • miDebuggerPath:本机要使用的 GDB(你电脑上安装的)
  • miDebuggerServerAddress:远端 Renode 的 GDB server 监听地址
json 复制代码
"preLaunchTask": "Run Renode Remote",

VSCode 会先执行名称为 "Run Renode Remote" 的任务(在 tasks.json 里)。

json 复制代码
"postRemoteConnectCommands": [
    { "text": "monitor machine Pause" },
    { "text": "monitor sysbus LoadELF @/home/orangepi/object/renode_portable/firmware.elf" },
    { "text": "monitor cpu Reset" }
],

这是整个调试流程的灵魂。

逐句解释:

① 暂停仿真

css 复制代码
monitor machine Pause

防止 Renode 继续运行旧固件。

② 在 Renode 内重新加载 ELF

arduino 复制代码
monitor sysbus LoadELF @/home/orangepi/object/renode_portable/firmware.elf

原因:

  • VSCode 每次 build 完成本地有一个 ${workspaceFolder}/build/firmware.elf
  • 但是远程服务器上的 ELF 通常在另一个目录,比如 /home/orangepi/object/...
  • 重新加载 ELF 可以让 Renode 与最新符号一致

这是 "本地 build → scp → 远端自动加载" 的机制。

③ 重置 CPU(类似 reset halt)

复制代码
monitor cpu Reset

相当于:

  • 程序从头开始运行
  • 下一步 VSCode 会停在 main()

这个序列相当于模拟 FreeRTOS 中的 debug reset。

Tasks.json配置

出了launch.json,还有tasks.json需要配置,配置如下:

perl 复制代码
{
    "version": "2.0.0",
    "tasks": [

        /* === 1. CMake Configure === */
        {
            "label": "CMake Configure",
            "type": "shell",
            "command": "cmake",
            "args": [
                "-S", "${workspaceFolder}",
                "-B", "${workspaceFolder}/build",
                "-DCMAKE_BUILD_TYPE=Debug"
            ],
            "problemMatcher": ["$gcc"]
        },

        /* === 2. CMake Build === */
        {
            "label": "CMake Build",
            "type": "shell",
            "command": "cmake",
            "args": [
                "--build", "${workspaceFolder}/build",
                "--config", "Debug"
            ],
            "dependsOn": ["CMake Configure"],
            "problemMatcher": ["$gcc"]
        },

        /* === 3. 上传 ELF === */
        {
            "label": "Upload ELF",
            "type": "shell",
            "command": "scp",
            "args": [
                "${workspaceFolder}/build/firmware.elf",
                "root@192.168.31.135:/home/orangepi/object/renode_portable/"
            ],
            "dependsOn": ["CMake Build"],
            "problemMatcher": []
        },

        /* === 4. 上传 RESC === */
        {
            "label": "Upload RESC",
            "type": "shell",
            "command": "scp",
            "args": [
                "${workspaceFolder}/renode/platform.resc",
                "root@xxx.xxx.xxx.xxx:/home/orangepi/object/renode_portable/"
            ],
            "problemMatcher": []
        },

        /* === 5. 关闭旧 Renode 实例 === */
        {
            "label": "Close Renode Remote",
            "type": "shell",
            "command": "ssh",
            "args": [
                "root@xxx.xxx.xxx.xxx",
                "pkill renode || true"
            ],
            "problemMatcher": []
        },

        /* === 6. 启动 Renode Remote(后台任务) === */
        {
            "label": "Run Renode Remote",
            "type": "shell",
            "command": "ssh",
            "args": [
                "root@xxx.xxx.xxx.xxx",
                "/home/orangepi/object/renode_portable/renode /home/orangepi/object/renode_portable/platform.resc"
            ],
            "dependsOn": [
                "Upload ELF",
                "Upload RESC",
                "Close Renode Remote"
            ],
            "isBackground": true,

            "problemMatcher": {
                "owner": "Renode",
                "pattern": [
                    {
                        "regexp": "^(?!.*)",
                        "file": 0,
                        "line": 0,
                        "message": 0
                    }
                ],
                "background": {
                    "activeOnStart": true,
                    "beginsPattern": "Renode, version .*",
                    "endsPattern": ".*Loaded SVD.*"
                }
            }
        }
    ]
}

测试

使用vscode的一键debug,可以看到vscode正常和renode通讯,打断点和uart输出正常:

相关推荐
charlie1145141918 小时前
现代嵌入式C++教程:对象池(Object Pool)模式
开发语言·c++·学习·算法·嵌入式·现代c++·工程实践
我是海飞1 天前
杰理 AC792N 使用 WebSocket 连接百度语音大模型,实现 AI 对话
c语言·单片机·嵌入式·ai对话·杰理·websockey
不凉帅2 天前
NO.2计算机基础
网络·嵌入式·硬件·软件·计算机基础
PinoLio3 天前
鲁班猫烧录镜像win10平台
嵌入式·鲁班猫
不脱发的程序猿3 天前
使用Python高效对比多个相似的CAN DBC数据
python·单片机·嵌入式硬件·嵌入式
皮蛋sol周3 天前
嵌入式学习数据结构(二)双向链表 内核链表
linux·数据结构·学习·嵌入式·arm·双向链表
cui__OaO4 天前
Linux驱动--基于驱动设备分离的按键中断驱动
linux·运维·服务器·嵌入式
Hello_Embed4 天前
RS485 双串口通信 + LCD 实时显示(DMA+IDLE 空闲中断版)
笔记·单片机·学习·操作系统·嵌入式·freertos
Hello_Embed4 天前
RS485 双串口通信 + LCD 实时显示(中断版)
c语言·笔记·单片机·学习·操作系统·嵌入式
要做朋鱼燕5 天前
【AES加密专题】3.工具函数的编写(1)
笔记·密码学·嵌入式·aes