[W01/16] 开发环境搭建:从零到 VSCode 能点亮 NUCLEO-L552ZE-Q 的 LD1

《Cortex-M33 内核与 RTOS 源码学习》系列第 1 篇
硬件:NUCLEO-L552ZE-Q · 主机:Windows 11
目标:装完工具链 → CubeMX 生成工程 → VSCode 编译烧录 → LD1 闪起来


开篇:为什么折腾 VSCode,而不是直接用 CubeIDE

先把结论摆前面:CubeIDE 能用,但它不是我想要的日常环境。

三个理由:

  1. AI 插件生态。VSCode 里能挂各种代码助手、clangd、Copilot,看源码效率比 CubeIDE 高一个量级。后面读 RTX5 和 FreeRTOS 源码的时候你会感谢自己这个选择。
  2. 工具链解耦。CubeIDE 把 GCC、OpenOCD、Eclipse、CubeMX 全绑一起,想换编译器或换调试器都得费劲。VSCode + CMake + arm-none-eabi-gcc 是业界通用组合,跳到别的芯片、别的 RTOS 都能复用。
  3. 。CubeIDE 启动 10 秒 vs VSCode 2 秒。这个差距每天累积很可观。

但 CubeIDE 还是要装——当 VSCode 出问题时它是第二参照,官方例程也是它打开最顺手。我把它定位成"备用 IDE + 应急调试器"。

最终技术栈:

STM32CubeMX        → 图形化生成初始化代码 + CMake 工程骨架
STM32CubeCLT       → 命令行工具链(arm-none-eabi-gcc + OpenOCD + ST-LINK 驱动)
STM32CubeIDE       → 备用 IDE(可选,但推荐装)
STM32CubeProgrammer→ 烧录和 Option Byte 查看(TrustZone 调参必备)
VSCode             → 主力编辑器
  └─ 插件:STM32 VS Code Extension / C-C++ / clangd / Cortex-Debug

一、硬件准备

1.1 板子

NUCLEO-L552ZE-Q,STMicroelectronics 官方开发板。

关键参数:

内容
主芯片 STM32L552ZET6Q(Cortex-M33,110 MHz)
Flash / RAM 512 KB / 256 KB
板载调试器 ST-LINK/V3E
供电 USB Type-C 或外部
特色 支持 TrustZone、Arduino Uno V3 扩展接口、SMPS 低功耗

1.2 线材

一条 USB Type-C 数据线,插 CN1(STLINK USB)。注意两件事:

  • 必须是数据线,不是充电线。部分廉价 Type-C 线只有电源没有 D+/D-,插上去板子能亮灯但电脑识别不到 ST-LINK。
  • 插 CN1 不是 USB USER。板子上有两个 Type-C 口,CN1 带 “STLINK” 丝印,只有它走 ST-LINK/V3E;CN8(USB USER)是给用户 USB 外设用的,插错了不报错但烧不进去。

1.3 上电验证

线插好后,板子上应该看到:

  • LD4 红色常亮(电源指示)
  • LD6 绿色(COM LED,ST-LINK 通信时闪烁)

Windows 设备管理器里应多出:

  • USB 串行设备(一个虚拟 COM 口,ST-LINK/V3E 的 VCP)
  • USB 大容量存储设备(NOD_L552ZE-Q 盘符,拖 .bin 文件到里面即可烧录——这是 ST-LINK 的 Mass Storage 模式)

如果这三样没到位,大概率是线或驱动问题,先解决这里再往下走。


二、软件清单与下载顺序

顺序很重要,装错顺序会导致 VSCode 找不到工具链。

# 软件 大小 下载地址
1 STM32CubeCLT ~500 MB ST 官网 → st.com/en/development-tools/stm32cubeclt.html
2 STM32CubeMX ~400 MB ST 官网 → stm32cubemx
3 STM32CubeProgrammer ~300 MB ST 官网 → stm32cubeprog
4 STM32CubeIDE(可选) ~1 GB ST 官网 → stm32cubeide
5 VSCode ~100 MB code.visualstudio.com
6 Git for Windows ~60 MB git-scm.com

