【STM32】CLion STM32开发环境搭建
CLion STM32 开发环境搭建
目录
- CLion STM32 开发环境搭建
- 1. 开发环境概览
- 2. 环境搭建
- 1. 安装 CLion
- 2. 安装 STM32CubeMX
- 3. 安装 STM32CubeCLT
- 4. 安装OpenOCD
- 3. CLion STM32环境配置
- 4. 使用STM32CubeMX生成模板工程
- 1. 配置要使用的外设
- 2. 配置时钟
- 3. 配置工程
- 5. CLion 打开STM32工程
- 1. 打开STM32CubeMX生成的工程
- 2. 选择工具链,并编译
- 3. 烧录 & 调试设置
- 6. printf 重定向输出
1. 开发环境概览
使用到的组件有:
| 组件 | 作用 | 推荐版本 |
|---|---|---|
| CLion | C/C++ IDE,支持CMake、调试 | ≥2023.3 |
| STM32CubeMX | STM32外设配置与代码生成 | ≥6.10 |
| ARM GNU Toolchain | 编译工具链 (arm-none-eabi) | 13.3.rel1 |
| OpenOCD | 调试与烧录工具 | ≥0.12.0 |
| ST-Link / DAP驱动配置 | 连接STM32 | 最新版 |
| CMake + Ninja | 构建系统 | CLion自带 |
| STM32Cube HAL 库 | 外设驱动库 | 含于CubeMX中 |
2. 环境搭建
1. 安装 CLion
下载地址:
👉 https://www.jetbrains.com/clion/
2. 安装 STM32CubeMX
下载地址:
👉 https://www.st.com/en/development-tools/stm32cubemx.html
作用:STM32 外设配置,用于生成 HAL 工程模板,输出工程类型选择 STM32CubeIDE 。
3. 安装 STM32CubeCLT
STM32CubeCLT集成了STM32开发过程中所需的一系列关键工具,下表概括了其主要组件和功能:
| 组件类别 | 工具示例 | 主要功能 |
|---|---|---|
| 编译器 | arm-none-eabi-gcc (GNU ARM编译器) | 将C/C++代码编译为STM32可执行的二进制文件(如.elf、.bin)。 |
| 调试器 | st-util (ST-Link工具) | 通过ST-Link与开发板通信,实现断点调试、变量监视等功能。 |
| 烧录工具 | stm32programmercli (命令行版STM32 Programmer) | 将编译生成的固件下载到STM32芯片的Flash中。 |
| 构建工具 | make、cmake、ninja | 解析项目的Makefile或CMakeLists.txt,自动化整个编译流程 |
其目录如下:

其中已经包括了要使用到的 ARM 编译器 arm-none-eabi 和底层构建工具 Ninja。
4. 安装OpenOCD
下载地址:Download OpenOCD for Windows
是一个开源工具,允许使用各种下载器使用 GDB 调试各种 ARM 设备,用于支持通过ST-Link / DAP / JLink 调试器下载并调试芯片。
功能和特点:
- 与调试器(ST-Link / J-Link / CMSIS-DAP)通信
- 控制目标 MCU(复位、读写内存、烧录 Flash)
- 为 GDB 提供远程调试接口(
localhost:3333) - 支持众多调试探头:ST-Link, J-Link, CMSIS-DAP, FTDI芯片等。
- 支持海量目标芯片:几乎覆盖了所有主流的ARM Cortex芯片(STM32, GD32, Nordic, NXP等),还支持RISC-V, MIPS等其他架构。
3. CLion STM32环境配置
在 CLion 环境中,选择 设置 配置工具链

添加上OpenOCD 调试工具路径:

4. 使用STM32CubeMX生成模板工程
打开 STM32CubeMX 选择芯片,这里使用stm32f103rct6为例子演示:
在STM32CubeMX中新建项目,选择芯片具体型号:STM32F103RCT6
1. 配置要使用的外设
⚠️ 这里别忘了使能调试接口,避免之后下载器没法识别设备。

2. 配置时钟
3. 配置工程
输出IDE 选择 STM32CubeIDE.

最后,点击生成代码,一个模板工程就创建好了。
5. CLion 打开STM32工程
1. 打开STM32CubeMX生成的工程
在CLion中打开工程目录,视图如下:

在选择配置文件时,先点击 跳过 。
2. 选择工具链,并编译
为项目选择上面设置的 工具链 ,并点击 build 图标对项目进行编译,可以看到编译成功。

3. 烧录 & 调试设置
在这里需要新建芯片配置文件,在文件根目录中新建一个配置文件,可以命名为 stm32f103rct6_config.cfg 文件命名不用有空格。

点击编译按钮旁边的配置栏下拉,选Edit Configurations,打开配置窗口:
选择上面新建的配置文件,并 确认 。

配置文件需要根据不同的调试器和芯片,创建对应的配置文件
如果使用的是自制DapLink作为调试器,文件的内容如下:
# choose st-link/j-link/dap-link etc.
adapter driver cmsis-dap
transport select swd# 0x40000 = 256K Flash Size
set FLASH_SIZE 0x40000source [find target/stm32f1x.cfg]# download speed = 10MHz
adapter speed 10000
如果是用ST-Link的话:
# choose st-link/j-link/dap-link etc.source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f1x.cfg]
# download speed = 10MHz
adapter speed 10000
前两行设置了仿真器的类型和接口,下面几行指定了Flash大小、芯片类型、下载速度等。
如果对自己的芯片不知道怎么设置,可以参考OpenOCD自带的一系列配置文件,路径在OpenOCD安装目录的share\openocd\scripts下:

target 目录下,可以看到很多支持的芯片型号,选择对应的配置文件即可。
只需要关注这几个目录:
- board:板卡配置,各种官方板卡
- interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面
- target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面
设置好配置文件之后,就可以点击下载或者调试按钮进行下载和在线调试了。
下载结果如下:
这里使用的时 CMSIS-DAP 进行下载和调试

调试界面如下:

在调试时,可以先将 STM32CubeCLT 中的 芯片对应的 SVD 文件复制到根目录下:
在调试时,添加对应的 .svd 外设文件:

6. printf 重定向输出
在Keil里面为了使用printf函数我们需要重定向fputc函数:
int fputc (int ch, FILE *f)
{(void)HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);return ch;
}
其中的FILE定义在stdio.h头文件中,所以需要在项目中包含这个头文件,但是经过测试发现,Keil里面包含的是MDK\ARM\ARMCC\include这个目录下的stdio.h,而在Clion中是不会链接到这个文件的。因此如果在Clion中也按之前的方法进行重定向,会发现printf没有任何输出。
在Clion中链接的是GNU-Tools-ARM-Embedded\arm-none-eabi\include里面的stdio.h,如果仍然想使用printf函数功能,则需要重新改变标准输入输出的函数定义,使其重定向到串口:
新建一个retarget.h文件内容如下:
#ifndef _RETARGET_H__
#define _RETARGET_H__#include "stm32f1xx_hal.h"
#include <sys/stat.h>
#include <stdio.h>void RetargetInit(UART_HandleTypeDef *huart);int _isatty(int fd);int _write(int fd, char *ptr, int len);int _close(int fd);int _lseek(int fd, int ptr, int dir);int _read(int fd, char *ptr, int len);int _fstat(int fd, struct stat *st);#endif //#ifndef _RETARGET_H__
retarget.c 文件定义如下:
#include <_ansi.h>
#include <_syslist.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/times.h>
#include <stdint.h>
#include "retarget.h"#if !defined(OS_USE_SEMIHOSTING)#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2UART_HandleTypeDef *gHuart;void RetargetInit(UART_HandleTypeDef *huart)
{gHuart = huart;/* 关闭标准输出 stdout 的缓冲机制* ,使 printf 等输出函数的内容 立即输出到终端,* 而不是等缓冲区满或遇到换行符才输出。. */setvbuf(stdout, NULL, _IONBF, 0);
}int _isatty(int fd)
{if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)return 1;errno = EBADF;return 0;
}int _write(int fd, char *ptr, int len)
{HAL_StatusTypeDef hstatus;if (fd == STDOUT_FILENO || fd == STDERR_FILENO){hstatus = HAL_UART_Transmit(gHuart, (uint8_t *) ptr, len, HAL_MAX_DELAY);if (hstatus == HAL_OK)return len;elsereturn EIO;}errno = EBADF;return -1;
}int _close(int fd)
{if (fd >= STDIN_FILENO && fd <= STDERR_FILENO)return 0;errno = EBADF;return -1;
}int _lseek(int fd, int ptr, int dir)
{(void) fd;(void) ptr;(void) dir;errno = EBADF;return -1;
}int _read(int fd, char *ptr, int len)
{HAL_StatusTypeDef hstatus;if (fd == STDIN_FILENO){hstatus = HAL_UART_Receive(gHuart, (uint8_t *) ptr, 1, HAL_MAX_DELAY);if (hstatus == HAL_OK)return 1;elsereturn EIO;}errno = EBADF;return -1;
}int _fstat(int fd, struct stat *st)
{if (fd >= STDIN_FILENO && fd <= STDERR_FILENO){st->st_mode = S_IFCHR;return 0;}errno = EBADF;return 0;
}#endif //#if !defined(OS_USE_SEMIHOSTING)
在CMakeLists.txt 文件中,添加这两个文件到工程,更新CMake,编译之后会发现,有几个系统函数重复定义了,被重复定义的函数位于Src目录的syscalls.c文件中,我们把里面重复的几个函数删掉即可。
这里在项目根目录下,新建了一个 target 目录,并把源文件放在改目录下:
在CMakeLists.txt中添加文件,如下:
在main函数的初始化代码中添加对头文件的引用并注册重定向的串口号:
#include "retarget.h"RetargetInit(&huart1);
示例:
在main函数中调用 RetargetInit(&huart1);
/* Initialize all configured peripherals */MX_GPIO_Init();MX_USART1_UART_Init();/* USER CODE BEGIN 2 */RetargetInit(&huart1);printf("hello world\n");float x = 122.44f;printf("float x = %0.2f",x);/* USER CODE END 2 */
输出结果:
hello world
float x = 122.44
结果正确,浮点数也可以打印。
备注:
如果上面的修改完成之后可能会发现无法正常读取浮点数,这里修改CMakeList.txt,加入下述编译选项
set(COMMON_FLAGS "-specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float")
