使用 VSCode 开发调试 STM32 单片机尝试


本文记录基于 Windows + DAP-Link 开发 STM32F103C8T6 的实践过程,其他操作系统或芯片应该也只是大同小异的问题。

注意:工作空间中千万不要出现中文目录和空格!



一、环境准备

硬件环境就是 STM32F103C8T6 核心板和 DAP 调试器,复杂的主要在软件部分。

调试时需要让gdb链接openocd,因此需要telnet工具。Windows下直接在Windows功能里打开telent client并重启就行

1.1_软件

  1. VSCode

    可以使用普通版或便携版,我使用的是大佬制作的便携版:https://portapps.io/app/vscode-portable/

  2. STM32CubeMX

    用来生成 Markfile 工程,已有工程模板的话不必须安装。使用 CubeMX 时需要用到 Java , Java 64位下载地址:https://java.com/en/download/manual.jsp

  3. GNU Arm Embedded Toolchain

    ARM 的 GUN 工具链,下载地址:https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads

    安装完成后需要添加到环境变量,使用命令 arm-none-eabi-gcc -v 测试。

  4. OpenOCD

    下载调试用的工具,已编译好的Windows平台可用二进制文件下载地址: https://gnutoolchains.com/arm-eabi/openocd/

    同样安装完成后需要添加到环境变量,使用命令 openocd -v 测试。

  5. make

    下载地址:http://gnuwin32.sourceforge.net/packages/make.htm

    同上,环境变量,make -v

  • 添加环境变量

    因为所有个工具都放在了同一个目录下,所以我喜欢这么加环境变量,环境变量添加完后需要点击所有的“确定”然后重启命令窗口才生效。

    如果出现配置完环境变量 VSCode 中的终端识别不到的情况重启电脑可以解决。

1.2_VSCode-插件

  1. C/C++
实时语法检查。
  1. ARM

    ARM的汇编语法高亮

  2. Cortex-Debug

MCU的调试核心,比 VSCode 默认的调试界面强大很多。为了更好的使用这个工具进行调试我们还需要对应单片机的 .svd 文件,这个文件定义了某个芯片的非常详细的信息,包含了哪些片内外设、每一个外设的硬件寄存器、每一个寄存器中每一个数据位的值以及详细的说明信息等等。svd 文件可以在单片机的固件库原包里找到,也可以去其他地方下载,这里推荐一个地方:<https://github.com/posborne/cmsis-svd/tree/master/data>

二、用CubeMX新建Makefile工程

CubeMX 的下载和安装就不多说了,注意运行它需要 java ,而且网络不好的情况还需要挂代理。

2.1_添加软件包

启动 CubeMX 后点击 "Help" -> "Manage embedded software packages" 可以进入软件包管理页(快捷键Alt+U)。进入后根据需要安装相应型号的软件包即可。

2.2_创建工程

  1. 点击软件首页的 ACCESS TO MCU SELECTOR 进入MCU选择器选择一个芯片,然后软件会跳到工程配置界面。

  2. 配置RCC. 时钟是必须要配置的,先在 Pinout&onfiguration 里配置时钟源引脚,然后去 Clock Configuration 中配置时钟树。配置时钟树时先选择好时钟源输入,然后在 HCLK(MHz) 中输入需要的频率并回车软件就会自动配置后面的部分。

  3. 选择调试模式和基础时钟源。在 Pinout&onfiguration -> SYS -> Debug 中选择 Serial Wire,时钟源同样在 SYS 标签下,选择默认的 SysTick 即可。

  4. 初始化一个连接 LED 的 GPIO 口。

2.3_生成工程

  1. 进入 Project Manager 选项卡,依次填入工程名称、工程路径、Toolchain选择Makefile.

  2. 点击 GENERAATE CODE 即可在指定路径生成工程。


三、VSCode_写代码和编译

3.1_编译和下载

