第 4 节

插件使用教程

作者:董佳辉

CubeMX安装FW

点击Help

选Manage embedded software packages,把STM32F1,F4,H7的第一个最新的固件勾上。

点install

可以不用登录账号选do not ask me again

然后等下载和安装完

下载好就行了。

VScode安装插件

然后打开VScode,在终端输入下面的命令

code

然后可以配置一个环境单独给CubeIDE插件使用,避免和默认环境冲突。

alt text

进行一些设置,自己按习惯来,不懂的暂时按我的来也行

alt text

选中STM32

alt text

然后安装一些插件

找到下面这个STM32CubeIDE for Visual Studio Code插件安装

alt text

右边弹这个提示要选择安装(要有良好的科学网络)

alt text

紧接着会进行一些环境的安装

alt text

也可以再安装一些其他的插件,比如Codex等插件 这些看你自己啦

alt text

OpenOCD使用事项

选择debugger和mcu型号

OpenOCD 一般通过 .cfg 配置文件确定:使用什么调试器 + 连接什么 MCU

openocd -f interface/调试器.cfg -f target/芯片系列.cfg

第一个参数:看你用什么调试器

调试器OpenOCD 配置说明
ST-Link V2 / V2-1 / V3"interface/stlink.cfg"STM32 最常见
DAPLink"interface/cmsis-dap.cfg"DAPLink 实现 CMSIS-DAP
CMSIS-DAP"interface/cmsis-dap.cfg"很多第三方调试器使用
J-Link"interface/jlink.cfg"SEGGER J-Link
FTDI 类 JTAG/SWD"interface/ftdi/xxx.cfg"根据具体硬件选择

第二个参数:看 STM32 哪个系列

STM32 系列常见型号示例OpenOCD Target
STM32C0C011、C031、C071"target/stm32c0x.cfg"
STM32F0F030、F072"target/stm32f0x.cfg"
STM32F1F103C8、F103RC"target/stm32f1x.cfg"
STM32F2F205、F207"target/stm32f2x.cfg"
STM32F3F303、F334"target/stm32f3x.cfg"
STM32F4F405、F407、F429、F446"target/stm32f4x.cfg"
STM32F7F746、F767"target/stm32f7x.cfg"
STM32G0G030、G070、G0B1"target/stm32g0x.cfg"
STM32G4G431、G474"target/stm32g4x.cfg"
STM32H5H503、H563、H573"target/stm32h5x.cfg"
STM32H7H743、H750、H745、H747"target/stm32h7x.cfg"
STM32H7RSH7R3、H7S3"target/stm32h7rsx.cfg"
STM32L0L031、L073"target/stm32l0.cfg"
STM32L1L151、L152"target/stm32l1.cfg"
STM32L4 / L4+L432、L476、L496、L4R5"target/stm32l4x.cfg"
STM32L5L552、L562"target/stm32l5x.cfg"
STM32N6N657 等"target/stm32n6x.cfg"
STM32U0U031、U073"target/stm32u0x.cfg"
STM32U3U385 等"target/stm32u3x.cfg"
STM32U5U575、U585、U5A5"target/stm32u5x.cfg"
STM32WBWB55 等"target/stm32wbx.cfg"
STM32WBA2WBA2xx"target/stm32wba2x.cfg"
STM32WBA5WBA5xx"target/stm32wba5x.cfg"
STM32WBA6WBA6xx"target/stm32wba6x.cfg"
STM32WLWL55、WLE5"target/stm32wlx.cfg"
STM32C5C53、C54、C55、C56、C59、C5A见下方说明

STM32C5 要特别注意:

现在不要把 C5 写死成target/stm32c5x.cfg, 原因是 STM32C5 的 OpenOCD 支持非常新。2026 年 7 月才有 stm32c5x.cfg 支持补丁提交到 OpenOCD Gerrit;补丁里的文件名确实就是target/stm32c5x.cfg。 但是现在还没完全发行到OpenOCD,需要等一段时间。

测试设备连接

我们用的stm32f103c8和st-link:

所以用

openocd -f interface/stlink.cfg -f target/stm32f1x.cfg

