在CLion中进行STM32F1系列标准库开发,解决printf问题并添加CMSIS-DSP库
Oravin
编辑于 2022年11月29日 20:30

一、写在前面

    先是前几天看了稚晖君在CLion中开发STM32,于是学习了一下告别了Keil。这里概述一下使用CLion开发标准库的过程。留下一些记录,也希望可以为其他想尝试在CLion上进行库函数开发的朋友参考参考。在一些步骤中我也是按他人的教程做的,我会直接给出教程地址。本文解决了如下几个问题

  1. 在CLion中搭建STM32标准库开发环境。

  2. 解决printf无法使用的问题

  3. 解决math.h无法使用的问题并引入CMSIS-DSP库

二、配置标准库开发环境

    稚晖君的原文是《配置CLion用于STM32开发【优雅の嵌入式开发】》(原文链接https://zhuanlan.zhihu.com/p/145801160),文中稚晖君使用的库是HAL库,和我们要使用的标准库还有些差异,之后参考了up主陈伟国AE的《Clion实现Stm32标准库开发》​,按照文中步骤做完后基本的功能都可以实现了。补充说明一点,创建工程时,在一些版本的STM32CubeMX的Toolchain/IDE中可能找不到SW4STM32,直接选择STM32CubeIDE即可。

三、printf问题

    在usart.c中自定义如下函数即可。

代码块
JavaScript
自动换行
复制代码
int _isatty(int fd) {
    if (fd >= STDIN_FILE_NO && fd <= STDERR_FILE_NO)
        return 1;
    return 0;
}

int _write(int fd, char *ptr, int len) {
    if (fd == STDOUT_FILE_NO || fd == STDERR_FILE_NO) {
        uint32_t i;
        for (i = 0; i < len; i++) {
            USART_SendData(USART1, ptr[i]);

            while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
        }
        while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);

        return len;

    }
    return -1;
}


int _close(int fd) {
    if (fd >= STDIN_FILE_NO && fd <= STDERR_FILE_NO)
        return 0;
    return -1;
}


int _lseek(int fd, int ptr, int dir) {
    (void) fd;
    (void) ptr;
    (void) dir;
    return -1;
}


int _read(int fd, char *ptr, int len) {
    if (fd == STDIN_FILE_NO) {
        while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET) {
        }
        *ptr = USART_ReceiveData(USART1);
        return 1;
    }
    return -1;
}
复制成功

四、数学库的使用

    1. math.h

        要使用math.h需要在CMakeLists中的add_executable命令前加入LINK_LIBRARIES(m)即可。

    2. CMSIS-DSP(arm_math.h)

        当然在嵌入式环境下直接使用math.h会慢一些,所以我们也可以使用CMSIS的DSP库(下文简称DSP库)。

    2.1 获取DSP库

    (1) 首先获取DSP库的方式可以是从Keil的文件夹或是STM32CubeMX的文件夹中复制,但是不推荐这么做,这么做的话获得的DSP库版本不统一,可能会增加后续查阅说明文档的难度(当然也可以看他们文件夹中有Documentation文件夹,其中有对应版本的离线说明文档)。我是选择了去官网下载了目前的最新版1.14.2,链接如下:

https://github.com/ARM-software/CMSIS-DSP/releases

下载zip格式的包,解压之后如图4-1

 (2)获取CMSIS-Core文件夹,这个一般STM32CubeMX在一开始生成的时候就会生成一个,删了也可以去文件夹C:\Users\Administrator\STM32Cube\Repository\STM32Cube_FW_F1_V1.8.4\Drivers\CMSIS\Core\Include

中找到,包含内容如图4-2。

图4-2 CMSIS-Core

2.2 将DSP库文件移动到工程目录中

    在工程目录中新建CMSIS文件夹,在CMSIS文件夹下再新建两个文件夹,在CMSIS中按照图4-3结构新建文件夹。

图4-3 新建的文件夹结构,分别是Core和DSP,Core中包含一个Inc文件夹,DSP中包含三个文件夹分别是Inc,PrivateInclude和Src。

    新建完成后,将DSP库移动过来。

    (1)PrivateInclude文件夹内的内容直接复制到这边的PrivateInclude文件夹即可,如图4-4。

   

图4-4 DSP\PrivateInclude

   

    (2)将CMSIS-DSP-1.14.2\Include中所有内容复制到DSP\Inc中,如图4-5。

图4-5 DSP\Inc

    (3)将CMSIS-DSP-1.14.2\Source中的所有文件夹复制到DSP\Src中,如图4-6。

    

图4-6 DSP\Src

    (4)将CMSIS-Core中的文件移动到Core\Inc中,如图4-7。

