
目录参考1.准备工作1.安装STM32CubeCLT1. 它里面包含了什么2. 它解决什么痛点2.安装CLion2.第一个项目1.新建项目生成CMake项目2.配置工具链3.添加调试服务器4.编译运行问题 1.提示调试器固件版本太低3. 文件结构解析1. 核心代码与驱动文件夹2. 编译与构建配置文件核心纽带3. 硬件与芯片个性的配置文件4. CubeMX 专用文件4.工具链解析CMakeNinja它和 Make 是什么关系arm-none-eabi-gcc命名解析为什么STM32不需要专用编译器硬件的差异交给“链接脚本”和“寄存器头文件”编译大小对比参考【爽手把手教你用CLion开发STM32【大人时代变啦】】 https://www.bilibili.com/video/BV1pnjizYEAk/?share_sourcecopy_webvd_source76327fb2011d9dba418b60898e94496e建议主要参考B站视频再结合本文。1.准备工作1.安装STM32CubeCLTSTM32CubeCLT全称STM32Cube Command-Line Toolset即 STM32Cube 命令行工具集是意法半导体ST官方推出的一款纯命令行、轻量化的嵌入式开发工具链整合包。简单来说它是把官方胖大臃肿的 STM32CubeIDE 剥掉图形界面GUI外壳后剩下的纯粹底层核心工具。如果你不想用 ST 官方那个基于 Eclipse 的 STM32CubeIDE而是想用VSCode、CLion或者搭建自动化编译链那么这个工具包就是为你量身定制的。1. 它里面包含了什么作为一个“全家桶”STM32CubeCLT 把原本散落在各处的编译和烧录工具打包在了一起免去了你一个一个去单独下载、配环境变量的痛苦GNU Toolchain for STM32官方魔改/优化过的arm-none-eabi-gcc编译器、链接器等。GDB Debugger调试器客户端GDB Client以及配合 ST-LINK 使用的 GDB 服务端GDB Server。STM32CubeProgrammer (CLI 版本)ST 官方的烧录和内存管理工具。你可以通过命令行直接一键擦除、擦写芯片的 Flash 或外部存储。完整的 SVD 文件库 (System View Description)包含了整个 STM32 MCU 家族的芯片寄存器映射描述文件。CLion 或 VSCode 的调试插件如 Cortex-Debug必须要读取这类文件才能在调试时让你直观地看到每个外设寄存器如 GPIOA_ODR的当前十六进制值。2. 它解决什么痛点在 STM32CubeCLT 推出之前如果你想用 VSCode 或 CLion 开发 STM32你通常需要去 Arm 官网下载 GCC 工具链。去 ST 官网下载单独的 STM32CubeProgrammer。去一些开源社区或者把 CubeIDE 装上强行把里面的.svd文件拷出来。手动把这堆东西的路径一个一个配进环境变量。一旦工具版本更新或者团队换了新电脑、甚至在 Linux 服务器上跑自动化 CI/CD 构建这套环境就要重新配一遍极其痛苦。而STM32CubeCLT 实现了“一次安装全部搞定”。它支持 Windows、macOS 和 Linux提供标准化的安装路径和元数据第三方 IDE比如新版本的 CLion 或 VSCode 插件能够非常轻松地自动检测到它。----------------------------------- | 1. 图形化配置 初始化代码生成 | -- STM32CubeMX (生成 CMake 项目) ----------------------------------- | v ----------------------------------- | 2. 代码编写、重构与语义分析 | -- CLion / VSCode (前端 IDE) ----------------------------------- | v ----------------------------------- | 3. 底层编译、烧录、GDB 调试和 SVD | -- STM32CubeCLT (后端工具链) -----------------------------------2.安装CLionCLion: A cross-platform IDE for C and CCLion对个人用户免费直接去官网下载即可。2.第一个项目1.新建项目生成CMake项目需要先由STM32CubeMx生成一个项目再在CLion中指定文件夹。2.配置工具链选择STM32CubeCLT中的各项工具即可默认的Mingw同样可以编译运行因为它会自己扫描并选择合适的工具链。3.添加调试服务器在调试服务器里面新增 ST-LINK调试服务器。4.编译运行小锤子构建绿色的三角形运行问题 1.提示调试器固件版本太低执行脚本可能会提示 DFU mode问题。此时把STLink从电脑上移除重新再插入即可。成功3. 文件结构解析Keil项目CMake项目1. 核心代码与驱动文件夹Core/项目的核心应用层。这里存放的是你作为开发者需要频繁修改的业务代码。里面通常包含Src/main.c程序的入口。Src/stm32f1xx_it.c中断服务函数Interrupt Service Routines。Src/gpio.c、usart.c等由 CubeMX 自动生成的、对应你在图形界面配置的外设初始化代码。Drivers/底层驱动库。这里是底层的基石你一般不需要也不应该去修改里面的内容。它包含CMSISARM 官方提供的 Cortex-M 核标准接口提供底层寄存器定义和核心 CPU 控制指令。STM32F1xx_HAL_DriverST 官方的 HAL 库硬件抽象层。你代码里调用的HAL_GPIO_WritePin等函数实现都在这里。cmake/CMake 辅助脚本目录。这里通常存放着由 CubeMX 自动生成的特定工具链配置文件比如corrosion.cmake或寻找 ARM 编译器的gcc-arm-none-eabi.cmake用来辅助主 CMake 脚本找到你的编译器。2. 编译与构建配置文件核心纽带CMakeLists.txt整个项目的“总设计图”。这是 CLion 能识别并编译这个项目的核心。它用文本形式规定了使用什么编译器arm-none-eabi-gcc。要编译哪些.c文件包含哪些.h头文件路径。开启什么优化等级如你的-Os或Release模式以及定义了哪些全局宏如USE_HAL_DRIVER。CMakePresets.jsonCMake 预设配置文件。这是现代化 CMakeCMake 3.19引入的标准。它为 CLion 等工具直接提供了“一键配置”的方案里面记录了诸如Debug、Release的构建目录如build/debug应该怎么配置让 IDE 打开项目时能自动识别并配置好编译环境。3. 硬件与芯片个性的配置文件startup_stm32f103xe.s汇编启动文件。单片机上电后执行的第一行代码就在这里Reset_Handler。它负责初始化堆栈指针SP。把 FLASH 中的已初始化变量RW-data搬运到 RAM。把未初始化变量区ZI-data/BSS清零。最后跳转到你的main()函数。(注文件名中的xe代表该芯片属于大容量产品即 High-density拥有 512KB FLASH正符合你上一发问中的容量)STM32F103VETx_FLASH.ld链接脚本Linker Script。我们在前面提到的“内存地图”。它明明白白地告诉链接器这颗STM32F103VET芯片的 FLASH 起始于0x08000000大小 512KRAM 起始于0x20000000大小 64K。GCC 链接时会严格按照这个规则来把代码摆放到固件对应的位置。4. CubeMX 专用文件LED.iocCubeMX 图形化配置文件。双击它可以直接唤醒 STM32CubeMX 软件。如果你以后想修改引脚配置比如再加个串口、改个时钟频率直接在图形界面里改改完后点击Generate Code它会自动刷新上述的CMakeLists.txt、Core/和Drivers/而不会破坏你自己写的代码。.mxprojectCubeMX 的内部描述文件。隐藏文件或系统文件记录了生成这个工程时 CubeMX 所使用的库版本、MCU 型号等底层控制文本供 CubeMX 自身再次读取时使用开发者无需理会。4.工具链解析CMakeC/C无论是电脑程序还是 STM32 裸机项目一旦变大会有无数的.c文件、.h头文件、第三方库。如果直接去调用 GCC你要写一长串长达几百个字符的疯狂命令。CMake 的工作人类写一个CMakeLists.txt。CMake 读取它去检查你的电脑上有没有安装交叉编译器、头文件在哪里。它的输出物它自己不编译任何一行 C 代码。它唯一的任务是把人类好理解的配置翻译成构建工具Make 或 Ninja能看懂的“低级脚本”如果你指定了 Make它就生成Makefile。如果你指定了 Ninja它就生成build.ninja。NinjaNinja 是一个为“速度”而生的构建系统。它的核心目标只有一个尽可能快地只构建那些需要重新构建的代码。可以把它理解成一个“超级高效的调度员”或“任务执行专家”。它和 Make 是什么关系你可能知道makeNinja 和make是同一类工具但设计哲学完全不同特点MakeNinja设计目标功能丰富语法灵活方便人直接编写极致的速度尤其针对大型项目可读性Makefile 语法相对友好人可以写build.ninja 语法极简但不适合人写智能程度内置很多规则会自动推导非常“笨”几乎不做任何推导完全听命于输入文件并行编译支持 (-j)但效率一般原生优秀能充分利用多核 CPU典型用途中小型项目或作为 CMake 的生成目标作为 CMake 的生成目标用于大型项目一句话总结Make 像一把瑞士军刀功能多但不够快Ninja 像一把专门的手术刀只做一件事——飞快地执行构建命令。Ninja 的速度优势来自它的“笨”不推导依赖它不会自己去猜foo.c包含了哪个头文件。所有依赖关系都由 CMake 这样的“上级生成器”提前算好写在build.ninja文件里。Ninja 只是照单执行。极简解析它的配置文件语法非常简单解析速度飞快。完美的并行它天生被设计为并行运行能毫无浪费地让你的 CPU 所有核心满负荷工作。对于一个大型项目比如 Chromium 浏览器、LLVM 编译器或一个庞大的 STM32 工程使用 Ninja 比使用 Make 可以节省 30% - 70% 的编译时间。arm-none-eabi-gcc命名解析arm-none-eabi-gccarm目标芯片是 32 位的 ARM 架构如 Cortex-M 系列。none供应商Vendor未指定或者是无意义的因为不需要厂商定制的系统库。eabiEmbedded ABI嵌入式应用程序二进制接口。这意味着它使用的是针对资源受限环境的裸机标准标准 C 库通常采用非常精简的newlib而不是体积庞大的 Linuxglibc。注意中间少了一段因为没有操作系统所以“操作系统”那一栏直接被跳过了。arm-linux-gnueabihf-gccarm目标芯片是 32 位的 ARM 架构如 Cortex-A7/A9 等支持运行 Linux 的全功能处理器。linux明确指定目标操作系统是Linux。编译器在编译时会注入 Linux 的系统调用接口System Calls并且链接标准 Linux C 库glibc。gnu符合 GNU 规范。eabihfEABI Hard-Float硬浮点。表示目标 CPU 带有硬件浮点运算单元VFP/NEON编译器会直接生成硬件浮点指令运算速度极快。为什么STM32不需要专用编译器其实你的直觉非常敏锐在过去很长一段时间里单片机开发确实极度依赖“专用编译器”。如果你接触过 51 单片机或者用过早期的 PIC、AVR 芯片你会发现它们都有自己专属的、甚至需要高昂授权费的专用编译器比如 Keil C51、IAR 等。如果你把编译 51 的编译器拿去编译 AVR那是绝对行不通的。那为什么到了 STM32一款开源、通用的 GCC 编译器就能包办一切呢这里面主要有两个时代背景和底层架构的变革根本原因ARM 公司改变了游戏规则统一的架构在 STM32 诞生之前单片机市场是一片“百家争鸣”的混战状态。每家芯片厂商都有自己独特的指令集架构ISA意法半导体ST早期有自己的单片机内核。微芯Microchip有 PIC 架构。爱特梅尔Atmel有 AVR 架构。因为每个芯片的硬件电路和指令比如怎么做加法、怎么跳转完全不一样所以编译器必须针对特定的芯片“私人订制”。改变发生在这个男人手里ARM。ARM 自己不卖芯片只卖授权。它设计出了大名鼎鼎的Cortex-M内核STM32 的心脏。ST意法半导体不需要自己设计 CPU 核心了直接从 ARM 买来内核外面包上自己的外设如 GPIO、定时器、ADC就组装成了 STM32。由于市场上无数厂商ST、GD、NXP、TI都在用同一种 ARM Cortex-M 内核这就意味着它们的底层指令集是完全统一的。这时候开发一款通用的、支持 ARM 指令集的编译器其性价比和受众群体就会变得无比巨大。GCC 的精妙设计前端与后端的“解耦”GCCGNU Compiler Collection之所以被称为“通用编译器”并不是说它有一套魔法能直接把 C 语言变成所有芯片的机器码而是因为它在架构设计上玩了极高明的模块化分工。GCC 的编译过程是分层的编译器前端Frontend负责死抠 C/C 语法。它把你的 C 语言代码检查一遍看看有没有少分号变量有没有定义然后把它翻译成一种与芯片无关的中间语言称为 IR。优化器Optimizer在中间语言的基础上进行代码优化比如死代码消除、循环展开。编译器后端Backend这是关键后端专门负责把中间语言翻译成特定芯片的机器码。开发一个新的芯片架构GCC 官方或者芯片大厂比如 ARM 官方的工程师只需要重写 GCC 的后端代码告诉它怎么把中间语言翻译成 ARM 汇编指令。因此arm-none-eabi-gcc并不是一个拍脑袋凭空出现的独立编译器它本质上是“GCC 的通用前端 ARM 官方贡献的 ARM 后端插件”。硬件的差异交给“链接脚本”和“寄存器头文件”你可能会问“既然编译器是通用的那它怎么知道我这款 STM32 有多少 Flash怎么知道引脚寄存器的地址在哪”通用编译器只负责把 C 语言逻辑翻译成通用的 ARM 指令比如寄存器 R0 和 R1 相加。至于具体到某一款具体型号比如STM32F103C8T6的硬件个性是通过两份文本文件塞给编译器的寄存器映射头文件如stm32f103xb.h它用 C 语言的结构体和宏定义把芯片手册上的物理硬件地址绑定到代码里的变量上。// 告诉编译器GPIOA 这个结构体指针指向 0x40010800 这个物理地址 #define GPIOA ((GPIO_TypeDef *) 0x40010800)链接脚本.ld文件Linker Script这是通用的arm-none-eabi-ld链接器必须读取的文件。它用纯文本明确勾勒出这颗具体芯片的“内存地图”MEMORY { FLASH (rx) : ORIGIN 0x08000000, LENGTH 64K RAM (xrw) : ORIGIN 0x20000000, LENGTH 20K }有了这两个东西通用的 ARM 编译器就能完美适配成千上万种不同的 ARM 芯片而不需要为每款芯片单独写一个编译器程序。编译大小对比Program Size: Code2700 RO-data372 RW-data16 ZI-data1632Memory region Used Size Region Size %age UsedRAM: 1584 B 64 KB 2.42%FLASH: 4920 B 512 KB 0.94%物理资源Keil 编译结果CLion (Debug)CLion (Release) FLASH 占用3088 B4920 B3988 B缩减 932 字节RAM 占用1648 B1584 B1584 B① 为什么 CLion (GCC) 的 FLASH 大了近 1.8 KB这里大概率是由两个原因引起的C 标准库的实现体积差异最主要原因Keil默认开启了Microlib微库。Microlib 是 ARM 专门为微控制器深度定制的精简标准库它里面的printf、memcpy、数学函数等体积极其小巧。CLion (GCC)默认使用的是newlib或newlib-nano。虽然newlib-nano已经做过裁剪但它的体积尤其是涉及格式化输出、底层桩函数_sbrk等系统调用逻辑时依然会比 Keil 的 Microlib 稍微臃肿一些。优化等级Optimization Level不同Keil 可能开启了针对代码体积的优化如-O3或-Os。CLion 默认的 CMake 配置文件如果是Debug模式-O0GCC 会生成大量冗余的调试指令和未优化的代码。如果是Release模式-Os这个差距会明显缩小。