将 CubeMX 生成的工程文件夹拖入到 VSCode 中打开,这时候如果不出意外的话在 VSCode 的终端中输入 make 就可以成功的编译你的工程了(前提是正确安装了 "GNU Arm Embedded Toolchain" 和 "make" 并配置了环境变量)。

程序编译完成就可以得到 hex 文件了,想要把这个文件下载进单片机有一百种办法,你可以在 J-Flash、OpenOCD、pyOCD 等工具中选择一个你喜欢的。

3.2_vscode的配置文件

通过上面的介绍已经可以对工程进行编译和下载了,但是仍有很多不足。比如工程文件中会有一大堆画波浪线的错误(前提是安装了C/C++插件)、工程的编译下载过程很繁琐等,因此我们还需要进一步配置些东西。

vscode的配置文件是放在与 .vscode 这个文件夹下的,如果 VSCode 未自动创建的话我们就需要手动创建,它在工程的根目录下

  1. c_cpp_properties.json

    • .vscode 目录下建立 c_cpp_properties.json 文件,需要用这个文件记录头文件的包含路径和全局宏定义。具体要哪些路径和哪些宏定义可以去 Makefile 目录下的 C_INCLUDESC_DEFS 找。
    • 不过只把那几项添加进来还是不够的,有些用到的头文件 CubeMX 生成的工程里并没有携带,这些C语言的标准库头文件是需要在 arm-none-eabi-gcc 的安装路径里找的。至于怎么找当然是有技巧的,在命令行里输 echo 'main(){}' | arm-none-eabi-cpp -E -v -(CMD) 就可以看到 arm-none-eabi-cpp 默认的头文件和库文件路径了,把它们添加进去。
    • 但是当你把这几个头文件加进去你会发现还是会报错,多半是什么 uint32_t 未定义之类的。这是因为 GNU-ARM 的 stdint.h 会向下钻取并生成 stdint-gcc.h ,后者依赖于一个宏,同时它也依赖其他宏才能到达那里,但是由于这些宏我们未定义所以会报错。解决这个问题的办法就是手动定义那些宏,使用 arm-none-eabi-gcc -dM -E - < nul(CMD) 可以查看 arm-gcc 的内置宏定义,查出来结果可能有几百条,不过不要在乎那些,把这些全添加进去报错就会消失了,(在添加的时候只保留第一个空格前边的内容,这点小事让Excel干就好了)。
    • 如果还是提示有问题那多半是 VSCode 的问题,重启一下试试(打开命令窗口输 ">reload windw")。
    • (PS.额外添加的这点东西只是为了告诉vscode我们有哪些东西,并不会影响gcc的编译(毕竟我们添加的都是gcc默认包含的),更不要在Markfile文件中同步修改)

    {
    "configurations": [
    {
    "name": "Win32",
    "includePath": [
    "${workspaceFolder}/**",
    "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/include",
    "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/include-fixed",
    "c:/42hdst/software/hardware/gcc-arm-none-eabi/9.2.1/bin/../lib/gcc/arm-none-eabi/9.2.1/../../../../arm-none-eabi/include",
    "${workspaceFolder}/Inc/*",
    "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/*",
    "${workspaceFolder}/Drivers/STM32F1xx_HAL_Driver/Inc/Legacy/*",
    "${workspaceFolder}/Drivers/CMSIS/Device/ST/STM32F1xx/Include/*",
    "${workspaceFolder}/Drivers/CMSIS/Include/*"
    ],
    "defines": [
    "_DEBUG",
    "UNICODE",
    "_UNICODE",
    "USE_HAL_DRIVER",
    "STM32F103xB",
    "__DBL_MIN_EXP__",
    "__HQ_FBIT__",
    ......
    ......
    ......
    "__ATOMIC_RELEASE"
    ],
    "intelliSenseMode": "msvc-x64"
    }
    ],
    "version": 4
    }
  2. tasks.json 和 launch.json

    至此用 VSCode 愉快的写代码就已经没有问题了,剩下的就是调试方面的工作了。这两个文件分别是任务和调试用的,用VSCode调试代码痛快不痛快方便不方便就全看这两个文件的水平高低了,想要写得好就得有个好老师,先观摩观摩 github 上的一个项目:


四、Github项目——VS-Code-STM32-IDE

  • 项目地址:https://github.com/damogranlabs/VS-Code-STM32-IDE
  • This project transform VS Code to a great IDE that can be used with STM32CubeMX tool to create a projects without any limitations and code size restrictions, without any bloatware and fast user setup (once all prerequisites are installed). Project is based on python scripts and is therefore fully customizable. OpenOCD tool and Cortex-Debug VS Code plugin is used for debug purposes.

4.1_环境准备

使用这个工具必须要用的除了上面提到的 arm-gcc、make、openocd 三个软件和 C/C++、ARM、Cortex-Debug 三个插件外还需要用到 Python 和 VSCode 中的 Python 插件。

4.2_使用方法

  1. 准备安装好所有的工具和软件。arm-gcc、make、openocd 三个工具怎么用自己定吧,可以配置好环境变量也可以按他说的解压到推荐目录%userprofile%\ AppData \ Roaming \ GNU MCU Eclipse中。

  2. 需要有一个用 CubeMX 生成的 Makefile 工程,工程从哪来前面说过了。

  3. 将从 GitHub 上下载到的 "ideScipts" 文件夹复制到工程目录下。

  4. 使用 VSCode 打开工程文件夹,然后用 python 运行 update.py 脚本,按提示操作(如果他的 python 脚本有什么语法错误的话不用理他,超纲了。能用就行):

    • 第一次运行不成功,说需要 .code-workspace 文件,所以在工程的根目录下给它建一个。

    • 正式运行 update.py 脚本,这次运行应该就不会报错了,按它的步骤走就行了:

      # 因为我的 arm-gcc 添加到了环境变量,所以它能检测到,不废话当然 yes.
      Default path to 'arm-none-eabi-gcc executable (arm-none-eabi-gcc.exe)' detected at 'C:\42HDST\Software\Hardware\gcc-arm-none-eabi\9.2.1\bin\arm-none-eabi-gcc.EXE'
      Use this path? [y/n]: y # 确认 make 的路径,还是yes.
      Default path to 'make executable (make.exe)' detected at 'C:\42HDST\Software\Hardware\make\3.8.1\bin\make.EXE'
      Use this path? [y/n]: y # 确认 openocd 的路径,还是yes.
      Default path to 'OpenOCD executable (openocd.exe)' detected at 'C:\42HDST\Software\Hardware\OpenOCD\0.10.0\bin\openocd.EXE'
      Use this path? [y/n]: y # 调试器配置文件的路径
      Enter path or command for 'OpenOCD ST Link interface path ('stlink.cfg')':
      Paste here and press Enter: C:\42HDST\Software\Hardware\OpenOCD\0.10.0\share\openocd\scripts\interface\cmsis-dap.cfg WARNING: Exception error overwriting 'toolsPaths.json' file:
      [Errno 2] No such file or directory: 'C:/Users/luhua/AppData/Roaming/Code/User/toolsPaths.json' # 芯片配置文件的路径
      Enter path(s) to OpenOCD configuration file(s):
      Example: 'target/stm32f0x.cfg'. Absolute or relative to OpenOCD /scripts/ folder.
      If more than one file is needed, separate with comma.
      Paste here and press Enter: C:\42HDST\Software\Hardware\OpenOCD\0.10.0\share\openocd\scripts\target\stm32f1x.cfg # svd文件路径
      Enter path or command for 'stm32SvdPath':
      Paste here and press Enter: ./STM32F103xx.svd
  5. 脚本运行完之后能会有些报错,但是无伤大雅,现在我们绝对可以用它生成的文件下载和调试工程了。即便不直接用它生成的东西对我们自己写配置文件也是很有参考价值的


五、自定义配置文件

已经有了一个好老师,接下来我们尝试自己写任务和调试的配置文件吧,这看起来很简单。

5.1_任务文件

