注意:使用Clion开发的必要前置知识,

  • 使用过keil开发STM32
  • 熟悉使用Clion,特别是CMake
  • 了解STM32CubeMx的使用
  • 了解软件编译以及调试的内部原理

如果不具备上述前置知识,不建议使用Clion。

以下配置使用的是STM32F407ZGT6最小系统板,下载器使用的是stlink

1. 软件安装

  • Clion
  • STM32CubeMx
  • OpenOCD (下载调试)
  • arm-none-eabi-gcc (编译)

1.1 Clion 和 STM32CubeMX

到对应官网下载,安装,Clion , STM32CubeMx

1.2 OpenOCD

下载地址 ,下载解压,将对应路径添加到环境变量

image-20231116195139928

1.3 arm-none-eabi-gcc

下载地址 ,选择压缩包版本,好像没有64bit 的包,只有32bit的,但是32bit在64bit的机器也是可以使用的。

image-20231116195448260

添加环境变量

image-20231116195646661

2. STM32CubeMx 项目生成

项目配置

选择使用的MCU创建一个项目,在项目管理中配置:

image-20231116200216235

  1. 项目的名称配置
  2. 项目的文件结构,有两种选择Basic和Advanced,这两种只是生成的目录结构略微不同
  3. IDE选择, 使用Clion开发必须选择 STM32CubeIDE

把Code Generator中的下面这个选项勾上

image-20231116200619324

外设配置

时钟配置

因为高速和低速时钟都是使用的外部晶振,所以RCC配置成外部晶振

image-20231116200932137

配置时钟

image-20231116201740500

调试模式

使用的swd调试方式

image-20231116201038760

外设配置

配置板子LED对应的IO, 这里使用的是PC13,STM32CubeMx的外设配置参考其他教程

image-20231116202140783

配置串口

image-20231116221203820

生成工程

保存,点击生成代码

image-20231116202358246

生成后直接关闭,下面在clion中打开项目

image-20231116202645733

3. Clion 配置

在Clion打开STM32CubeMx生成工程,打开时会弹出下面的选择框

image-20231116202940565

这是选择板卡,这里列出的板块都是一些MCU厂商的官方板卡,如果使用的MCU是和官方某款板卡一样的,可以选择对应的,然后点击use。如果不是或不确定,可以点击跳过,在后续的配置中再选择。

Clion 开发STM32必须安装的插件

image-20231116203430161

配置编译器

image-20231116203850695

名称根据需要修改,c编译器选择arm-none-eabi-gcc 解压后的目录bin下的gcc, 对应c++编译器选择g++

image-20231116204201105

配置好后,要把新配置的STM32编译器移到最上面 , 截图的第一个名称为STM32的编译环境是我之前配置好的。

配置完成之后,就可以编译

image-20231116205207478

调试器

在下面中添加OpenOCD的安装路径和STM32CubeMx的安装路径,一般Clion会自动检测,如果没有自动添加可以手动把对应的路径添加上去

image-20231116204356881

下载

添加一个下载的配置

image-20231116210217121

名称按按需修改,目标 文件选择刚刚编译生成的

image-20231116210527173

板卡选择, 选择一个同系列的MCU可以了,将这个配置文件复制到当前工程下,

image-20231116210745746

打开配置文件,注释最后一行

image-20231116210850925

当然这配置文件可以手动创建,具体配置的写法可参考OpneOCD安装目录 share\openocd\scripts 的下面几个文件夹的配置文件

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

下载:

image-20231116211648748

调试

添加在线调试的配置

image-20231116211920113

image-20231116212336487

调试过程也可以查看外设寄存器的值,先要下载对应MCU的svd文件 ,直接在st的官网 查找对应系列的svd文件

image-20231116214152393

然后加载

image-20231116213543230

image-20231116213754370

查看寄存器的值

4. 其他

重定向

配置print 函数重定向,注意,这里事先需要在STM32CubeMx的配置中配置串口,在 Core/Inc 下添加 regtarge.h

#ifndef DEMO_RETARGET_H
#define DEMO_RETARGET_H

#include "stm32f4xx_hal.h"
#include <sys/stat.h>
#include <stdlib.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 //DEMO_RETARGET_H

Core/Src 下添加 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)

编译,出现错误,根据错误提示把 Core/Src/syscalls.c 中重定义的函数注释,主要是下面四个函数

int _close(int file)
{
  (void)file;
  return -1;
}

int _fstat(int file, struct stat *st)
{
  (void)file;
  st->st_mode = S_IFCHR;
  return 0;
}

int _isatty(int file)
{
  (void)file;
  return 1;
}

int _lseek(int file, int ptr, int dir)
{
  (void)file;
  (void)ptr;
  (void)dir;
  return 0;
}

CMake

如果需要打印浮点数,还需要再Cmake中加入下面这行配置

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

参考