使用Clion配置STM32标准库
sdw4agl
编辑于 2026年03月07日 14:57
收录于文集
共5篇

一、前言

        最近正在学习STM32,由于我是小白水平,并且已经习惯了在Clion使用vim插件,所以在Keil里面写代码实在是不习惯,才开始在网上找前辈的文章配置Clion。本文参考和引用了以下前辈的文件 ,在此致谢!本文只作功能实现,不讲原理,因为我也不会。

1、知乎:【教程】手把手教你用Clion进行STM32开发【如何优雅の进行嵌入式开发】

2、Clion实现Stm32标准库开发​

3、在CLion中进行STM32F1系列标准库开发,解决printf问题并添加CMSIS-DSP库​

二、环境搭建

1、Clion安装

        网上教程很多,请自行安装。

2、下载安装STM32CubeMX

        STM32CubeMX6.11.0不能打开升级6.5.0版本的项目。请移步官网自动下载安装,https://www.st.com/en/development-tools/stm32cubemx.html

3、下载OpenOCD

        OpenOCD是用于对STM32进行下载仿真的开源软件包,我使用的版本是20231002,将文件夹解压放在无空格、无中文的路径下。

下载地址:https://gnutoolchains.com/arm-eabi/openocd/。

4、链接OpenOCD和STM32CubeMX

        在Clion软件中操作FIle-->Setting-->Build,Execution,Deployment

单击后面的Test按钮,报绿色说明运行正常.

4、安装gcc-arm-none-eabi

        选择"Windows 32-bit ZIP package"格式下载。文件夹解压放在无空格、无中文的路径下。

下载地址:https://developer.arm.com/downloads/-/gnu-rm,

添加系统环境变量:

在CMD窗口中输入arm-none-eabi-gcc -v,有返回信息说明安装成功。

5、安装MinGW(可以不安装)

最新版的Clion内已经内置了MinGW环境,也可以手动设置。

下载地址:https://sourceforge.net/projects/mingw-w64/files/,选择图示版本:

文件夹解压放在无空格、无中文的路径下。将WinGW的bin文件夹添加至系统环境变量。在CMD窗口中输入gcc -v,有返回信息说明安装成功。

6、配置Clion

File-->Build,Execution,Deployment-->Toolchain

如果有多个编译配置,记得将准备使用的配置移至最上部。

外部MinGW

内置MinGW

外部Build Tool

外部Debugger

Build Tool选择内置“Detected:ninja.exe”网上说会编译速度会比mingw32-make快,未测试。还有一个区别是:

ninja

mingw32-make

虽然使用ninja未在窗口中输出显示hex和bin文件,但是在项目文件夹的依然会生成这两个文件。

Debugger:选择内置“Bundled GDB”网上说可以使用Clion的调试功能,暂时未测试。

三、创建STM32CubeMX代码

1、新建文件夹STM32_Stdlib_0605_0329

2、新建项目

第一次启动STM32CubeMX时,有可能会失败,没有IOC文件,此时需重新创建项目。

创建项目成功后,会弹出“Select Board Config File”,选“Skip”。

3、打开STM32CubeMX

4、选择MCU型号

双击图示位置,在弹出的新窗口中选择MCU型号;

双击确认

5、项目管理

Project Name:输入新创建的文件夹;

Toolchain/IDE:

    STM32CubeMX6.5.0-->SW4STM32

    STM32CubeMX6.11.0-->STM32CubeIDE

Minimum Heap/Stack Size:默认够用,对应Keil启动文件内的Stack_Size和Heap_Size。

分别放置头文件和源文件

单击右上角的“GENERATE CODE”创建代码。

重写IOC文件

Yes

Close

四、修改标准库文件

1、下载正点原子模板源码

https://pan.baidu.com/s/1qZgG-UAznhQWgGcX8yFduQ?pwd=zz5e

下载“A盘”-->4、程序源码-->2、标准例程-库函数版本.zip,解压缩得到“实验0-1 Template工程模板-新建工程章节使用”文件夹备用。

2、添加文件

        将STM32_Stdlib_0605_0329中STM32CubeMX生成的“Core/Src/syscalls.c”文件另存为UTF-8编码格式,备用。

STM32CubeMX 6.5.0:

        (1)、将“Core/Startup”文件夹放到STM32_Stdlib_0605_0329目录下,删除“Core”目录。

        (2)、将“Drivers”目录删除;

        (2)、把Template工程模板的“CORE”、“STM32F10x_FWLib”、“SYSTEM”和“USER”四个目录复制到STM32_Stdlib_0605_0329目录下。

        (3)、“STM32F10x_FWLib”文件夹不修改;

        (4)、将无用文件删除,将头文件和源文件分别放置。

STM32Cube 6.11.0:

        (1)、将工程目录下的“Drivers”、“CORE/Inc”、"CORE/Src"三个目录删除;

        (2)、把Template工程模板的“STM32F10x_FWLib”、“SYSTEM”和“USER”三个目录复制到STM32_Stdlib_0605_0329目录下;

        (3)、“STM32F10x_FWLib”文件夹不修改;

        (4)、把Template工程模板CORE目录下的“Inc”、“Src”目录复制到“STM32_Stdlib_0605_0329/CORE”目录下;

        (5)、其余3个文件夹将无用文件删除,将头文件和源文件分别放置。

3、修改CMakeLists.txt文件

 STM32CubeMX 6.5.0修改内容:

代码块
clike
自动换行
复制代码
//第52行修改为以下内容
include_directories(Core/Inc SYSTEM/Inc STM32F10x_FWLib/Inc USER/Inc) 
//第56行修改为以下内容
file(GLOB_RECURSE SOURCES "Startup/*.*" "CORE/*.*" "SYSTEM/*.*" "STM32F10x_FWLib/*.*" "USER/*.*")
复制成功

STM32CubeMX 6.11.0修改内容:

代码块
clike
自动换行
复制代码
//第52行修改为以下内容
include_directories(Core/Inc SYSTEM/Inc STM32F10x_FWLib/Inc USER/Inc)
//第56行修改为以下内容
file(GLOB_RECURSE SOURCES "Core/*.*" "SYSTEM/*.*" "STM32F10x_FWLib/*.*" "USER/*.*")
复制成功

这一步很重要,每次修改都要更新。

Load CMake Changes

4、修改STM32F103RETX_FLASH.ld文件(6.5.0无需此步)

        STM32CubeMx 6.11.0 生成的 .ld 文件中含有 READONLY 关键字,此关键字只能在 gcc 11 版本及以后使用,gcc 10及以下版本解析不了报错(在后面生成的注释中也有说明),所以需要将.ld 文件,删除所有 “(READONLY) ”字段删除。

援引的文章:https://www.cnblogs.com/XuYuFan/p/18094079

5、修改USER/Inc/stm32f10x.h文件

代码块
clike
自动换行
复制代码
#ifndef __STM32F10x_H
#define __STM32F10x_H

//在原文件第52行增加以下内容
//#define STM32F10X_LD	//16KB≤FLASH≤32KB
//#define STM32F10X_MD	//64KB≤FLASH≤128KB
#define STM32F10X_HD	//256KB≤FLASH≤512KB
#define USE_STDPERIPH_DRIVER
//以上为增加的内容

#ifdef __cplusplus
 extern 
复制成功

6、修改CORE/Src/core_cm3.c文件

7、将SYSTEM/Src/sys.c文件修改为以下内容

8、编译工程

如果编译按钮是灰色的,可清除工程缓存。

五、使用ST-Link下载程序

在STM32_Stdlib_0605_0329目录下新建ST_Link.cfg文件

代码块
clike
自动换行
复制代码
# choose st-link/j-link/dap-link etc.\
# adapter driver cmsis-dap
# board:板卡配置,各种官方板卡
# 路径在OpenOCD安装目录的share\openocd\scripts下:
# interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面
# target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面
# 设置好配置文件之后,就可以点击下载或者调试按钮进行下载和在线调试了。
# 在配置文件中不要加reset_config srst_only这一句,会导致下载失败,这一句是指示系统重启的,删除不影响下载。
source [find interface/stlink.cfg]
transport select hla_swd
# 0x10000 = 64K Flash Size
# 0x100000 = 1024k Flash Size
# set FLASH_SIZE 0x100000
source [find target/stm32f1x.cfg]
# download speed = 10MHz
adapter speed 10000000
//实物测试通过
复制成功

单击右上角编译图标右侧的下三角,选择“Edit Configurations”

单击左上角“+”新建配置,选择“OpenOCD Download & Run”

选择板配置文件

单击Clion右上角的运行图标下载程序。

六、串口输入输出的实现

1、在"SYSTEM/Inc"下新建“retarget.h”文件。

代码块
clike
自动换行
复制代码
#ifndef _STDUSART_RETARGET_H__
#define _STDUSART_RETARGET_H__

#include <stdio.h>
#include "stm32f10x_usart.h"

#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */

#ifdef __GNUC__
#define GETCHAR_PROTOTYPE int __io_getchar (void)
#else
#define GETCHAR_PROTOTYPE int fgetc(FILE * f)
#endif /* __GNUC__ */