⚠️ 下载要注册 ST 账号,免费,但邮箱激活链接有时进垃圾箱。

推荐装到非 C 盘非中文非空格路径,比如 D:\ST\。后面 CMake 路径调用对空格和中文敏感,早期图省事后期难排查。


三、安装步骤详解

3.1 STM32CubeCLT(先装这个!)

这是整个 VSCode 方案的灵魂——它提供了:

  • arm-none-eabi-gcc(交叉编译器)
  • cmake / ninja
  • openocd(烧录调试后端)
  • STM32_Programmer_CLI(命令行烧录)

安装要点

  1. 默认路径 C:\ST\STM32CubeCLT_<版本> 可以用。
  2. 安装最后一步勾选 “Add to PATH”,否则 VSCode 找不到。
  3. 装完打开 PowerShell 验证:
arm-none-eabi-gcc --version
# 应输出:arm-none-eabi-gcc (GNU Tools for STM32 xx.x.xxx) 12.x.x

没输出就是 PATH 没生效,重启电脑或手动在环境变量里把 C:\ST\STM32CubeCLT_<版本>\GNU-tools-for-STM32\bin 加进去。

3.2 STM32CubeMX

  • 安装过程中会要求装 JRE,同意。
  • 首次启动会提示更新 MCU 库,选择更新。网速慢可以先跳过,用的时候再装。
  • 首次启动 File → Manage embedded software packages → 下载 STM32Cube FW_L5 固件包(对应 L552)。

3.3 STM32CubeProgrammer

  • 一路下一步。
  • 装完打开能连接 ST-LINK/V3E 就算成功。这工具看 Option Bytes(TrustZone 的 TZEN 位就在这里)时会非常好用,第 5 周讲 TrustZone 必装。

3.4 VSCode + 插件

VSCode 本体装完后,在 Extensions 面板按顺序装:

插件 发布者 作用
STM32 VS Code Extension STMicroelectronics 官方插件,一键识别 CubeMX 工程
C/C++ Microsoft 基础 IntelliSense
clangd LLVM 真正好用的补全(和 C/C++ 二选一,后面讲如何切换)
Cortex-Debug marus25 GDB 调试前端
CMake Tools Microsoft CMake 集成(STM32 插件会自动装)

关键一步:装完 STM32 扩展后,按 Ctrl+Shift+PSTM32 VS Code Extension: Check system requirements

如果看到:

Arm Tools: 0

说明 CubeCLT 没被识别。两个办法:

  1. Command Palette → STM32 VS Code Extension: Set up STM32CubeCLT path,手动指到 C:\ST\STM32CubeCLT_<版本>
  2. 检查环境变量 STM32_CUBE_CLT_PATH 是否存在。

看到 Arm Tools: ✓ 才算通。


四、CubeMX 生成第一个 VSCode 工程

4.1 新建工程

  1. CubeMX 启动 → ACCESS TO BOARD SELECTOR
  2. 搜索 NUCLEO-L552ZE-Q → 选中 → Start Project
  3. 弹窗"Initialize all peripherals with their default Mode?"——选 Yes,LED/按键会自动配好。

4.2 ⚠️ 三个必调项(第 1 周关键)

这三个不调好,后面所有的麻烦都会翻倍。

① Project Manager → Project → Toolchain / IDE:选 CMake

这是 VSCode 方案的命门。默认是 MDK-ARM,选错了生成出来的工程 VSCode 认不出来。

② System Core → TrustZone:Disabled(第 1 周暂时关掉)

很多人不知不觉就把 TrustZone 开了——标志是生成的工程名带 _S_NS 后缀、有两套 main.c。第 1 周先关掉,专注搞通基础链路;第 5 周(Module D)再正式研究。

验证方法:CubeMX 左上角芯片框不应该有 “TrustZone” 字样。如果有,去 System Core → TrustZone 关掉。

③ Middleware → FreeRTOS:Disabled(第 1 周暂时关掉)

CubeMX 的 FreeRTOS 不是我们要学的那个——它是 CMSIS-RTOS2 封装过的版本,学源码反而碍事。第 3 周讲上下文切换时我们从 GitHub 拉原版 FreeRTOS 源码手动集成。

