配置clion用于stm32开发
以stm32f103ZET6为例
2025.4.11更新
更改了部分内容,以适配软件更新。
2022.4.8更新(解决了OCD无法调试的问题)
之前配置完环境后,就只是实验了一下烧录的功能,一直没有尝试使用ocd的调试功能。
后来有,有人私信我,询问我ocd能否进行调试,这才发现我的ocd没办法进行调试。起初以为是ocd的版本和clion的版本不匹配造成的,后来经过实验并不是这样。最后,发现是我的ocd的安装目录中存在空格,导致我的ocd无法调试。更改完安装目录(记得把环境变量也改掉哦),重启电脑后,调试功能就能正常使用了。
0.开始之前
从接触单片机开始,基本就一直用着keil
在进行编程,中间又参加了智能车的比赛,了解到了IAR
、AURIX-studio
等IDE,但是始终没有开始学习C语言时,用的VS那种感觉。
之后,又尝试了一下VS
+VisualGDB
还有VScode
上面的Keil Assistant
的这款插件。
但是VScode
上时不时仍需要打开keil
,VisualGDB
用的破解版时不时出一些问题,正版太贵,属实买不起。
作为稚晖君的铁粉,自然知道dalao使用的是clion开发,正逢比赛延期,留出了些许空闲的时间,就基于稚晖君dalao的写的clion配置教程,折腾了一番。
虽说这篇文章可以说是保姆级教程了,可是我在配置过程中仍遇到了不少问题,花费了很多时间去查找解决方法,故特写此篇,以此记录。
1.配置前的准备
软件准备
- Windows 10
- STM32CubeMX
- clion2021
- MinGW
- OpenOCD
- arm-none-eabi-gcc
硬件准备
- STM32F103ZET6
- 正点原子的核心板
- J-Link
软件安装
安装的时候,注意安装路径上不要出现中文
-
STM32CubeMX
这是st开发的代码生成器,基于hal库的。正常安装就行。 -
OpenOCD
这是一个对stm32的下载调试仿真的开源工具包,经常见到,之前使用VisualGDB
时,也有见到过。下载好后,直接解压即可,无需安装。 -
MinGW
clion使用这个工具包的环境来配置工具链。安装完后,打开,进行组件的下载。右键单击选择Mark for Installation
,将Basic Setup
中的选项都勾选上。最后选择installation - Mark All Upgrades
即可完成组件的安装。如下图:
-
arm-none-eabi-gcc
这是交叉工具链??相当于一个编译器,将C/C++翻译成arm架构的芯片能懂的语言的编译器??(大概是这个意思)下载压缩包即可。
也可以像我这样,把上面这个四个软件放在一个文件夹下
2022.2.18
不要和我一样把OpenOCD的文件夹放在文件夹“Program Files”中,理由前面说过了。
- clion
这个的安装应该是没什么问题,当然clion
是收费的,我们学生的话可以去学信网下载学籍在线验证报告,然后在clion
官网上申请教育认证,一周左右应该就能申请下来,能免费使用一年。当然你也可以选择pojie版。
最后打开系统的环境变量(右击此电脑->属性->高级系统设置->环境变量),找到系统变量
中的PATH
变量,点击编辑,在当中新建
三个路径,分别是OpenOCD
、MinGW
和arm-none-eabi-gcc
三个软件安装目录下的bin
文件夹的安装路径。
重启电脑,打开命令行(win+R后,输入cmd),分别输入一下三行代码:
gcc -v
arm-none-eabi-gcc -v
openocd -v
如果都有输出,那说明环境安装成功。
2.Clion的配置
打开Clion
,选择Customize - All settings - Build,Execution,Deployment - Toolchains
,选择一下MinGW环境,应该会自动帮你找到的,如果找不到的话,就手动添加一下。
大致是:
- Environment:
...\MinGW
- Make:
...\MinGW\bin\mingw32-make.exe
- C Compiler:
...\MinGW\bin\gcc.exe
- C++ Compiler:
...\MinGW\bin\g++.exe
将Debugger
的路径改成...\gcc-arm-none-eabi\bin\arm-none-eabi-gdb.exe
这里和稚晖君的配置有所出入,我按照稚晖君的设置反而无法使用,而我这个配置在我的电脑上是可以使用的。
具体什么原因我也不清楚,我对于编译器啊工具链啊之类的知之甚少。
2025.4.11
新版本的clion中已经没有Make这一栏了,取而代之的是Build Tool这一栏,经测试这一栏保持默认不影响编译
然后点击CMake
,确认一下是否选择了刚才的配置
再选择Embedded Development
选项卡,设置一下OpenOCD
和CubeMX
的位置。一般为...\OpenOCD\bin\openocd.exe
和...\STM32CubeMX\STM32CubeMX.exe
。
点击一旁的Test
按键,确认一下路径是否正确。
如果出现绿色的提示框,则说明路径正确。
配置完成,关闭设置。
3.新建工程
点击Projects - New Project - STM32CubeMX
新建工程,例如:
点击create
后会生成一个.ioc
文件,这个文件跟使用STM32CubeMX
直接创建的是一样的,点击图中的链接可以跳转到STM32CubeMX
中打开这个ioc
文件:
默认选中的芯片型号是STM32F030F4Px
,我们可以在CubeMX
中重新选择自己需要的芯片。
这次实验选择的是原子的开发板,故我配置了两个GPIO口PB5
和PE5
用于流水灯,又配置了串口。相关参数见下图
需要注意的是,就是在下面的设置中项目名称和项目路径一定要和在Clion
中建立的一致,这样生成的工程文件才会覆盖Clion
中的文件,否则会另外生成一个文件夹,Clion
就无法读取了。
还有生成的IDE类型选择是SW4STM32
。
2025.4.11
新版本的STM32CubeMX 需选择
STM32CubeIDE
选项
每次修改完点Generate
之后,弹窗直接点Close
,Clion
里面会自动更新文件。
第一次设置完回到Clion会弹出一个板卡选择窗口:
国外的软件总喜欢选择开发板,这玩意儿不应该按芯片来选吗( ̄_, ̄ ),那个PlatformIO
貌似也是这样的,这个配置先不选,直接点Cancel
。
4.编译工程
编写代码的时候,把自己的代码写在
/* USER CODE BEGIN N */
// 在此处添加你的代码
/* USER CODE END N */
这类注释的中间,这样之后用CubeMX
重新生成文件时不会将你写的代码删除掉。
在IDE底栏的CMake选项卡中如果没有提示错误,说明工程配置就没问题了。
点击这个按钮可以更新CMake工程:
顶栏的三个图标分别是编译、下载、调试
随便写个流水灯的代码,点击编译,顺利的话,应该能看到下方命令行中编译的输出。
成功生成了用于烧录的hex
和bin
文件。
2025.4.11
最新的 STM32CubeMx 生成的 .ld 文件中含有 READONLY 关键字,根据注释中所说,该关键字只适用于gcc11以后的版本,gcc10和更早的版本需要将这个关键字去除
否则会报如下错误:
non constant or forward reference address expression for section .ARM.extab
5.烧录程序
之前使用的IDE有关烧录器的配置基本都是通过选项卡的形式配置的。而这里的配置需要依靠代码实现(貌似dalao们更钟爱使用代码来设置,VScode中都这样)。
点击编译按钮旁边的配置栏下拉,选择Edit Configurations
,打开配置窗口。
可以看到没有设置板子的config
文件所以出现警告错误,这个配置文件就是前面说的需要自己生成的文件。
因此,我们在工程根目录下新建一个configuration
文件夹,在里面新建一个配置文件jlink.cfg
,内容为
source [find interface/jlink.cfg]
transport select swd
source [find target/stm32f1x.cfg]
下面给出的是稚晖君给出的Dap-Link
和ST-Link
的配置文件内容:
Dap-Link
# choose st-link/j-link/dap-link etc.
adapter driver cmsis-dap
transport select swd
# 0x10000 = 64K Flash Size
set FLASH_SIZE 0x20000
source [find target/stm32f1x.cfg]
# download speed = 10MHz
adapter speed 10000
ST-Link
# choose st-link/j-link/dap-link etc.
#adapter driver cmsis-dap
#transport select swd
source [find interface/stlink.cfg]
transport select hla_swd
source [find target/stm32f1x.cfg]
# download speed = 10MHz
adapter speed 10000
其他的芯片或者下载器的配置,可以参考OpenOCD
自带的一系列配置文件,路径在OpenOCD
安装目录的share\openocd\scripts
下:
只需要关注这几个目录:
- board:板卡配置,各种官方板卡
- interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面
- target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面
设置好配置文件之后,就可以点击下载
或者调试
按钮进行下载和在线调试了。
在配置文件中不要加reset_config srst_only这一句,会导致下载失败,这一句是指示系统重启的,删除不影响下载。
如果你使用的J-Link
的话,正常情况下到了这一步,和我一样应该仍然无法下载,貌似是keil
的原因,或者是J-Link
本身的问题。需要先插上J-Link
,然后用zadig
这个软件,将J-Link
重新设置为普通USB设备。
选择Options - List All Devices
,选择J-Link
后,按下Reinstall Driver
即可。
只有就能正常使用了。(但这样Keil就识别不了这个jlink了,需要重新安装固件)
6.其他
关于CMake
CLion
中组织编译规则都是基于CMakeLists.txt
文件的,就是根据代码来配置工程,据说功能非常强大,用熟了之后应该还是非常方便的。不过那些个源代码目录和include文件夹路径的添加,简单看一下这个文件的内容还是很好辨认的。甚至,还有一份自动生成的CMakeLists_template.txt
文件可供参考学习。
include_directories(
Core/Inc
UserApp
// 其他include目录
)
file(GLOB_RECURSE SOURCES
"startup/*.*"
"Drivers/*.*"
"Core/*.*"
"UserApp/*.*"
"3rdParty/*.*"
// *.*表示通配符,也就是这个文件夹里的所有文件都会被编译
)
关于编译错误
这部分问题,我暂时没有遇到过,故全部摘自稚晖君的文章
- 如果移动了工程文件夹的话,最好打开
.ioc
文件重新Generate
一下再编译,可以解决很多错误。 - 遇到任何
CMake
相关的报错,一般是由于缓存没有更新引起的,可以在CLion
中选Tools-CMake-Reset Cache and Reload Project
即可解决。
关于printf的重定向
在正点原子给出的参考例程中,有关printf的重定向是将fputc
这个函数经行重定向,这个可以在正点原子的usart.c
文件中看到,如下
// 加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
// 标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
// 定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
// 重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
但若直接移植,在clion
中是无法使用的,稚晖君也解释了这一问题:
其中的
FILE
定义在stdio.h
头文件中,所以需要在项目中包含这个头文件,但是经过测试发现,Keil里面包含的是MDK\ARM\ARMCC\include
这个目录下的stdio.h
,而在Clion
中是不会链接到这个文件的。因此如果在Clion
中也按之前的方法进行重定向,会发现printf
没有任何输出。
在Clion
中链接的是GNU-Tools-ARM-Embedded\arm-none-eabi\include
里面的stdio.h
。
于是,需要新建一个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 <retarget.h>
#include <stdint.h>
#if !defined(OS_USE_SEMIHOSTING)
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
UART_HandleTypeDef *gHuart;
void RetargetInit(UART_HandleTypeDef *huart)
{
gHuart = huart;
/* Disable I/O buffering for STDOUT stream, so that
* chars are sent out as soon as they are printed. */
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;
else
return 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;
else
return 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)
添加这两个文件至工程,然后编译的时候会发现,几个函数在syscalls.c
文件中被重复定义,将syscalls.c
中的这几个函数删掉即可。
在main函数的初始化代码中添加对头文件的引用并注册重定向的串口号:
#include "retarget.h"
RetargetInit(&huart1);
稚晖君的代码中貌似没有如原子那样配置避免使用半主机模式的函数,具体的也不是很清楚。
稚晖君的代码很完善的将串口读取的函数也重定向了一遍,意味着咱们也可以愉快的使用scanf
了。
在使用scanf
输入时要注意,向串口发送的数据必须包含空格等识别符,否则scanf
不会执行。
当然在使用scanf时,会像命令行中一样,程序运行到scanf
时,会卡死在这里,等待输入。如果想让程序正常运行的话还是需要使用中断来接收。
另外,稚晖君还提供了第二种方法,但是我没有成功,暂且先列出来:
直接修改CMakeList.txt,加入下述编译选项
set(COMMON_FLAGS "-specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float")
clion的一些设置
clion
提供了一些有用的插件,其中就包括中文插件。
进入Settings - Plugins - Marketplace
,在搜索框中搜索chinese
,
找到Chinese Language Pack
,点击install
即可。
如果下载速度极慢,导致安装不成功,可以在这里下载。
在设置里,点击Install Plugin from Disk
,找到下载好的文件,安装即可
可以在外观-背景图像
中设置clion
的背景
效果如下:
7.结束
啊哈哈哈哈,这样就基本上完成了配置,可以希望在clion
上开发能带来快乐,大致上把我在配置上遇到的问题都写明了,另外,关于软件的下载,俺全程架着梯子,不知道没梯子行不行 (*  ̄3)(ε ̄ *)