注意:使用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
下载地址 ,下载解压,将对应路径添加到环境变量
1.3 arm-none-eabi-gcc
下载地址 ,选择压缩包版本,好像没有64bit 的包,只有32bit的,但是32bit在64bit的机器也是可以使用的。
添加环境变量
2. STM32CubeMx 项目生成
项目配置
选择使用的MCU创建一个项目,在项目管理中配置:
- 项目的名称配置
- 项目的文件结构,有两种选择Basic和Advanced,这两种只是生成的目录结构略微不同
- IDE选择, 使用Clion开发必须选择 STM32CubeIDE
把Code Generator中的下面这个选项勾上
外设配置
时钟配置
因为高速和低速时钟都是使用的外部晶振,所以RCC配置成外部晶振
配置时钟
调试模式
使用的swd调试方式
外设配置
配置板子LED对应的IO, 这里使用的是PC13,STM32CubeMx的外设配置参考其他教程
配置串口
生成工程
保存,点击生成代码
生成后直接关闭,下面在clion中打开项目
3. Clion 配置
在Clion打开STM32CubeMx生成工程,打开时会弹出下面的选择框
这是选择板卡,这里列出的板块都是一些MCU厂商的官方板卡,如果使用的MCU是和官方某款板卡一样的,可以选择对应的,然后点击use。如果不是或不确定,可以点击跳过,在后续的配置中再选择。
Clion 开发STM32必须安装的插件
配置编译器
名称根据需要修改,c编译器选择arm-none-eabi-gcc 解压后的目录bin下的gcc, 对应c++编译器选择g++
配置好后,要把新配置的STM32编译器移到最上面 , 截图的第一个名称为STM32的编译环境是我之前配置好的。
配置完成之后,就可以编译
调试器
在下面中添加OpenOCD的安装路径和STM32CubeMx的安装路径,一般Clion会自动检测,如果没有自动添加可以手动把对应的路径添加上去
下载
添加一个下载的配置
名称按按需修改,目标 文件选择刚刚编译生成的
板卡选择, 选择一个同系列的MCU可以了,将这个配置文件复制到当前工程下,
打开配置文件,注释最后一行
当然这配置文件可以手动创建,具体配置的写法可参考OpneOCD安装目录 share\openocd\scripts
的下面几个文件夹的配置文件
- board:板卡配置,各种官方板卡
- interface:仿真器类型配置,比如ST-Link、CMSIS-DAP等都在里面
- target:芯片类型配置,STM32F1xx、STM32L0XX等等都在里面
下载:
调试
添加在线调试的配置
调试过程也可以查看外设寄存器的值,先要下载对应MCU的svd文件 ,直接在st的官网 查找对应系列的svd文件
然后加载
查看寄存器的值
4. 其他
重定向
配置print
函数重定向,注意,这里事先需要在STM32CubeMx的配置中配置串口,在 Core/Inc
下添加 regtarget.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 ")