4.3 其他该确认的

位置 设置 说明
System Core → SYS → Timebase Source SysTick 第 1 周裸机无 RTOS,SysTick 就够了;后期上 RTOS 会改成 TIM6
System Core → CORTEX_M33_NS → ICACHE Enabled ST 强烈推荐,Flash 访问加速
Clock Configuration HCLK = 110 MHz 板子最高频,用 MSI + PLL
Project Manager → Code Generator → Generate peripheral init as pair of .c/.h 勾选 代码清晰,每个外设一个文件

4.4 生成代码

Project Manager → Project

  • Project Name: L552_W01_Blinky(或你喜欢的任何名,避免空格和中文)
  • Project Location: D:\stm32_projects\(同上,避免中文和空格)
  • Toolchain/IDE: CMake(再次确认!)

点右上角 GENERATE CODE

生成成功后会弹窗:“Open Folder with VS Code”——点 Yes


五、在 VSCode 里编译、烧录、调试

5.1 编译

VSCode 打开工程后,左下角状态栏:

  • Build preset: Debug(默认)
  • Kit: arm-none-eabi(应该自动识别,没识别就手动选)

Ctrl+Shift+PSTM32 VS Code Extension: Build,或者直接点状态栏的齿轮图标。

成功输出:

[build] [100%] Linking C executable L552_W01_Blinky.elf
[build] Build finished with exit code 0

生成文件在 build/Debug/L552_W01_Blinky.elf

想要 .bin/.hex? 打开 CMakeLists.txt,找到:

# 在文件末尾加:
add_custom_command(TARGET ${CMAKE_PROJECT_NAME} POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${CMAKE_PROJECT_NAME}> $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/${CMAKE_PROJECT_NAME}.bin
    COMMAND ${CMAKE_OBJCOPY} -O ihex   $<TARGET_FILE:${CMAKE_PROJECT_NAME}> $<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/${CMAKE_PROJECT_NAME}.hex
    COMMENT "Generating .bin and .hex"
)

重新 Build,同目录下就会有 .bin.hex

5.2 烧录

三种方式,按推荐度排序:

方式 A:拖 .bin 到 U 盘(最简单)

打开资源管理器,找到 NOD_L552ZE-Q 盘,把 L552_W01_Blinky.bin 拖进去。LD6 闪几下,自动烧录 + 复位。零工具、零配置,适合快速验证。

方式 B:VSCode 一键烧录

Ctrl+Shift+PSTM32 VS Code Extension: Flash,调用 OpenOCD 烧录。生产环境推荐这个,能和 CI 结合。

方式 C:STM32CubeProgrammer

图形化工具,看 Option Byte 和调 TrustZone 时必用,日常烧录有点杀鸡焉用牛刀。

5.3 调试:单步操作完整手册

这一节是重点,也是读者真正想学的部分。我按「配置 → 启动 → 基本单步 → 断点 → 观察窗口 → 进阶观测」六步写,每步都能直接照做。

5.3.1 确认 Build Type 是 Debug

左下角状态栏点 Release → 选 Debug,或 Ctrl+Shift+PCMake: Select VariantDebug

Release 模式下编译器会优化掉大量中间变量,单步时光标会乱跳,看起来像见鬼。第 1 周到第 16 周,日常调试一律用 Debug 模式

5.3.2 生成或检查 launch.json

左侧调试面板(Ctrl+Shift+D)→ 如果看到 create a launch.json file 就点它 → 选 Cortex Debug

自动生成的 .vscode/launch.json 应该长这样(关键字段):

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "STM32 Debug (ST-LINK)",
            "type": "cortex-debug",
            "request": "launch",
            "servertype": "stlink",
            "cwd": "${workspaceFolder}",
            "executable": "${workspaceFolder}/build/Debug/L552_Template_Base.elf",
            "device": "STM32L552ZE",
            "interface": "swd",
            "runToEntryPoint": "main",
            "svdFile": "${workspaceFolder}/STM32L552.svd",
            "showDevDebugOutput": "none"
        }
    ]
}