任务文件 tasks.json ,有了任务这个东西我们就可以一键让系统自动执行好几条指令了,不用再一条一条敲。要实现 STM32 的基本开发我们总要最少设置这么几个任务——编译、下载并复位、编译且下载并复位。

  1. 编译工程,Build project. 参考参考大佬的代码,然后修修补补就成了。

    /* !!! json 文件中不能有注释,复制代码时一定要删除干净 !!! */
    {
    /* 标签 */
    "label": "Build project",
    /* build 组里边默认执行的任务,就是按 Ctrl+Shift+B 肯定执行它 */
    "group": {
    "kind": "build",
    "isDefault": true
    },
    /* 命令和参数,这里用的是绝对路径,如果配置好了环境变量也可以不写路劲。"args" 里是参数,可以写多个 */
    "type": "shell",
    "command": "C:/42HDST/Software/Hardware/make/3.8.1/bin/make.EXE",
    "args": [
    "GCC_PATH=C:/42HDST/Software/Hardware/gcc-arm-none-eabi/9.2.1/bin",
    /* -j4 是说使用四个CPU线程编译,我的CPU只有4个线程,四舍五入就是满载了 */
    "-j4"
    ],
    /* 这一块的意思是如果在运行任务时终端出现了 "Warning"、"error" 等等东西就把他们显示到问题面板上,详见图 */
    "problemMatcher": {
    "pattern": {
    "regexp": "^(.*):(\\d+):(\\d+):\\s+(warning|error):\\s+(.*)$",
    "file": 1,
    "line": 2,
    "column": 3,
    "severity": 4,
    "message": 5
    }
    },
    "presentation": {
    /* 使用这句话获取控制面板焦点,也就是说这个任务执行完焦点自动跑到终端输出哪里 */
    "focus": true
    }
    }

  2. 编译下载并复位,CPU: Build, Download and run

    {
    /* !!! json 文件中不能有注释,复制代码时一定要删除干净 !!! */
    "label": "CPU: Build, Download and run",
    "type": "shell",
    /* 这次使用的是相对路径,LHardware是我配置的一个环境变量,LHardware=C:/42HDST/Software/Hardware */
    "command": "${env:LHardware}/OpenOCD/0.10.0/bin/openocd.EXE",
    "args": [
    /* 这三个文件一定要找对,调试器配置文件、芯片配置文件和编译生成的 .elf 文件 */
    "-f",
    "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/interface/cmsis-dap.cfg",
    "-f",
    "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/target/stm32f1x.cfg",
    "-c",
    "program build/STM32F103C8Tx.elf verify reset exit"
    ],
    "problemMatcher": [],
    /* 这个任务依赖于另一个任务 */
    "dependsOn": "Build project"
    }

5.2_调试文件

  1. 有了 "Build Project" 任务我们就可以写调试文件 launch.json 了,因为调试之前总需要重新下载代码吧。

  2. 要想好好调试先得有个好工具,所以再次强调一下 "Cortex-Debug" 插件和对应芯片的 .svd 文件。

    {
    /* !!! json 文件中不能有注释,复制代码时一定要删除干净 !!! */
    "name": "Cortex debug",
    "type": "cortex-debug",
    "request": "launch",
    /* 调试器选择,很重要 */
    "servertype": "openocd",
    /* 输出路径 */
    "cwd": "${workspaceFolder}",
    /* elf 文件的路径,调试用的 */
    "executable": "build/STM32F103C8Tx.elf",
    /* svd 文件路径 */
    "svdFile": "./STM32F103xx.svd",
    /* 下面这两个是调试器和芯片的配置文件 */
    "configFiles": [
    "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/interface/cmsis-dap.cfg",
    "${env:LHardware}/OpenOCD/0.10.0/share/openocd/scripts/target/stm32f1x.cfg"
    ],
    /* 依赖于 "Build project" 任务 */
    "preLaunchTask": "Build project"
    }