图4-7 移动后的CMSIS-Core

    2.3配置CMakeLists

    将CMSIS/Core/Inc,CMSIS/DSP/Inc,CMSIS/DSP/PrivateInclude添加到引用路径中,即把上述三项添加到include_directories命令中即可,代码如下。

代码块
JavaScript
自动换行
复制代码
include_directories(
        CMSIS/Core/Inc
        CMSIS/DSP/Inc
        CMSIS/DSP/PrivateInclude
        STM32F10x_FWLib/Inc
        Core/Inc
        SYSTEM/Inc
        HARDWARE/Inc
        USER/Inc
    )
复制成功

    同样将源文件添加到项目中,需要注意只能添加与每个库文件夹同名的那个.c文件,因为那个文件中include了他所在文件夹的其他文件,如果我们这里直接将所有文件夹添加到项目中会导致multiple definition的问题。当然CMSIS-DSP按照不同功能的库进行了区分,分为多个文件夹,也可以直添加需要的库,这里以只添加DistanceFunction库为例,如果想要全部添加的话去掉注释即可。

代码块
JavaScript
自动换行
复制代码
file(
        GLOB_RECURSE SOURCES
        #"CMSIS/DSP/Src/BasicMathFunctions/BasicMathFunctions.c"
        #"CMSIS/DSP/Src/CommonTables/CommonTables.c"
        #"CMSIS/DSP/Src/ComplexMathFunctions/ComplexMathFunctions.c"
        #"CMSIS/DSP/Src/ControllerFunctions/ControllerFunctions.c"
        #"CMSIS/DSP/Src/FastMathFunctions/FastMathFunctions.c"
        #"CMSIS/DSP/Src/FilteringFunctions/FilteringFunctions.c"
        #"CMSIS/DSP/Src/MatrixFunctions/MatrixFunctions.c"
        #"CMSIS/DSP/Src/StatisticsFunctions/StatisticsFunctions.c"
        #"CMSIS/DSP/Src/SupportFunctions/SupportFunctions.c"
        "CMSIS/DSP/Src/DistanceFunctions/DistanceFunctions.c"
        #"CMSIS/DSP/Src/QuaternionMathFunctions/QuaternionMathFunctions.c"
        #"CMSIS/DSP/Src/SVMFunctions/SVMFunctions.c"
        #"CMSIS/DSP/Src/TransformFunctions/TransformFunctions.c"
        #"CMSIS/DSP/Src/InterpolationFunctions/InterpolationFunctions.c"
        #"CMSIS/DSP/Src/BayesFunctions/BayesFunctions.c"
        "CMSIS/Core/*.*"
        "startup/*.*"
        "STM32F10x_FWLib/*.*"
        "Core/*.*"
        "HARDWARE/*.*"
        "SYSTEM/*.*"
        "USER/*.*"
    )
复制成功

    之后需要取消CMaleList中的一些宏定义的注释来启用浮点数运算,这里使用的是STM32F1,没有FPU,故选择使用软件浮点运算(取消第二段注释),M4核心的话也可以使用硬件浮点运算(取消第一段注释)。

代码块
JavaScript
自动换行
复制代码
#Uncomment for hardware floating point
#SET(FPU_FLAGS "-mfloat-abi=hard -mfpu=fpv4-sp-d16")
#add_definitions(-DARM_MATH_CM4 -DARM_MATH_MATRIX_CHECK -DARM_MATH_ROUNDING -D__FPU_PRESENT=1)

##Uncomment for software floating point
SET(FPU_FLAGS "-mfloat-abi=soft")

SET(COMMON_FLAGS
    "-mcpu=cortex-m3 ${FPU_FLAGS} -mthumb -mthumb-interwork -ffunction-sections -fdata-sections \
    -g -fno-common -fmessage-length=0 -specs=nosys.specs -specs=nano.specs -u _printf_float")
复制成功

    2.4 修改arm_math.h

    在arm_math.h开头加入

代码块
JavaScript
自动换行
复制代码
#include "stm32f10x.h"
复制成功

即可,同理如果使用hal库的话就引用stm32f1xx.h。

    2.5完工

    之后就可以用了,可以选择只引用需要的功能,如

代码块
JavaScript
自动换行
复制代码
#include "dsp/distance_functions.h"
复制成功

    也可以引入整个库

代码块
JavaScript
自动换行
复制代码
#include "arm_math.h"
复制成功

    之后正常用就行了,至于有哪些功能可以参考官方文档

https://arm-software.github.io/CMSIS-DSP/latest/

    总结一下,添加CMSIS-DSP库需要以下步骤

  •  获取CMSIS-DSP和CMSIS-Core

  • 将上述文件添加到工程目录中

  • 修改CMakeList

  • 修改arm_math.h