两个关键字段解释

  • runToEntryPoint: "main" —— F5 启动后自动跑到 main() 暂停。想从 Reset_Handler 第一条指令看起(第 2 周要干的事),把它改成 "Reset_Handler"
  • svdFile —— 指向 STM32L552 的 SVD 文件(寄存器描述),有了它 Cortex Peripherals 窗口才能按名字显示 NVIC/SCB/SysTick。从 ST 官网 CMSIS Pack 里能找到。没有这个文件第 2 周会很难受。
5.3.3 启动调试(F5)

确保板子插好 USB-C、ST-LINK/V3E 识别正常,按 F5 或点左上角绿色三角。

正常流程:

  1. 底部状态栏变橙色,显示 Cortex-Debug: Flash ...
  2. OpenOCD 把 elf 烧进 Flash(几秒)
  3. CPU 复位 → 跑到 main() 入口第一行暂停
  4. 左侧自动出现 CALL STACK / VARIABLES / WATCH / BREAKPOINTS 等面板

此时你应该看到

  • 编辑器里 main.c 的第一行左边出现一个黄色箭头 ▶
  • CALL STACK 里只有 main @ main.c:XX
  • 顶部浮动工具栏出现 6 个图标:⏸ ▶ ⟲ ⬇ ↪ ↩ ■
5.3.4 单步键位表(背下来这张)
快捷键 动作 什么时候用
F5 Continue 继续运行 跑到下一个断点,或自由运行
F10 Step Over 单步跳过 遇到函数调用不进去,直接执行完看结果
F11 Step Into 单步进入 遇到函数调用跳进去看里面怎么跑的
Shift+F11 Step Out 单步跳出 跳进函数了但不想看,直接跑完返回上一层
Ctrl+Shift+F5 Restart 重启调试 想从头再来
Shift+F5 Stop 停止调试 结束这次会话
F9 Toggle Breakpoint 在光标所在行加/删断点

90% 的调试场景只用 F5 / F10 / F11 / F9 这 4 个键

5.3.5 断点

普通行断点

  • 编辑器行号左边点一下(空白处,不是行号本身)→ 出现红圆点 → 设断点
  • 或光标停在那行按 F9
  • 再点一下 / 再按一次 F9 → 取消

条件断点(RTOS 调试神器):

  • 右键红圆点 → Edit Breakpoint → 输入条件,如 thread_count > 5 → 只有命中这个条件才停
  • 用途:调度器跑了几百次才出问题时,用条件断点只停"出问题那次"

函数断点(不用先翻代码找行号):

  • BREAKPOINTS 面板右上 + → 输入函数名,如 PendSV_Handler → 任何跳进这个函数的时刻都会停
  • 用途:第 6 周读 PendSV 时用这个比翻源码找行号快十倍

数据断点 / 观察点(Data Breakpoint)

  • VARIABLES 面板右键变量 → Break on Value Change
  • 用途:某个变量被改了但不知道谁改的——例如 RTOS 的 osRtxInfo.thread.run.curr 被异常改写,用数据断点抓现场
  • ARMv8-M 只有 2–4 个硬件 watchpoint,省着用
5.3.6 六大观察窗口

按调试深度从浅到深排:

① VARIABLES(左侧面板上方)

  • 自动显示当前作用域的局部变量和函数参数
  • 鼠标悬停在代码里的变量上也能看到值
  • 右键 → Set Value 可以直接改变量,不用重编译

② WATCH(左侧面板中部)

  • 加入想持续追踪的表达式:osRtxInfo.thread.run.curr->name*(volatile uint32_t*)0xE000ED08(VTOR 地址)、HAL_GetTick()
  • 每次停下都会刷新
  • RTOS 学习主力窗口

③ CALL STACK(左侧面板顶部)

  • 显示调用链:PendSV_Handler → osRtxPendSV → osRtxThreadSwitch → ...
  • 点任意一层会切到那层的源码和局部变量——读 RTX5 源码时极其关键

