做嵌入式的朋友们都应该有过想要有个功能强大的IDE或者编辑器,Keil MDK5、IAR EWARM是用的比较普遍的平台了,但是两者在编辑器方面都比较弱势,当然可以设置 或者使用外部编辑器,像Sourceinsight、notePad++这样,但毕竟需要切换回来进行Build、Debug,非常麻烦。
VScode从发布至今口碑一直很好,而且里面有非常多的插件,比如彩虹括号、Code Runner、Git、各种语言的支持包等,都非常好用,并且,启动速度快,界面和使用方式和VS很像,各种好看的主题配色。像Keil、IAR不支持深色主题,看久了真的是眼睛痛(如果只把editor背景设为深色会觉得很不协调)。
前段时间刚好研究了一下linux下用makefile进行编译链接,对编译、链接有了一定的了解。这两天正好在玩STM32的CubeMX,发现里面可以自动生成makefile,就产生了在windows平台下,使用VScode和makefile编译链接,GDB调试的想法。
1. 需要安装的环境:
- minGW:用到里面的make工具;
- GNU Tools ARM Embedded(arm-none-eabi):用到里面的GCC工具(不知道为什么里面没有make,所以使用了minGW的make);
- Jlink套件:用于debug;
STM32 CubeMX:用于生成工程模板和makefile文件; - VScode,以及其插件C/C++(microsoft)。网上的教程需要许多插件,比如Clang、Cmake、ARM之类的,这里都没有用到,我还特意把其他插件都禁用测试过。
- Git Bash:用于提供一个minGW的终端,在安装minGW是会带有msys,但是我没有测试过。
2. 配置系统环境变量
配置系统环境变量的目的是可以通过命令或者文件名之直接访问系统环境变量下的文件,
将三个安装目录加入到系统环境变量(不是用户环境变量):
C:\Program Files (x86)\CodeBlocks\MinGW\bin;D:\LLVM\bin;
C:\Program Files (x86)\GNU Tools ARM Embedded\8 2019-q3-update\bin;
C:\Program Files (x86)\SEGGER\JLink_V512f;
jlink的目录可以不加,加入是为了做后面的自动开启Jlink GDB Server用。
3. 准备工程模板
这里只做一个最简要的工程,使用CubeMX创建工程,在Project Manager中配置Toolchain为makefile。然后配置项目名称、位置之类。
配置时钟、引脚功能。
点击GENERATE CODE进行生成代码。
生成完成后使用VScode打开工程目录,在main.c中添加一些代码,我写的是两个LED闪烁的程序。注意要在CubeMX规定的用户代码区域中添加代码,否则重新生成工程会被覆盖。写好后,保存。
3. 配置默认终端
在终端里,选择默认终端:
选择Git Bash。这里选择这个终端的原因是用makefile来编译的指令“make”是minGW的指令,cmd是无法识别的。另外,CubeMX生成的makefile里也会有一些linux指令,使用minGW终端可以解决这个问题。不过,在后面的tasks.json、launch.json中的command要注意使用shell命令。
4. 配置debug、make
之前完成的工程应该是这样的:
如果没有.vscode文件夹没关系,自己新建这个文件夹,然后在里面新建这两个文件:
launch是用来载入debug的配置文件,tasks是配置的任务,可以单独执行(ctrl+shift+B)。
先在tasks中创建一个Build任务,让他通过makefile进行编译、链接,生成烧录文件。然后创建一个Clean任务,可以清空build文件。在tasks.json中输入如下代码:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "mingw32-make",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Clean",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "mingw32-make -f makefile clean",
"group": {
"kind": "build",
"isDefault": true
}
},
]
}
这里我们可以先测试一下这个build task。Ctrl+Shift+B,选择Bulid,终端窗口会打印当前的状态,然后完成编译,如下图:
CubeMX的makefile默认是生成elf、hex、bin文件的,足够使用。
如果已经build,再次build,会出现如下提示:
如果想要重新生成,可以先运行Clean,然后Build。下图是运行Clean任务:
可以看到makefile中clean其实是将build文件夹删除。
然后在launch中创建一个debug配置,这个配置中要调用刚刚的build任务,然后在进行debug。
因为使用的是Jlink,所以这里采用的方法是使用Jlink的GDB server方式。原理是VScode调用GNU的gdb调试器,将gdb远程调试链接到Jlink GDB server的端口,Jlink GDB server再链接目标Device。Jlink GDB server的默认端口是 2331。
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Debug with Jlink",
"type": "cppdbg",
"request": "launch",
"targetArchitecture": "arm",//虽然官方说弃用了,但实际上必须指明
"program": "${workspaceFolder}/STM32F429IGT/a.exe",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/STM32F429IGT",
"environment": [],
"externalConsole": true,
"preLaunchTask": "Build",
"MIMode": "gdb",
"miDebuggerPath": "arm-none-eabi-gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerServerAddress": "localhost:2331",
"customLaunchSetupCommands": [
{
"text": "target remote :2331",
"description": "connect to server",
"ignoreFailures": false
},
{
"text": "file E:/Git/STM32/STM32F429/STM32F429IGT/build/STM32F429IGT.elf",
"description": "load file to gdb",
"ignoreFailures": false
},
{
"text": "load",
"description": "download file to MCU",
"ignoreFailures": false
},
{
"text": "monitor reset",
"description": "reset MCU",
"ignoreFailures": false
},
{
"text": "b main",
"description": "set breakpoints at main",
"ignoreFailures": false
},
]
}
]
}
5. 开始调试
因为用到了Jlink GDB server,所以在调试之前要先打开Jlink GDB server,选择对应单片机,这个时候Jlink要连接到电脑上。
点击OK,server就打开了,如下图:
GDB的状态是Waiting for connection。
现在就可以开始debug了。切换到VScode的debug页面,选择上面配置的(gdb) Debug with Jlink,点击开始按钮,就开始调试了。
局部变量、监视都可以正常使用,但有个问题不知道是为什么,在程序运行时打断点,会出现下面的情况:
点击继续运行就可以运行到断点处。
另外一个问题是在launch中有一个command是让gdb在main处打一个断点,但是开始debug后程序会直接运行。这两个问题需要解决之外,这样调试的缺点还有不能查看寄存器、memory。不过基本的编程、烧录功能使用起来都比较简单,可以替代MDK5、EWARM。
6. 更新、优化
在研究之后,我将打开Jlink GDB server也加入到debug的前置任务中,如果已经打开,则会先关闭当前打开Jlink GDB server然后重启,并且Jlink GDB server的单片机型号可以在task中直接设置。
另外,增加rebuild all的task,原理是先clean然后build。
在做了这些工作后,发现开始debug后,可以在main函数出自动断点,等待运行了。
修改后的tasks.json:
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "mingw32-make",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Clean",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "mingw32-make -f makefile clean",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "RebuildAll",
"type": "shell",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "E:/Git/STM32/STM32F429/.vscode/RebuildAll.cmd",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
},
{
"label": "Jlink GDB Server",
"type": "process",
"options": {
"cwd": "${workspaceRoot}/STM32F429IGT"
},
"command": "E:/Git/STM32/STM32F429/.vscode/MakeAndStartJlink.cmd",
"args": ["STM32F429IG"],
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}
其中可以看到RebuildAll、Jlink GDB Server的命令使用了cmd,因为不止一条命令,最好单独写一个文件。另外,由于cmd终端和minGW终端不太兼容,Jlink GDB Server的type为process,即外部进程。
"args"为传入cmd的参数,填写MCU型号,一定要能在Jlink devices中找到的,比如这里写了STM32F429IG,写STM32F429就是不可以的。
下面附上两个cmd的代码:
MakeAndStartJlink.cmd:
echo off
cd %ProgramFiles(x86)%/SEGGER/JLink_V512f
tasklist /fi "Imagename eq JLinkGDBServer.exe"|find "JLinkGDBServer.exe"&&taskkill /f /im "JLinkGDBServer.exe"
tasklist /fi "Imagename eq JLinkGDBServer.exe"|find "JLinkGDBServer.exe"&&taskkill /f /im "JLinkGDBServer.exe"
start JLinkGDBServer.exe -select USB -device %1 -if JTAG -speed 1000 -noir
mingw32-make
RebuildAll.cmd:
mingw32-make -f makefile clean
mingw32-make
exit
然后就是launch.json,只是将前置任务改为新添加的Jlink GDB Server。
{
//-select USB -device STM32F429IG -if JTAG -speed 1000 -noir
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Debug with Jlink",
"type": "cppdbg",
"request": "launch",
"targetArchitecture": "arm",//虽然官方说弃用了,但实际上必须指明
"program": "${workspaceFolder}/STM32F429IGT/a.exe",
"args": [],
"stopAtEntry": true,
"cwd": "${workspaceFolder}/STM32F429IGT",
"environment": [],
"externalConsole": true,
"preLaunchTask": "Jlink GDB Server",
"MIMode": "gdb",
"miDebuggerPath": "arm-none-eabi-gdb.exe",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
],
"miDebuggerServerAddress": "localhost:2331",
"customLaunchSetupCommands": [
{
"text": "target remote :2331",
"description": "connect to server",
"ignoreFailures": false
},
{
"text": "file E:/Git/STM32/STM32F429/STM32F429IGT/build/STM32F429IGT.elf",
"description": "load file to gdb",
"ignoreFailures": false
},
{
"text": "load",
"description": "download file to MCU",
"ignoreFailures": false
},
{
"text": "monitor reset",
"description": "reset MCU",
"ignoreFailures": false
},
{
"text": "b main",
"description": "set breakpoints at main",
"ignoreFailures": false
},
]
}
]
}
这些工作都完成后,.vscode文件目录结构应该是这样的:
这样,改好了,debug运行一下,Jlink GDB Server自动打开了,debug也正常,惊奇的是,现在debug会停在main函数入口等待开始了。
综上,开发环境基本配置完毕,也满足基础的需求,在这个过程中,主要遇到的问题是cmd、shell命令。我不知道在launch.json和tasks.json中能不能指定使用的终端,所以只能都采用shell(因为make指令、makefiles里是shell命令),因此,也花了一些时间去学习、测试这些指令。
网友评论