别用Keil了,CLion同样可以用于STM32开发~

本文介绍了如何将STM32的开发环境从Keil迁移到更强大的IDE Clion,包括STM32CubeMX的使用、Clion的环境配置、MinGW和OpenOCD的安装、CMakeLists配置以及烧录和调试步骤。通过Clion的代码补全和调试功能,可以提升STM32开发的效率。
摘要由CSDN通过智能技术生成

d5f30a641ad0a56fa85d62bf9f9a4a17.png

0.前言

最近有用到 STM32 这款MCU,好久没用 Keil 感觉各种不适应,遂尝试能不能把 STM32 的开发环境部署到其他更强大的IDE,结果很圆满,以下是配置 Clion 用于 STM32 开发的过程记录,供大家参考~

做嵌入式开发的人对STM32这个平台应该都是非常熟悉的,在国内尤其流行,很多产品里面都是基于这个平台做的方案。多数人在开发STM32的时候用的都是Keil这个老牌IDE,很大一部分原因是因为大多数人最初是从51单片机学习过来的,51就是基于Keil去开发的,然后迁移到STM32的时候也就沿用下来了。

cce8e527640510b9f545dc7b81cc3192.png
Keil开发环境界面

Keil操作简单,容易上手,而且可以很方便地进行调试。但是对于以前不是做嵌入式开发的软件开发人员来说,面对Keil这种上世纪风格的IDE(不只是UI)肯定是不太有好感的。在尝试过STM32CubeIDETrueStudio等用Eclipse修改的IDE之后,总是觉得还是没内味儿。

我平时工作中最常用的是PycharmAndroid Studio,都是Jetbrains系的IDE。用过Jetbrains系IDE的朋友肯定会被它强大的代码补全、界面风格、各种插件、流畅性等众多优点所吸引,毫无疑问这些是能够极大提高开发效率的。而其中有一款CLion IDE就是专门面向C/C++开发的,所以本篇文章会介绍如何把STM32的编译调试环境部署到CLion中,过程还是有很多坑值得注意的。

代码的话大家以前用的基本上都是ST的标准库,然后自己开发逻辑部分,或者在一些方便的地方直接操作寄存器。直接操作寄存器能提高代码的执行效率,但是很难移植,后来ST开发了一款可以快速demo的平台Cube MX,通过这个软件配合ST的HAL库,可以快速的搭建工程,并能生成跨芯片平台的工程。HAL库更容易进行移植,而且应用起来也更加方便,ST也正在加大对HAL库的支持,并渐渐放弃标准库。

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

1.环境及所需工具

软件环境:

  • Windows 10

  • STM32CubeMX

  • Clion-2019

  • MinGW

  • OpenOCD

  • arm-none-eabi-gcc

硬件环境:

  • STM32F103VET6

  • 自制的DapLink下载器(ST-Link/J-Link也是可以的)

工具安装

  1. STM32CubeMX
    这个正常去官网下载最新版的安装就行了:st.com/en/development-t

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

c1cac238362c75e02cda7bcea279ba64.png

在CLion中配置OpenOCD的方法:

8e352c491c0eee0618a0d1ec99b07659.png

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

4c88fdc1159afd62a5db3db27fcfc287.png

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

c75db0286af1543a1f043ddca52bf8e0.png

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

ac17717a28219b20e3fc6ccdc7c35871.png

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

5132b68f6340b16d5e80c107c31beef6.png

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

2325443adbcad06b3ac09cfff1d322ed.png

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

3e3b82f699807dc76b9b07c07564d4ea.png

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

Clion配置

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

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

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

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

519cc8f248a7302c1a642bb7e4b8bc9e.png

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

2.在Clion中创建STM32工程

创建CubeMX工程

在Clion中选择File-New Project可以创建STM32CubeMX的项目:

d1326fc17ebb0889c64c996992db4322.png

点击create后会生成一个.ioc文件,这个文件跟使用STM32CubeMX直接创建的是一样的,点击图中的链接可以跳转到STM32CubeMX中打开这个ioc文件:

d89fb4e7c55e8709c330f13b132679aa.png

默认选中的芯片型号是STM32F030F4Px,我们可以在CubeMX中重新选择自己需要的芯片,一切操作都和使用Keil开发没有区别。

只有一个地方需要注意一下,就是在下面的设置中项目名称一定要和在Clion中建立的一致,这样生成的工程文件才会覆盖Clion中的文件,否则会另外生成一个文件夹,Clion就无法读取了。

另外生成的IDE类型选择是SW4STM32。

c1d969f32595f2911bd320d936b846ab.png

每次修改完点Generate之后,弹窗直接点Close,Clion里面会自动更新文件。

9103c70517a459e395ca0f2df527323c.png

第一次设置完回到Clion会弹出一个板卡选择窗口:

5c4aed0c94e0474bcea55e4f3ba235e5.png

这些配置文件是跟OpenOCD下载程序有关的,里面的板子很可能是没有我们自己要用的型号的,后面会介绍怎么自己建立这个配置文件,这里先点取消。

编译工程

在IDE底栏的CMake选项卡中如果没有提示错误,说明工程配置就没问题了。

点击这个按钮可以更新CMake工程:

62a43f749141d2b474f9b39c39a4d115.png

顶栏的这三个图标分别是编译、下载、调试:

8cf84fdf47d4ceba6989297934106bf2.png

点击编译,可以看到编译输出:

3dc37b6c5c9c525355a1687043905a58.png

可以看到成功生成了用于烧写的.bin和.hex文件。

烧录程序 & 在线调试

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

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

d5e97d9d6c005eff9959f926b9840d4e.png

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

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

# 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

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

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

7b7e2872b263f661789b4dfc205c4ec1.png

只需要关注这几个目录:

  • board:板卡配置,各种官方板卡

  • interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面

  • target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面

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

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

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

9a8cc270989e2b5db5dd156f9619bc14.png
强大的代码补全功能
498c4f29cbc6869e6e08bd129443be11.png
单步调试和变量观察功能

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

include_directories(
        Core/Inc
        UserApp
// 其他include目录
)


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

3.其他问题

编译错误问题

  1. 如果移动了工程文件夹的话,最好打开.ioc文件重新Generate一下再编译,可以解决很多错误。

  2. 遇到任何CMake相关的报错,一般是由于缓存没有更新引起的,可以在CLion中选Tools-CMake-Reset Cache and Reload Project即可解决。

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 <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函数的初始化代码中添加对头文件的引用并注册重定向的串口号:

#include "retarget.h"

RetargetInit(&huart1);

然后就可以愉快地使用printfscanf啦:

char buf[100];

printf("\r\nYour name: ");
scanf("%s", buf);
printf("\r\nHello, %s!\r\n", buf);

其二:

直接修改CMakeList.txt,加入下述编译选项

set(COMMON_FLAGS "-specs=nosys.specs -specs=nano.specs -u _printf_float -u _scanf_floa

a76ce89553b29f802b66bea8d63ab45d.png

555983d1f0e5a3afb60820dd3212402a.png

1.2022年第3期《单片机与嵌入式系统应用》电子刊新鲜出炉!

2.国内有哪些厂商推出了RISC-V内核的MCU?

3.全球芯片竞赛已提速!“太湖之芯”创业大赛打造竞速平台

4.一颗小小的MCU,有必要支持AI功能吗?

5.采用24V供电的MCU电源电路及BOM表分析

6.不同的电平信号怎么进行“沟通”?

9ecda46ba185a8da8db4e070a5a78136.gif

免责声明:本文系网络转载,版权归原作者所有。如涉及作品版权问题,请与我们联系,我们将根据您提供的版权证明材料确认版权并支付稿酬或者删除内容。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值