④ Cortex Registers(点左侧面板里的 XPERIPHERALS → Cortex Live Watch,或 Ctrl+Shift+P → Cortex-Debug: Registers

  • 显示 R0–R15、xPSR、MSP、PSP、PRIMASK、BASEPRI、FAULTMASK、CONTROL
  • RTOS 内核学习的核心观测窗口。第 2 周要看的"上电 MSP 初值从哪里来"就靠这个

⑤ Cortex Peripherals(SVD 外设视图)

  • 左侧面板下方 CORTEX PERIPHERALS → 展开 NVIC / SCB / SysTick / GPIO
  • 能看到每个寄存器的每个位域的名字和当前值
  • 第 4 周讲 SCB->CCR.STKALIGNSCB->VTOR、第 5 周讲 NVIC 优先级都要反复用
  • 没有 svdFile 这个窗口就是空的

⑥ Disassembly 反汇编视图

  • Ctrl+Shift+POpen Disassembly View
  • 显示当前 PC 附近的反汇编(带源码交叉对照)
  • 按 F10 会进行"汇编级单步",一条 Thumb 指令一步
  • 第 6 周读 PendSV_Handler 汇编时必开
5.3.7 第一次调试练习(不写代码,只练操作)

照做一遍,熟悉手感:

  1. F5 启动,停在 main() 第一行
  2. 打开 Cortex Registers 窗口,记下当前 MSP 和 PSP 的值(写在纸上)
  3. 打开 Cortex Peripherals → 展开 SCB → 找到 VTOR,记下它的值(应该是 0x08000000,你的 Flash 起始地址)
  4. HAL_Init(); 那行按 F9 加断点
  5. F10 几下,看黄色箭头一行一行往下走
  6. F5 继续,停在 HAL_Init() 那行
  7. F11 进入 HAL_Init,看 CALL STACK 多了一层
  8. Shift+F11 跳出
  9. Shift+F5 结束

做完这 9 步,你就掌握了 80% 的日常调试操作。第 2 周我们会把步骤 2 和 3 的观测升级——用它们来解剖整个启动过程。

5.3.8 调试常见错误
报错 原因 解决
No ST-LINK detected ST-LINK 被 CubeProgrammer / CubeIDE 占用 关掉那些窗口
Error: unable to open CMSIS-DAP device 驱动没装 / 线松了 重插 USB、装 CubeProgrammer(带驱动)
F5 启动后停在汇编而非 C 代码 elf 里没调试符号 确认 Build Type 是 Debug 不是 Release
单步时光标乱跳 Release 优化 / -O2 级别高 同上,切回 Debug
svdFile not found SVD 文件路径错 下载 STM32L552.svd 放到 ${workspaceFolder}
断点打了没停 代码被优化掉了 / 编的不是当前 elf 清 build 目录重新编

六、验证:让 LD1 闪起来

默认 CubeMX 模板不会自动闪灯,需要手动改一行。打开 Core/Src/main.c,找到 while (1) 主循环:

while (1)
{
    HAL_GPIO_TogglePin(LD1_GPIO_Port, LD1_Pin);
    HAL_Delay(500);
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
}

注意 要写在 USER CODE BEGIN 3USER CODE END 3 之间,否则下次 CubeMX 重新生成会清掉你的代码。

Build → Flash → 板子上 LD1(绿色)应该以 1 Hz 闪烁

看到 LD1 闪起来的那一刻,第 1 周的主线任务就完成了。


七、常见坑汇总

现象 原因 解决
Arm Tools: 0 CubeCLT 路径没识别 手动 Set up STM32CubeCLT path,或加环境变量
VSCode 编译时一堆 undefined reference TrustZone 被意外开启,生成了两套工程 CubeMX 里 TrustZone→Disabled,重新生成
工程文件名带 _NS 后缀 同上 同上
烧录失败"Target not found" USB 线不对 / 插错 USB 口 换数据线 / 插 CN1
clangd 找不到头文件 没有 compile_commands.json Build 一次后 clangd 会自动识别;或在 settings 里指到 build/Debug/compile_commands.json
LD1 不闪 CubeMX 默认没有闪灯代码 上面第 6 节手动加
调试进不了 main,停在 Reset_Handler 正常!启动代码在 startup_stm32l552xx.s,单步 si 能一路走到 main ——
Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