可以插上之后测试一波:

照上图所示,已经连接成功了。

几个关键日志分别说明:

Info : STLINK V2J48S7 ...
→ OpenOCD 正常识别 ST-Link。

Info : Target voltage: 3.150483
→ 目标板供电正常,约 3.15 V。

Info : SWD DPIDR 0x1ba01477
→ SWD 通信已经建立。

Info : [stm32f1x.cpu] Cortex-M3 r1p1 processor detected
→ 已经真正识别并连接 MCU 核心。

Info : target has 6 breakpoints, 4 watchpoints
→ Cortex-M3 的硬件断点/数据观察点正常。

Info : Examination succeed
→ Target 初始化成功。

最后:
Info : [stm32f1x.cpu] starting gdb server on 3333
Info : Listening on port 3333 for gdb connections
→ OpenOCD 的 GDB Server 已经正式启动,可以给 VS Code / STM32Cube 调试器连接了。

工程创建与测试

使用CubeMX创建工程

点击进入单片机挑选的按钮

搜索对应芯片,并双击对应芯片选项。

进行一些配置,以下都是很基础的东西,你在看这个视频前肯定都会了

随便开一个IO用来测试,比如LED的GPIO

FreeRTOS也要配置一下。

本例在用户目录中实现 StartDefaultTask() 来替换默认任务函数,需要CubeMX生成的同名函数为弱定义。 可以在FreeRTOS的 Tasks and Queues 中,将 defaultTask 的 Code Generation Option 设为 As weak,重新生成后确认该函数带有 __weak,避免两个普通定义产生冲突。参考ST社区说明。

这些文件夹也要配置好,最后Toolchain选择CMake,编译器选择GCC(6.14.1及之前没有选择编译器这个选项很正常)

对工程进行配置与编译

在工程文件夹打开终端(或者直接在此处打开VScode也行)

alt text

code .