P、重要补充

  1. 进一步解决 C/C++ 插件的报错

      经过之前的一通操作,又是加 include 路径又是加 define 的,基本已经可以达到 C/C++ 插件在用户代码的文件里面不报错了。但是仍有意外,我们不可能只在用户代码里呆着而是经常需要进到库函数里面看看,但是这个时候报错就又来了,比如在 stm32f1xx_hal_gpio.c 中就会疯狂报错,虽然知道这并不影响什么,在 keil 里边库函数还有几个报错呢,但是就是很不痛快。

      不过好在功夫不负有心人,这个问题的终极解决方案终于被我给找到了,我们只需要在 c_cpp_properties.json 文件中略微调一个值就可以。打开 c_cpp_properties.json 翻到最后应该能找到这么一句话 "intelliSenseMode": "msvc-x64" 这是什么意思?这是说智能感应模式是msvc-x64,搜搜msvc是什么鬼吧——MSVC是指微软的VC编译器!淦,我们明明用的是 GCC 吧!坑人呢。所以直接把这个 "msvc-x64" 改成 "gcc-x86" 莫名其妙的问题就不会再出现了,如果不放心可以建个标签把 gcc 的路径写进去。(这里之所以改成 "gcc-x86" 是因为不能选 "gcc-arm" )

  2. 关于 GCC 编译生成的 bin 文件太大的问题

    GCC 编译生成的文件是大,虽然我们用的是 GNU Arm Embedded Toolchain, 但编译出来的文件依然很大。一个基础的配置了 LED 灯的代码 Keil 编译出来只有 5k 而 GCC 编译出来的却有 29k ,优化调到 OPT = -Os 也没什么效果。关于这个问题我想说的有以下几点:

    1. 避免使用 printf() 等 C 语言标准库函数可以大幅减小 bin 文件的体积。
    2. 在 Makefile 中不使用 -u _printf_float-u _scanf_float 参数可以大幅减小 bin 文件的体积,只是这样 C 标准库中浮点数和字符串互转的功能就不起作用了。
    3. 删掉没有使用的函数也能缩小 bin 文件大小,但是有点麻烦。
    4. GCC 编译出来的文件虽然大,但它和 Keil 编译出的文件关系也只是加法不是乘法,因为在 LED 和 Printf 的基础工程上再加上 FreeRTOS 编译出来的 bin 文件大小是 34k 较之前只增了 5k ;同样在此基础上加上 FreeRTOS 后 Keil 编译出来的 bin 文件是 13k ,较之前增加了 8k ,这样看 GCC 就没有那么可恶了。
    5. 如果使用了 OS 的话,那几乎可以肯定它是一定使用了 C 标准库的函数了,所以。。。
    6. 想来在单片机领域 VSCode 和 GCC 算是高级玩家的奢侈玩法,因此那种对于 Flash 只有几 KB 甚至不到 1KB 的芯片还是乖乖用 Keil 吧。虽然用盗版软件不好,但是我们是学习用途嘛。

参考资料


使用 VSCode 开发调试 STM32 单片机尝试的更多相关文章

  1. 使用vscode开发调试.net core应用程序并部署到Linux跨平台

    使用VS Code开发 调试.NET Core RC2应用程序,由于.NET Core 目前还处于预览版. 本文使用微软提供的示例进行开发及调试. https://github.com/aspnet/ ...

  2. 玩转VSCode-完整构建VSCode开发调试环境

    随着VSCode的不断完善和强大,是时候将部分开发迁移到VS Code中了. 目前使用VS2019开发.NET Core应用,一直有一个想法,在VS Code中复刻VS的开发环境,同时迁移到VS Co ...

  3. 使用Vscode 开发调试 C/C++ 项目

    需要安装的扩展 C/C++ 如果是远程 Linux上开发还需要安装 Remote Development 创建工作目录后,代码远程克隆... 省略.. 创建项目配置文件,主要的作用是代码智能提示,错误 ...

  4. Linux下开发STM32单片机

    一开始学习51单片机就是用的MDK这个IDE软件,IDE软件虽然看起来直观好像更加容易入门(因为有界面看起来很形象),但是实际上IDE却是向我们这些入门人员隐藏了背后真实存在的过程,让我们以为编译就是 ...

  5. 使用vscode Container开发调试envoy

    由于我最近在研究 envoy 这个项目,这是个cpp的项目,对于我这种cpp新人来说还是比较有压力的,感觉处处都是坑,开个引导文章记录一下. 如果要研究 envoy 项目源码,那肯定是需要代码跳转的, ...

  6. STM32单片机应用与全案例实践 /stm32自学笔记 第二版 pdf

    STM32单片机应用与全案例实践pdf https://pan.baidu.com/s/16WrivuLcHvLTwS__Zcwl6Q 4rj3 stm32自学笔记 第二版 pdf https://p ...

  7. 为vscode开发一款svn右键菜单扩展

    在我平时的工作中会经常用到svn blame这个命令,但是vscode现有的svn扩展普遍都不能自定义右键菜单. 所以我产生一个想法:自己动手为vscode开发一款svn的扩展来定制右键菜单,本文记录 ...

  8. envoy开发调试环境搭建

    image 前段时间研究envoy的filter开发,在windows机器环境上面折腾了会,这里记录一下,希望能够帮助到大家少走一些坑 主要是使用vscode devContainer的方式来搭建开发 ...

  9. 使用VS Code从零开始开发调试.NET Core 1.0

    使用VS Code 从零开始开发调试.NET Core 1.0. .NET Core 是一个开源的.跨平台的 .NET 实现. VS Code 全称是 Visual Studio Code,Visua ...

随机推荐

  1. putty编译过程

    在Win7上用Visual Studio编译putty源代码. 安装vs2005,只安装c++和.net framework sdk即可: 将putty-src.zip解压到e:\MyDoc\VSPr ...

  2. Java ArrayList【笔记】

    Java ArrayList[笔记] ArrayList ArrayList基本结构 ArrayList 整体架构比较简单,就是一个数组结构 源码中的基本概念 index 表示数组的下标,从 0 开始 ...

  3. 说说XXE漏洞那些事

    想不起来写点啥了,又是摸鱼的一天,看了一些红队大佬们整理的资料,非常精彩,于是一个咸鱼翻身先选了一些简单的小点来写一写个人的感想(后续会继续更新其他内容) 不能说写的是技术分享,因为师傅们的文章珠玉在 ...

  4. 线程 Thread类 GIL锁 信号量 Event事件

    线程的开启方法 进程是操作系统调度的最小单位,一个进程最少有一个主线程,而一个进程中可以开启多个线程 from threading import Thread def task(): print('A ...

  5. NOIP 模拟 $22\; \rm d$

    题解 很好的贪心题 考虑去掉的矩形一定是几个 \(a\) 最小的,几个 \(b\) 最小的,枚举去掉几个 \(a\),剩下的去掉 \(b\) 先对 \(a\) 排序,用小根堆维护 \(b\) ,记录哪 ...

  6. chcon命令详解

    导读 chcon命令是修改对象(文件)的安全上下文,比如:用户.角色.类型.安全级别.也就是将每个文件的安全环境变更至指定环境.使用--reference选项时,把指定文件的安全环境设置为与参考文件相 ...

  7. mysql 常用见的错误处理

    一, 创建用户: 命令:CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明:username - 你将创建的用户名, host - 指 ...

  8. 刷题-力扣-63. 不同路径 II

    63. 不同路径 II 题目链接 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/unique-paths-ii/ 著作权归领扣网络所有.商业转 ...

  9. roslaunch保存的log文件没有打印的ERROR信息

    最近调试,发现roslaunch启动的节点,log文件中没有ERROR信息. 经过一番查证发现,INFO和WARN是保存在log文件中,ERROR直接打印在terminal 参考: https://g ...

  10. JavaScript高级程序设计读书笔记之JSON

    JSON(JavaScript Object Notation)JavaScript对象表示法.JSON是JavaScript的一个严格的子集,利用了JavaScript中的一些模式来表示结构化数据. ...