/**
 * @brief 注册重定向串口
 * @param usartx 需要重定向输入输出的串口
 */
void RetargetInit(USART_TypeDef *huart);
void USART1_Config(unsigned int bound);
#endif //_STDUSART_RETARGET_H__
复制成功

2、在“SYSTEM/Src”下新建“retarget.c”文件。

代码块
clike
自动换行
复制代码
#include "retarget.h"

/* 定义USART端口,用于注册重定向的串口(也可以用UART,根据实际情况来改写) */
static USART_TypeDef *sg_retargetUsart;

/**
 * @brief 注册重定向串口
 * @param usartx 需要重定向输入输出的串口
 */
void RetargetInit(USART_TypeDef *usartx) {
    /* 注册串口 */
    sg_retargetUsart = usartx;

    /* Disable I/O buffering for STDOUT stream, so that
     * chars are sent out as soon as they are printed. */
    setvbuf(stdout, NULL, _IONBF, 0);
    /* Disable I/O buffering for STDIN stream, so that
     * chars are received in as soon as they are scanned. */
    setvbuf(stdin, NULL, _IONBF, 0);

}

/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE {
    /* 发送一个字节数据到串口 */
    /* 很简单,直接调用库函数中的 串口发送数据函数 */
    USART_SendData(sg_retargetUsart, (uint8_t) ch);

    /*等待发送完毕*/
    while (USART_GetFlagStatus(sg_retargetUsart, USART_FLAG_TXE) == RESET);
    /* 返回发送的字符 */
    return ch;

}

/**
  * @brief  Retargets the C library scanf, getchar function to the USART.
  * @param  None
  * @retval None
  */
GETCHAR_PROTOTYPE {

    /* 很简单,直接调用库函数中的接收 */
    /* 等待串口输入数据 */
    while (USART_GetFlagStatus(sg_retargetUsart, USART_FLAG_RXNE) == RESET);
    /* 直接返回接收到的字符 */
    return (int) USART_ReceiveData(sg_retargetUsart);

}
void USART1_Config(unsigned int bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    /*使能端口时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    /*USART1-TX输出脚配置*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /*USART1-RX接收脚配置*/
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /*串口参数配置*/
    USART_InitStructure.USART_BaudRate = bound;//设置波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//一帧中传输或者接收的数据位数(8位/9位)
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//定义发送的停止位个数
    USART_InitStructure.USART_Parity = USART_Parity_No ;//设置为不需要校验
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//不使用流控制模式
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送且接收使能
    USART_Init(USART1, &USART_InitStructure);

    /*使能接收终端*/
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    /*配置好参数之后,使能USART1外设*/
    USART_Cmd(USART1, ENABLE);
    /*USART1终端优先级的配置*/
}
void NVIC_Configuration(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;
    /*设置优先级分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//指定IRQ通道
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//指定先占优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//从优先级

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //定义的IRQ是被使能还是失能
    NVIC_Init(&NVIC_InitStructure);
}

/*重定向c库函数printf到USART1,调用printf会将需要打印的数据从串口发送出去*/
int fputc(int ch, FILE *f)
{
    USART_SendData(USART1, (unsigned char) ch);
    while( USART_GetFlagStatus(USART1,USART_FLAG_TC)!= SET);	 //等到USART1发送完成
    return (ch);
}

/*串口发送一个字符*/
void USART_SendChar(USART_TypeDef* pUSARTx, uint8_t c)
{
    USART_SendData(pUSARTx, c);
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/*
*串口发送一个字符串*/
void USART_SendString(USART_TypeDef* pUSARTx, char* str)
{
    uint32_t n = 0;

    while (*(str + n) != '\0')
    {
        USART_SendChar(pUSARTx, *(str + n));
        n++;
    }
    while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}

/*
int main(void)
{
    USART1_Config(9600);
    NVIC_Configuration();
    printf(“********************************”);
    printf(“hello ward!\r\n”);
    printf(“********************************”);
    While(1){} //阻止程序结束

}
 */
复制成功

3、将“SYSTEM/Src/usart.c”中的fputc函数段注释掉。

4、将第四章第二步中备用的文件“syscalls.c”文件放到“CORE/Src”文件夹中。

现在就可以使用getchar,scanf,printf函数了。

七、修改项目生成目标名称

1、修改CMakeLists.txt文件第18行

project(Target_Name C CXX ASM)

2、重新CMake

如果没有CMake选择,可清除工程缓存。

3、重新编译文件,Build-->Build Project。

此时右上角的编译按钮仍是不可用,修改“Run/Debug Configurations”中的“Target”,选择新生成的elf文件即可正常。