打开VScode后记得切到`STM32·的配置

alt text

选择这里的Yes进行配置CMake预设

alt text

一般选Debug即可

alt text

可能谈一个很长的对话框,是这个STM32Cube Core提示的,你直接点yes开始下载(这是自动部署各种环境的)

反正右下角是STM32Cube Core提示让你安装的,你安就完了,每个版本提示可能不一样,总之安装就完了。

等上面的环境部署完毕后,

找个C语言的代码文件打开,然后右下角会提示安装一个C/C++插件,这个可以安装也可以不安装,他也带代码提示,但是他的代码提示对比自带的clangd简直是弱爆了,如果你是新手,你不会设置代码提示,建议按我下面的操作来,直接别装这个C/C++插件。

默认用的是clangd代码提示

你可以测试一下代码提示,是不是很强。

alt text

编译的话,图中的这俩build都可以

alt text

移植作者tungchiahui的标准C/C++工程模板

用git clone命令克隆仓库:https://github.com/tungchiahui/STM32HAL_CMake_CPP_Template

git clone https://github.com/tungchiahui/STM32HAL_CMake_CPP_Template.git

把仓库里的除了.git以外的东西都复制到我们的STM32工程根目录。

alt text

模板中的用户代码按功能模块组织,每个模块的 .cpp、.hpp(或 .c、.h)放在同一个文件夹里。复制后的目录结构如下:

STM32工程根目录/
├── applications/
│   └── main/
│       ├── cpp_interface.cpp
│       └── cpp_interface.h
├── bsp/
│   └── boards/
│       ├── common/
│       │   └── struct_typedef.h
│       └── delay/
│           ├── bsp_delay.cpp
│           └── bsp_delay.hpp
└── cmake/
    ├── stm32cubemx/            # CubeMX生成的配置
    └── user/
        └── CMakeLists.txt     # 用户代码的配置

cmake/user/CMakeLists.txt 已经把 applications 和 bsp/boards 加入头文件搜索路径,所以引用用户头文件时统一使用“模块目录/头文件名”的写法:

#include "main/cpp_interface.h"
#include "delay/bsp_delay.hpp"
#include "common/struct_typedef.h"

在这两个目录下新增模块时,沿用这个写法即可,不需要为每个模块单独添加头文件搜索路径。

打开CubeMX生成的 Core/Src/main.c,在 USER CODE BEGIN Includes 和 USER CODE END Includes 之间加上:

#include "main/cpp_interface.h"

然后在 main() 的 USER CODE BEGIN 2 和 USER CODE END 2 之间调用 cpp_main(),此时系统时钟和外设已经初始化完成:

cpp_main();

注意 applications/main/cpp_interface.h 中的 isRTOS 设置:

  • 裸机使用默认值 0,业务代码可以写在 cpp_main() 的无限循环中;
  • 使用FreeRTOS时要改成 1,使 cpp_main() 执行完初始化后返回,程序才能继续创建任务并启动调度器。
  • 下面沿用前面配置了FreeRTOS的工程,因此先将 isRTOS 改为 1。

然后打开工程根目录的 CMakeLists.txt,在CubeMX原有的 add_subdirectory(cmake/stm32cubemx) 后面添加 add_subdirectory(cmake/user),引用用户代码的配置:

# Add STM32CubeMX generated sources
add_subdirectory(cmake/stm32cubemx)

# Add USER generated sources
add_subdirectory(cmake/user)

在同一个顶层 CMakeLists.txt 中,保留原有的C语言标准设置,并在 project() 之前增加C++标准设置(模板的 README-zh_CN.md 中也有说明):

# Setup compiler settings
set(CMAKE_C_STANDARD 11 CACHE STRING "C language standard")
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS ON)

set(CMAKE_CXX_STANDARD 20 CACHE STRING "C++ language standard")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS ON)

然后在 applications 下新建 led_task 文件夹,把 led_task.cpp 和 led_task.h 都放进去:

applications/
├── main/
│   ├── cpp_interface.cpp
│   └── cpp_interface.h
└── led_task/
    ├── led_task.cpp
    └── led_task.h

applications/led_task/led_task.cpp:

#include "led_task/led_task.h"
#include "cmsis_os.h"
#include "main.h"

int32_t t;

extern "C"
void StartDefaultTask(void *argument)
{
  for(;;)
  {
    HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
    ++t;
    osDelay(500);
  }
}

applications/led_task/led_task.h:

#ifndef LED_TASK_H
#define LED_TASK_H

#include "main/cpp_interface.h"

#endif

然后打开 cmake/user/CMakeLists.txt,把刚才新建的源文件添加到 Applications_Src 列表中:

set(Applications_Src
    ${CMAKE_CURRENT_SOURCE_DIR}/../../applications/main/cpp_interface.cpp
    ${CMAKE_CURRENT_SOURCE_DIR}/../../applications/led_task/led_task.cpp
)

只需要登记新增的 .c、.cpp 文件,.h、.hpp 不需要列为编译源文件。因为 applications 已经是头文件搜索根目录,led_task 模块也不需要额外添加搜索路径。

详细介绍(可以不看):cmake/stm32cubemx/CMakeLists.txt 由CubeMX管理,重新生成代码时可能更新,因此用户代码的源文件列表和头文件搜索路径统一维护在 cmake/user/CMakeLists.txt 中,顶层 CMakeLists.txt 通过 add_subdirectory(cmake/user) 加载它。这个用户配置文件已经包含在模板里,前面复制时就已经带过来了。

大功告成,编译一次试试。可以看到下图,那些新加的文件都编译上了。

alt text

下载程序到板子

下载之前首先要先配置:

如果你不是stlink和jlink,接着往下看

按下图的来点击,你看看你是什么debugger,你就选哪个。

如果这里看不到对应设备,遇到权限问题,请查看Linux 下 USB 权限问题。

可以更新下STlink的驱动:

先安装jlink-gdbserver的bundle,如下图所示:

alt text

使用openocd服务驱动原生gdb调试器

当你要使用除了ST-Link和J-Link以外的调试器时:

先点调试,让他生成launch.json:

openOCD接管选STM32Cube: STM32 Launch GDB:

然后选openocd:

过一会儿,肯定会调试失败,然后提示你open launch.json

然后再点STM32Cube: STM32 Launch GDB:

launch.json内容如下:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "stgdbtarget",
            "request": "launch",
            "name": "STM32Cube: Launch Generic GDB Server",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "deviceName": "${command:st-stm32-ide-debug-launch.get-device-name}",
            "deviceCore": "${command:st-stm32-ide-debug-launch.get-core-name}",
            "deviceTrustzone": "${command:st-stm32-ide-debug-launch.get-trustzone-status}",
            "serverExe": "",
            "serverParameters": [],
            "serverHost": "localhost",
            "serverPort": "",
            "serverCwd": "",
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        },
        {
            "type": "stgdbtarget",
            "request": "attach",
            "name": "STM32Cube: Launch GDB Client",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "serverHost": "localhost",
            "serverPort": "${command:st-stm32-ide-debug-launch.get-server-port}",
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        }
    ]
}

我们需要改哪里呢?

主要是:

  • serverExe : 就填二进制程序名 openocd或者openocd.exe
  • serverParameters :填那两个openOCD参数,参考 openocd的两个-f参数
  • serverHost :OpenOCD GDB Server 默认监听 localhost,一般不用改
  • serverPort :OpenOCD 默认 GDB 端口 为 3333
  • serverCwd : 填serverExe这个二进制程序在哪个目录底下
  • 添加liveWatch参数(重要)
  1. serverExe:

Linux填openocd Windows填openocd.exe

  1. serverParameters按下面这个格式来:

那俩-f的参数由openocd的两个-f参数可知:

  • interface/stlink.cfg
  • target/stm32f1x.cfg

但是除了这俩,还需要一些参数:

            "serverParameters": [                
                "-f",
                "interface/stlink.cfg",

                "-c",
                "transport select swd",

                "-f",
                "target/stm32f1x.cfg",

                "-c",
                "$_TARGETNAME configure -gdb-max-connections 2"],

这里建议按上面这个顺序排布参数:先探针 → 再通信协议 → 再 MCU → 最后修改这个 MCU target 的参数

这里的-c transport select xxx是选协议:

实际调试方式配置
STM32 常用 SWDtransport select swd
JTAGtransport select jtag

咱们一般都是swd,所以选transport select swd。

而这个-c $_TARGETNAME configure -gdb-max-connections 2是允许这个 OpenOCD target 同时接受最多 2 个 GDB 客户端连接。

正常调试已经占了一个:

OpenOCD :3333
    ↑
    └── GDB #1
         普通 Debug

咱们还需要用到Live Watch,所以把参数设置为2之后:

                  ┌─ GDB #1:普通 Debug
STM32 ← OpenOCD ──┤
                  └─ GDB #2:Live Watch
  1. serverCwd:

用终端命令直接查找下:

# Linux
which openocd

# Windows
where.exe openocd

Linux如上图是/usr/bin/openocd,所以serverCwd填/usr/bin。

Windows如上图是C:\xpack-openocd-0.12.0-7\bin\openocd.exe,所以serverCwd填C:\xpack-openocd-0.12.0-7\bin。 但是不能直接填C:\xpack-openocd-0.12.0-7\bin,因为在json中\是转义字符,所以傻逼Windows应该填C:/xpack-openocd-0.12.0-7/bin或者双反斜杠C:\\xpack-openocd-0.12.0-7\\bin。

  1. 添加liveWatch参数

在serverCwd后面,runEntry前面加上:

            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": "4"
            },

最后:

Linux:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "stgdbtarget",
            "request": "launch",
            "name": "STM32Cube: Launch Generic GDB Server",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "deviceName": "${command:st-stm32-ide-debug-launch.get-device-name}",
            "deviceCore": "${command:st-stm32-ide-debug-launch.get-core-name}",
            "deviceTrustzone": "${command:st-stm32-ide-debug-launch.get-trustzone-status}",
            "serverExe": "openocd",
            "serverParameters": [                
                "-f",
                "interface/stlink.cfg",

                "-c",
                "transport select swd",

                "-f",
                "target/stm32f1x.cfg",

                "-c",
                "$_TARGETNAME configure -gdb-max-connections 2"],
            "serverHost": "localhost",
            "serverPort": "3333",
            "serverCwd": "/usr/bin",
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": "4"
            },
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        },
        {
            "type": "stgdbtarget",
            "request": "attach",
            "name": "STM32Cube: Launch GDB Client",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "serverHost": "localhost",
            "serverPort": "${command:st-stm32-ide-debug-launch.get-server-port}",
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        }
    ]
}

Windows:

{
    // Use IntelliSense to learn about possible attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "stgdbtarget",
            "request": "launch",
            "name": "STM32Cube: Launch Generic GDB Server",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "deviceName": "${command:st-stm32-ide-debug-launch.get-device-name}",
            "deviceCore": "${command:st-stm32-ide-debug-launch.get-core-name}",
            "deviceTrustzone": "${command:st-stm32-ide-debug-launch.get-trustzone-status}",
            "serverExe": "openocd.exe",
            "serverParameters": [                
                "-f",
                "interface/stlink.cfg",

                "-c",
                "transport select swd",

                "-f",
                "target/stm32f1x.cfg",

                "-c",
                "$_TARGETNAME configure -gdb-max-connections 2"],
            "serverHost": "localhost",
            "serverPort": "3333",
            "serverCwd": "C:/xpack-openocd-0.12.0-7/bin",
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": "4"
            },
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        },
        {
            "type": "stgdbtarget",
            "request": "attach",
            "name": "STM32Cube: Launch GDB Client",
            "origin": "snippet",
            "cwd": "${workspaceFolder}",
            "preBuild": "${command:st-stm32-ide-debug-launch.build}",
            "program": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
            "gdb": "${command:st-stm32-ide-debug-launch.get-gdb-executable}",
            "serverHost": "localhost",
            "serverPort": "${command:st-stm32-ide-debug-launch.get-server-port}",
            "runEntry": "main",
            "imagesAndSymbols": [
                {
                    "imageFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "imageOffset": "",
                    "symbolFileName": "${command:st-stm32-ide-debug-launch.get-projects-binary-from-context1}",
                    "symbolOffset": ""
                }
            ]
        }
    ]
}

如何进入调试

显示1

如果你是下图这样,请按下面这样做:

如果不是,请往下滑看下面的显示2。

alt text

点完Run and Debug后,如下图:

  • ST-Link选STM32cube: STM32 Launch STLink GDB Server
  • J-Link选STM32Cube: STM32 LaunchJLink GDB Server
  • openOCD接管选STM32Cube: Launch Generic GDB Server

alt text

显示2

如果你是下图这样,请按下面这样做:

alt text

你显示上面这样,可能是因为你已经有生成的launch.json了。(这样直接点击就行)

alt text

进入调试

然后会出现这个条,他会下载程序到板子(仅J-link) alt text

然后就成功下载了程序并进入了Debug

alt text

按照下图所示,把你要监视的变量输入到最顶上的框里,就可以加入到实时监视框里了。

结构体也可以:

更换调试器软件为cortex debug(可选,很没啥必要):

安装cortex debug插件

在VScode里搜索cortex debug并安装

进行launch.json的配置

新建.vscode/launch.json:

{
    "version": "0.2.0",

    "configurations": [
        {
            // 调试配置名称
            "name": "STM32 - OpenOCD - Cortex Debug",

            // Cortex-Debug 固定写法
            "type": "cortex-debug",

            // 下载程序并开始调试
            "request": "launch",

            // 工程工作目录
            "cwd": "${workspaceFolder}",

            // ★ 必改:编译生成的 ELF
            "executable": "${workspaceFolder}/build/Debug/TEST.elf",

            // 使用 OpenOCD
            "servertype": "openocd",

            // ★ OpenOCD 可执行文件
            // Linux 下用 which openocd 查询
            "serverpath": "/usr/bin/openocd",

            // ★ 根据调试器和 MCU 修改
            //
            // ST-Link:
            // interface/stlink.cfg
            //
            // STM32F1:
            // target/stm32f1x.cfg
            //
            // STM32F4 则是:
            // target/stm32f4x.cfg
            "configFiles": [
                "interface/stlink.cfg",
                "target/stm32f1x.cfg"
            ],

            // ★ ARM GNU Toolchain 的 bin 目录
            // 建议不要直接写 "~"
            // 用 ${env:HOME} 更稳
            "armToolchainPath": "${env:HOME}/.local/share/stm32cube/bundles/gnu-tools-for-stm32/14.3.1+st.2/bin",

            // 启动后运行到 main()
            "runToEntryPoint": "main",

            // ★ Cortex Live Watch
            // OpenOCD 下可用
            // samplesPerSecond 最大 20
            // 先用 4Hz 足够
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": 4
            }


            // ============================================================
            // 以下为可选配置
            // ============================================================


            // 如果 Cortex-Debug 找不到 OpenOCD,
            // Linux:
            // which openocd
            //
            // 一般 Fedora 是:
            // /usr/bin/openocd
            //
            // "serverpath": "/usr/bin/openocd",


            // 如果 OpenOCD 找不到 interface/*.cfg 或 target/*.cfg,
            // Fedora 一般脚本目录在:
            // /usr/share/openocd/scripts
            //
            // 可以显式增加:
            //
            // "searchDir": [
            //     "/usr/share/openocd/scripts"
            // ],


            // 调试 Cortex-Debug / OpenOCD 问题:
            //
            // "showDevDebugOutput": "raw"
        }
    ]
}

目前我们要知道三件事,configFiles,executable,armToolchainPath和serverpath:

  1. configFiles:

参考 openocd的两个-f参数

我们用的stm32f103c8和st-link: 所以"configFiles": ["interface/stlink.cfg","target/stm32f1x.cfg"]。

  1. executable:

用vscode的终端命令直接查找下,或者直接在VScode的文件管理器里找:

# Linux
find build -name "*.elf"
# Windows
gci build -Recurse -File -Filter "*.elf"

注意,Windows要用powershell,不可以用cmd。

如上图所示,在build/Debug/TEST.elf。

所以executable = ${workspaceFolder}/build/Debug/TEST.elf。

  1. armToolchainPath:

用vscode的终端命令直接查找下:

cube bundle show --project

这个输出说明,我在用gnu-tools-for-stm32@14.3.1+st.2

用命令找他在哪:

# Linux
find ~/.local/share/stm32cube/bundles/gnu-tools-for-stm32 \
  -type f -name "arm-none-eabi-gdb"
# Windows
gci "$env:LOCALAPPDATA\stm32cube\bundles\gnu-tools-for-stm32" `
  -Recurse -File -Filter "arm-none-eabi-gdb.exe"

