在传统的嵌入式开发中,我们往往需要依赖实体开发板、仿真器(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 可跑仿真
- 对硬件依赖更低
而在国内还不太普及,所以我自己实践后写下这篇文章,希望帮助更多人。
步骤:
- 安装Renode
- 配置Renode(编辑Renode的脚本)
- 配置VScode(.vscode/tasks.josn/.vscode/launch.josn)
- 测试
目标:
- 实现编写代码控制renode模拟外设工作
- 可以使用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输出正常:
