使用vscode+gcc+Jlinkgdbserver来为stm32编写代码有一段时间了,将应用重点写下来给自己当备忘录,有缘人也可以看看。
之前在使用MDK编译CubeMX生成的Stm32H750工程的时候遇到了大麻烦:
使用armcc5正常编译,但巨慢无比。
使用armcc5去掉跳转编译,速度是快了,但是没跳转功能了。
使用armcc6编译,速度更快了,但是兼容性有问题,研究了一下,armcc6竟然默认定义了GNU宏,不知道自己不是gcc吗?定义这个宏干嘛???不知道lwip就是靠这个宏来区分编译器的么?一编译一大堆错,搞什么飞机!!
类似这种
#if defined ( __ICCARM__ ) /*!< IAR Compiler */
...
#elif defined ( __CC_ARM ) /* MDK ARM Compiler */
...
#elif defined ( __GNUC__ ) /* GNU Compiler */
...
#endif
问题来了,怎样知道编译器有哪些预定义宏呢?
如果想知道armcc6编译器预定义宏
cmd输入
"C:\Keil_v5\ARM\ARMCLANG\bin\armclang --target=arm-arm-none-eabi -E -dM d:\main.c"
发现里面有一行
#define __GNUC__ 4
如果想知道gcc预定义宏
cmd输入
"arm-none-eabi-gcc -dM -E d:\main.c"
里面对应的一行是
#define __GNUC__ 9
如果想知道armcc5的预定义宏
"C:\Keil_v5\ARM\ARMCC\bin\armcc.exe --list_macros"
输入ctrl+z
这就是armcc6(armclang)编译失败的根源
正好有点在linux下vscode工具链应用经验,被逼无奈,趁机换门。
看到有一些教程里面为了用gcc,竟然走了安装Mingw或者cygwin的路子,谁要是照做怕是要变成受害者。其实windows下可以全部搞定,不要去装什么劳什子Mingw。
vscode看代码跳转更加方便,按函数浏览,还有一些有用的插件。更重要的是使用vscode开发,配置文件一目了然。不像MDK,勾选一些选项到底有什么影响也缺少清楚的解释,总感觉和真实世界隔着一层,如果是编写windows程序倒是不在乎这些,但是单片机要求不一样,这样隔着一层没人喜欢。而当学会vscode后再回头用MDK,会更容易理解那些藏在角落里的小选项,或者一些所谓的骚操作是什么东东。
另外一个很重要的点是VScode+gcc-arm-none-eabi工具链是跨平台的,到linux下面完全没区别,我个人就是在linux下用过这套工具链,到windows下又重新捡起来。
用VS Code开发STM32(一)——软件安装
用VS Code开发STM32(二)——编译
用VS Code开发STM32(三)——调试
vscode调试功能没有MDK多,比如不能实时看内存状态(后来证明可以,参见另外一篇博文),需要Jlink全家桶软件中的Jmem帮忙才能形成一套完整的调试环境。而且Jlink有RTT、SystemView、JFlash软件加持,功能又多又好,当了解到这些时果断收起STLinkV2.1,扶Jlink上位。
顺便记录一下:
Openocd是跨平台通用片上调试软件,支持多种调试工具,其中包含stlink、Jlink。不过既然使用Jlink,那当然还是用Jlink提供的Jlinkgdbserver了,以前在linux下面使用的就是Openocd,试了一下windows下也可以正常应用。
so,最后我使用的工具链是VScode+gcc-arm-none-eabi+Jlinkgdbserver(没用到Openocd)
实测官方最新版的gcc-arm-none-eabi-10-2020-q4-major-win32编译cubemx生成的代码可能会报错,只能暂时使用我搬运的gcc-arm-none-eabi-9-2020-q2-update-win32 ,然后等待官方修复吧。
openocd下载地址(如果不用,可以不安装)
相关path环境变量
C:\Program Files (x86)\GNU Arm Embedded Toolchain\OpenOCD-20200729-0.10.0\bin
C:\Program Files (x86)\GNU Arm Embedded Toolchain\9 2020-q2-update\arm-none-eabi\bin
C:\Program Files (x86)\GNU Arm Embedded Toolchain\9 2020-q2-update\bin
C:\Program Files (x86)\GNU Arm Embedded Toolchain\9 2020-q2-update\arm-none-eabi\include
实际上,按按钮自动进入调试的过程是通过插件cortex-debug实现的,所以launch.json里面的一些参数是在配置cortex-debug插件。小技巧:输入"会自动跳出支持的参数,然后再去研究其含义。
{
"version": "0.2.0",
"configurations": [
{//Jlink
"name": "Jlink-Debug",
"cwd": "${workspaceRoot}",
"executable": "./build/${workspaceRootFolderName}.elf",
"serverpath": "C:/Program Files (x86)/SEGGER/JLink/JLinkGDBServerCL.exe",
"serverArgs": ["-speed", "50000"],
//JLinkGDBServerCL的参数,设定Jlink速度,要是不设定这个弄个龟速,你就亏了
"request": "launch",
"type": "cortex-debug",
"preLaunchTask": "build",//调用自定义的task
"rtos": "FreeRTOS", //不加这个调试时候不会显示任务,只会像正常程序一样显示断点位置
"runToMain": true, //调试时运行到main函数
"servertype": "jlink",//或者openocd,有固定支持选项,输入时候有选项提示。
"interface":"swd",//调试接口
"device": "STM32H750VB",
"svdFile": "${workspaceRoot}/user/STM32H750x.svd",
//在stm网站下载,没有svd文件不能看外设寄存器名称
"configFiles": [
"${workspaceRoot}/user/H7-jlink.cfg"//自定义的,后面有这个文件的内容
],
},
{//STLINKV2
"name": "Stlink-Debug",
"cwd": "${workspaceRoot}",
"executable": "./build/${workspaceRootFolderName}.elf",
"request": "launch",
"type": "cortex-debug",
"preLaunchTask": "build",
"runToMain": true,
"servertype": "openocd",
"interface":"swd",
"device": "STM32H750VB",
"svdFile": "${workspaceRoot}/user/STM32H750x.svd",
"configFiles": [
"${workspaceRoot}/user/H7-stlink.cfg"
],
},
]
}
launch.json每个工程都要用,它的位置在.vscode下,每建立一个新工程就把这个配置文件拷贝过去。
{
// 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",
"command": "make -j4"
},
{
"label": "RTT",
"type": "shell",
"command": "JLinkRTTClient.exe",
"args": [],
"problemMatcher": []
},
]
}
这里写了两个任务,一个用来对工程进行make,一个用来启动RTTClicent,RTT是Jlink带的类似swo一样的东西,当虚拟串口用。build任务供launch.json调用或者task插件调用。
task.json每个工程都要用,它的位置在.vscode下,每建立一个新工程就把这个配置文件拷贝过去。
source [find interface/jlink.cfg]
source [find target/stm32h7x.cfg]
reset_config none
每个工程都要用,把它放在user文件夹下,供launch.json引用。
source [find interface/stlink-v2-1.cfg]
source [find target/stm32h7x.cfg]
reset_config none
每个工程都要用,把它放在user文件夹下,供launch.json引用。
这个插件需要配置一下
插件上点击齿轮-扩展设置-在setting.json中编辑,改写为:
"better-comments.tags": [
"better-comments.tags"
{
"tag": "y-tag",
"color": "#000000",
"strikethrough": false,
"underline": false,
"backgroundColor": "#FFFF80",
"bold": true,
"italic": false
},
{
"tag": "o-tag",
"color": "#FFFFFF",
"strikethrough": false,
"underline": false,
"backgroundColor": "#FF8C00",
"bold": false,
"italic": true
},
{
"tag": "r-tag",
"color": "#FFFFFF",
"strikethrough": false,
"underline": false,
"backgroundColor": "#FF0000",
"bold": false,
"italic": true
},
{
"tag": "g-tag",
"color": "#FFFFFF",
"strikethrough": false,
"underline": false,
"backgroundColor": "#408080",
"bold": false,
"italic": true
},
{
"tag": "B-tag",
"color": "#FFFFFF",
"strikethrough": false,
"underline": false,
"backgroundColor": "#3498DB",
"bold": false,
"italic": true
},
],
这个插件是必须装的,但是它的配置恶心到了很多人,全网也没几个人解决的,被我解决了,哈!右下角双击win32,进入ui配置:
1.编译器路径
C:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update/bin/arm-none-eabi-gcc.exe
2.include路径
${workspaceFolder}/**
C:/Program Files (x86)/GNU Arm Embedded Toolchain/9 2020-q2-update/**
两个*意思是递归,只要给定上层目录,下面自动就全包含了。
3.预处理宏
_DEBUG
UNICODE
_UNICODE
USE_HAL_DRIVER
STM32H750xx
设置这些都是为了实现命令提示,代码补全。和MDK不同,在vscode里面编译链接和代码补全跳转是平行的两条线,互不影响。
配置完成以后即可以正常补全跳转,但是还是可能会出现讨厌的红色波浪线,可以尝试下列操作:
ctrl+shift+p 输入restart选择 Developer: restart extension host
ctrl+shift+p 输入reset选择 Reset IntelliSense database
ctrl+shift+p 输入rescan选择 Rescan Workspace
和MDK一样,所要配置的就是上面两个必要的宏和两个递归引用目录,代码量再巨大也不怕,都能正常跳转。
vs常用的快捷键:
ctrl+shift+p 打开命令面板。比如输入restart / task等。
ctrl+shif+o 按函数名称浏览,快速定位。
F12 跳转到定义 Alt+左箭头 再跳回来,Alt+右箭头 再跳过去。
CubeMX生成makefile的基本工程不再多说,随便一个教程都有。
windows下make程序需要gnu make for windows下载地址
编译窗口输出的东西过多,对makefile做简单修改。
$(BUILD_DIR)/%.o: %.c Makefile | $(BUILD_DIR)
@$(CC) -c $(CFLAGS) -Wa,-a,-ad,-alms=$(BUILD_DIR)/$(notdir $(<:.c=.lst)) $< -o $@
@echo compiling $(notdir $(<:.c=.o))
$(BUILD_DIR)/%.o: %.s Makefile | $(BUILD_DIR)
@$(AS) -c $(CFLAGS) $< -o $@
@echo compiling $(notdir $(<:.s=.o))
$(BUILD_DIR)/$(TARGET).elf: $(OBJECTS) Makefile
@$(CC) $(OBJECTS) $(LDFLAGS) -o $@
@echo -------------------------------------
@echo linking...
$(SZ) $@
clean:
powershell rm $(BUILD_DIR) -r -Fo
clean也需要简单改动以适应powershell规定。
使用cubemx重新生成工程,会保留makefile的更改,不用担心。但是在inuclude下面可能会多生成一个目录,造成编译错误,如果出现删除即可。
这样就可以在终端窗口里面,
输入make -j4编译;
输入make clean完成编译文件删除;
或者点击task插件里面的buid执行编译;
或者ctrl+shift+p输入task点击buid执行编译。
如果需要重命名工程:
1.将工程文件夹名字重命名;
2.将makefile的第一行进行如下修改:
TARGET = 新的工程名
如果有新增.c文件,需要修改makefile:
#C sources
C_SOURCES = \
Core/Src/main.c \
Core/Src/gpio.c \
...
如果有新增.h文件,需要修改makefile:
C_INCLUDES = \
-ICore/Inc \
-IDrivers/STM32H7xx_HAL_Driver/Inc \
-IDrivers/STM32H7xx_HAL_Driver/Inc/Legacy \
...
gcc的重定向和armcc不一样,需要重定义的是_write函数:
int _write(int file, char *data, int len)
{
HAL_UART_Transmit(&huart1, (uint8_t*)data, len, 400);
return 0;
}
注意使用printf时,后面一定要加上\r\n,不然不输出。
如果需要指定数组在内存中的位置:
1.定义数组时指定段名称。
uint8_t Rx_Buff[2][2] __attribute__((section(".RxArraySection")));
2.链接文件指定段位置。
.lwip_sec (NOLOAD) :
{
. = ABSOLUTE(0x30040000);
*(.RxDecripSection)
. = ABSOLUTE(0x30040060);
*(.TxDecripSection)
. = ABSOLUTE(0x30040200);
*(.RxArraySection)
} >RAM_D2 AT> FLASH
经过这些操作,vscode基本上已经没有遗憾点。其实大部分时候,gcc编译速度落后于armcc,但是正如前言里面说的,armcc有bug,至少对于H7+lwip来说,vs+gcc工具链效率是远远超越mdk的,节省太多不能忍的时间。对了,别忘了使用Jmem~~