注意,Windows要用powershell,不可以用cmd。

复制输出结果:

/home/tungchiahui/.local/share/stm32cube/bundles/gnu-tools-for-stm32/14.3.1+st.2/bin/arm-none-eabi-gdb

但是去掉最后的执行程序arm-none-eabi-gdb,只需要到bin即可:

/home/tungchiahui/.local/share/stm32cube/bundles/gnu-tools-for-stm32/14.3.1+st.2/bin

所以armToolchainPath = /home/tungchiahui/.local/share/stm32cube/bundles/gnu-tools-for-stm32/14.3.1+st.2/bin。

  1. serverpath:

用终端命令直接查找下:

# Linux
which openocd

而Windows,你自己装哪的你应该知道吧。。。

最终的launch.json:

{
    "version": "0.2.0",

    "configurations": [
        {
            // 调试配置名称
            "name": "STM32 - OpenOCD - Cortex Debug",

            // Cortex-Debug 固定写法
            "type": "cortex-debug",

            // 下载程序并开始调试
            "request": "launch",

            // 工程工作目录
            "cwd": "${workspaceFolder}",

            // ★ 必改:编译生成的 ELF
            "executable": "${workspaceFolder}/build/Debug/TEST.elf",

            // 使用 OpenOCD
            "servertype": "openocd",

            // ★ OpenOCD 可执行文件
            // Linux 下用 which openocd 查询
            "serverpath": "/usr/bin/openocd",

            // ★ 根据调试器和 MCU 修改
            //
            // ST-Link:
            // interface/stlink.cfg
            //
            // STM32F1:
            // target/stm32f1x.cfg
            //
            // STM32F4 则是:
            // target/stm32f4x.cfg
            "configFiles": [
                "interface/stlink.cfg",
                "target/stm32f1x.cfg"
            ],

            // ★ ARM GNU Toolchain 的 bin 目录
            // 建议不要直接写 "~"
            // 用 ${env:HOME} 更稳
            "armToolchainPath": "/home/tungchiahui/.local/share/stm32cube/bundles/gnu-tools-for-stm32/14.3.1+st.2/bin",

            // 启动后运行到 main()
            "runToEntryPoint": "main",

            // ★ Cortex Live Watch
            // OpenOCD 下可用
            // samplesPerSecond 最大 20
            // 先用 4Hz 足够
            "liveWatch": {
                "enabled": true,
                "samplesPerSecond": 4
            }
        }
    ]
}
Debug与LiveWatch

先编译

点击进入debug:

成功进入:

复制一个你程序里的某个全局变量,我这里是cmd_vel2:

填到cortex live watch,注意不是stm32cube live watch:

然后点运行看一下变量cmd_vel2

是不断在变化的:

常见问题

Linux下USB权限问题

如果你都正常识别出设备了,则没必要做这一步了

如果你遇到的话,你解决不了就让AI来解决,AI几秒钟就给你把事办了

如果调试器插上后能在 lsusb 中看到,但 VS Code 找不到设备,或者调试输出中出现下面的报错,通常是当前用户没有访问 USB 设备节点的权限:

libusb couldn't open USB device /dev/bus/usb/001/008, errno=13
libusb requires write access to USB device nodes
ST-Link enumeration failed

先在终端运行 lsusb,确认调试器已被系统识别。报错中的 /dev/bus/usb/001/008 只是示例,实际路径以你自己的输出为准。可以用 ls -l 查看该节点的权限:

lsusb
ls -l /dev/bus/usb/001/008

**使用 ST-Link:**先按上文点击插件中的 install ST-Link udev rules。如果仍然报权限错误,检查规则是否装到了系统中:

find /etc/udev/rules.d /usr/lib/udev/rules.d -maxdepth 1 -iname '*stlink*.rules' -print 2>/dev/null

