Clion+STM32cubeMX搭建stm32开发环境

本篇介绍的内容也是以基于HAL库开发为准的

环境及所需工具

软件环境:

  • Windows 10
  • STM32CubeMX
  • Clion-2019
  • MinGW
  • OpenOCD
  • arm-none-eabi-gcc

硬件环境:

  • STM32F103C6T6 或其他 STM32 开发板
  • ST-Link V2 下载器
微信图片_20220825154215

工具安装

STM32CubeMX

这个正常去官网下载的安装就行了

该软件用于配置时钟频率 gpio初始化等

版本号 <= 6.3.0 版本过高无法生成 后续文件

STM32CubeMX - STM32Cube initialization code generator - STMicroelectronics

OpenOCD

OpenOCD

OpenOCD是用于对STM32进行下载仿真的工具,是一个开源软件包,Windows版本下从这里下载,下载好解压到一个目录就行,后面会在Clion中链接这个目录:

image-20220825154716119

MinGW

Clion需要使用MinGW环境来配置工具链,安装方法如下: 首先去MinGW主页下载最新版本的MinGW: Minimalist GNU for Windows,这是MinGW的安装器:

preview

打开exe进行安装,修改安装目录(最好不能有空格),安装完成后进行组件下载:

image-20220825155527418

如上图中所示,把Basic Setup里面的组件全部勾选(也可也去掉不需要的语言编译器比如Objective-C)。 配置系统的环境变量,在Path环境变量里面添加一条,指向MinGW的bin文件夹:

image-20220825155604634

重启电脑,然后在命令窗口中输入下面的命令验证安装是否成功: gcc -v

image-20220825155637005

arm-none-eabi-gcc Windows到这里下载:https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads ,选择ZIP压缩包形式的:

image-20220825155721531

解压到一个文件夹,并把安装目录下的bin文件夹添加到环境变量:

然后重启使得环境变量生效之后可以在命令行里用以下语句测试: arm-none-eabi-gcc -v 如果有信息输出,那就是装好了。

image-20220825155837867

Clion配置

安装 很简单 这里就不详细说了

Clion是基于CMake来管理项目的,所以首选我们需要配置好预设的MinGW和CMake环境。

打开File-Settings-Build,Execution,Deployment选项卡,在Toolchains下面添加一个MinGW环境:

image-20220825160130915

将 C 编译器 选为arm-none-eabi-gcc bin 文件夹下的 xxx-gcc.exe

将 C++ 编译器 选为arm-none-eabi-gcc bin 文件夹下的 xxx-g++.exe

注意Debugger不要改,否则断点调试的时候无法连接。

然后再CMake栏下确认一下工具链是否正确:

image-20220825160308539

在设置-> 构建、执行、部署 -> 嵌入式开发

image-20220825161455637

绑定 openOCD 与 STM32cubeMX

至此Clion环境配置完成,可以创建STM32项目了。

新建工程

选择嵌入式

位置 你想要存放的位置 最后一个文件 就是项目名称

例如 我的工程叫 GPIO_demo

在 G 盘 Stm32_Project 下

image-20220825161946846

新建完 就是如下界面 点击跳过

image-20220825161922184

选择 通过STM32cubeMX 打开

image-20220825162138931

打开STM32cubeMX

image-20220825162253458
  1. 点击STM32F030F4Px 选择自己的芯片型号

在Part Number 搜索自己的芯片

image-20220825162437107
image-20220825162535422

​ 点击Start Project

设置引脚功能

注意:这里的内容需要根据 自己手里的开发板 操作

image-20220825162845228

可以看到 我的LED 是RGB的 其中红色引脚在PA0

在 如下图中 点击 引脚 设置为output

image-20220825162958972
image-20220825163103245
image-20220825163118459

下面设置该引脚名称

引脚右键 弹出如下菜单 选择ENter User Lable

我设置为LED_R

image-20220825163217442

引脚设置完成

设置时钟

选择Rcc 设置为图二的样子 使用外部晶振

image-20220825163412315
image-20220825163431036

配置时钟树

在Clock Configuration 配置为 图二的样子 是103c6t6 在72Mhz的频率下工作

image-20220825163513845
image-20220825163802440

设置项目生成

这里有几个需要注意的地方

Name 必须和clion 生成的项目名字一样

Toolchain/IDE 需要设置为SW4STM32 如果没有 就是STM32cubeMX版本太高了

完事 之后 点击 cenereate code

image-20220825163916606

弹出如下对话框 点击关闭就可

image-20220825164201145

自此STM32cubeMX设置完成

写代码在 Core/Src 里

image-20220825164250946
image-20220825164430859

三个按钮 分别是 编译 下载 调试

点击 编译会出现 烧录文件

image-20220825164506477

烧录程序 & 在线调试

Keil里面我们烧录程序的时候要指定使用的下载器(J-Link、ST-Link、CMSIS-DAP等),Clion烧录程序之前通用需要进行一些设置。

点击编译按钮旁边的配置栏下拉,选Edit Configurations 或者 编辑配置,打开配置窗口:

image-20220825165035523

可以看到没有设置板子的config文件所以出现警告错误,这个配置文件就是前面说的需要自己生成的文件。

我们在工程根目录下新建一个文件夹config,在里面新建一个配置文件daplink.cfg(因为我这里使用的是STlink V2作为仿真器),文件的内容如下:

1
2
3
4
5
6
7
8
# 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

前两行设置了仿真器的类型和接口,下面几行指定了Flash大小芯片类型下载速度等。

如果对自己的芯片不知道怎么设置,可以参考OpenOCD自带的一系列配置文件,路径在OpenOCD安装目录的share\openocd\scripts下:

只需要关注这几个目录:

  • board:板卡配置,各种官方板卡
  • interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面
  • target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面

设置好配置文件之后,就可以点击下载或者调试按钮进行下载和在线调试了。

在配置文件中不要加reset_config srst_only这一句,会导致下载失败,这一句是指示系统重启的,删除不影响下载。

CLion里面是支持全功能的单步断点调试的,也能在代码里直接观察变量的值,非常舒服~

最后说明一下,CLion中组织编译规则都是基于CMakeLists.txt文件的,如果熟悉CMake应该会觉得很方便很强大,不熟悉的也没事,基本不需要额外修改什么,只需要知道怎么在这个文件里面添加源码目录和include文件夹的路径就行了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
include_directories(
Core/Inc
UserApp
// 其他include目录
)


file(GLOB_RECURSE SOURCES
"startup/*.*"
"Drivers/*.*"
"Core/*.*"
"UserApp/*.*"
"3rdParty/*.*"
// *.*表示通配符,也就是这个文件夹里的所有文件都会被编译
)

printf重定向问题

​ 在Clion中链接的是GNU-Tools-ARM-Embedded\arm-none-eabi\include里面的stdio.h,如果仍然想使用printf函数功能,则需要进行如下操作:

新建一个retarget.h文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#ifndef _RETARGET_H__
#define _RETARGET_H__

#include "stm32xxxx_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文件内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#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)

​ 添加这两个文件到工程,更新CMake,编译之后会发现,有几个系统函数重复定义了,被重复定义的函数位于Src目录的syscalls.c文件中,我们把里面重复的几个函数删掉即可。

在main函数的初始化代码中添加对头文件的引用并注册重定向的串口号:

1
2
3
#include "retarget.h"

RetargetInit(&huart1);

配置完成

上面的修改完成之后可能会发现无法正常读取浮点数,这里修改CMakeList.txt,加入下述编译选项

1
set(COMMON_FLAGS "-specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_float")