使用 VSCode 开发调试 STM32 单片机尝试
阅读原文时间:2022年03月19日阅读:1

使用 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++

    实时语法检查。

  2. ARM

    ARM的汇编语法高亮

  3. Cortex-Debug

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


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 即可在指定路径生成工程。


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 上的一个项目:


  • 项目地址: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"
    }

  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 也没什么效果。关于这个问题我想说的有以下几点:


手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器