如果没有找到规则,可以从 ST 官方的 STSW-LINK007 下载页获取安装包。解压后进入 AllPlatforms/StlinkRulesFilesForLinux,按其中的 readme.txt 安装与你的发行版对应的 udev 规则包。在这个目录中,Fedora 使用 .rpm 包,Ubuntu/Debian 使用 .deb 包:

# Fedora:只运行这一行
sudo dnf install ./st-stlink-udev-rules-*.rpm

# Ubuntu/Debian:只运行这一行
sudo apt install ./st-stlink-udev-rules-*.deb

如果下载的包名不同,以实际文件名和随包说明为准。ST 的发行说明也说明了 Linux 下需要安装相应的 ST-Link USB 访问规则。

**使用 J-Link:**上文安装的 jlink-gdbserver bundle 用于启动调试服务;USB 权限还需要 SEGGER 的 udev 规则。安装 SEGGER J-Link Software and Documentation Pack 中对应发行版的 .rpm 或 .deb 包时,会一并安装规则。如果使用解压版,则按照包内 README.txt,将 99-jlink.rules 复制到 /etc/udev/rules.d/。具体方法见 SEGGER 的 Linux 排查说明。

安装规则后,运行:

sudo udevadm control --reload-rules

然后拔下调试器再插回去,重新运行 lsusb,用新的总线号和设备号查看 /dev/bus/usb/... 节点。可以将实际路径代入下方命令,检查当前用户能否读写:

test -r /dev/bus/usb/001/008 && test -w /dev/bus/usb/001/008 && echo 'USB 设备节点可读写'

拔插后设备号可能变化,不要照抄示例中的 001/008。确认权限后,回到 VS Code 重新选择调试器。如果 lsusb 根本看不到调试器,应先检查 USB 线、接口和供电;这不是 udev 权限问题。

这里检查的是 /dev/bus/usb/...。如果另外还要打开板载虚拟串口 /dev/ttyACM*,那是串口设备的权限,需要分别排查。