使用PlatformIO IDE使用CMSIS DSP库排坑

MCU:STM32F407
环境:VSCode + PlatformIO IDE + STM32CubeF4(v1.5.2)
编译器:7.2.1 20170904 (release) [ARM/embedded-7-branch revision 255204]
PlatformIO Core: version 5.1.1

STM32F4 Core Feature Description

问题描述

由于用的是STM32F4平台,在ST产品线中定位为高性能计算产品,带有FPU和DSP指令集,这不用来做数字信号处理属实浪费,刚好近期工作需求要用到FFT或者FIR,所以,万事俱备,开搞!!

1. DSP库在哪?文档?
看了一下PlatformIO自带的STM32CubeF4库,为了节约服务器带宽和用户下载时间,所以包很精简,可是...这用起来很不方便呀,没有文档怎么写代码?

image.png

于是去官网下了一个标准版本,1.6.1最新版,带了文档。然而后来才发现,原来CMSIS文档是有在线版的:>>> CMSIS DSP Software Library <<<

官网:STMicroelectronics/STM32CubeF4: STM32Cube MCU Full Package for the STM32F4 series - Github

CMSIS文档

2. 编译Demo
按理说三分钟出demo的事,结果一开始情况就不太妙...首先找到自带例程(为了要例程还是得下完整库)。拿一个FIR的例子开刀:

image.png

复制进自己的项目,该函数名,烧录运行?运行效果:
image.png

image.png

!!直接复制进来用会 遇到成吨报错轰炸!!
观察日志会发现基本全是CMSIS库中的变量缺失错误,以及类型缺失错误。定位错误可以发现:
image.png

类型错误很好解决,直接导入HAL的总入口.h文件#include <stm32f4xx_hal.h>(加到math_helper.h顶部)。
参考:#error "Compiler generates FPU instructions for a device without an FPU (check __FPU_PRESENT)"

3. arm_math.h:932:41: error: redefinition of '__SMLALD'
编译过后,错误少了一大筐:

image.png

但依然会有类似这种错误:

C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Include/cmsis_gcc.h:1652:31: note: previous definition of '__SHADD16' was here
 1652 | __STATIC_FORCEINLINE uint32_t __SHADD16(uint32_t op1, uint32_t op2)
      |                               ^~~~~~~~~
In file included from lib\arm_fir_example_f32\math_helper.h:45,
                 from lib\arm_fir_example_f32\arm_fir_example_f32.c:125:
C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\DSP\Include/arm_math.h:932:41: error: redefinition of '__SMLALD'
  932 |   CMSIS_INLINE __STATIC_INLINE uint64_t __SMLALD(
      |                                         ^~~~~~~~
In file included from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Include/cmsis_compiler.h:48,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Include/core_cm4.h:162,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Device\ST\STM32F4xx\Include/stm32f407xx.h:167,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Device\ST\STM32F4xx\Include/stm32f4xx.h:133,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\STM32F4xx_HAL_Driver\Inc/stm32f4xx_hal_def.h:30,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\STM32F4xx_HAL_Driver\Inc/stm32f4xx_hal_rcc.h:29,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\STM32F4xx_HAL_Driver\Inc/stm32f4xx_hal_conf.h:281,
                 from C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\STM32F4xx_HAL_Driver\Inc/stm32f4xx_hal.h:30,
                 from lib\arm_fir_example_f32\math_helper.h:44,
                 from lib\arm_fir_example_f32\arm_fir_example_f32.c:125:

继续定位,发现缺失的变量在cmsis_iccarm.h文件有定义:

image.png

根据导入链,查看arm_math.h文件,发现里面有这样一行宏定义,在VSCode中显示为灰色:

#if defined(ARM_MATH_CM7)
  #include "core_cm7.h"
  #define ARM_MATH_DSP
#elif defined (ARM_MATH_CM4)
  #include "core_cm4.h"
  #define ARM_MATH_DSP

这个解决办法就很明确了,直接在顶层include 文件添加一个宏定义,对应到自己的MCU平台即可。由于STM32F4系列对应于Cortex-M4架构,所以这里定义一个ARM_MATH_CM4
math_helper.h头部:

#define ARM_MATH_CM4
#include <stm32f4xx_hal.h>
#include "arm_math.h"

编译:

4. arm_fir_example_f32.c:202: undefined reference to `arm_fir_f32'

Archiving .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a
Archiving .pio\build\black_f407zg\libFrameworkCMSISDevice.a
Linking .pio\build\black_f407zg\firmware.elf
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(arm_fir_example_f32.o): in function `fir_test':
C:\Documents\PlatformIO\Projects\STM32F4_ADC_Sampling_v2/lib\arm_fir_example_f32/arm_fir_example_f32.c:194: undefined reference to `arm_fir_init_f32'
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: C:\Documents\PlatformIO\Projects\STM32F4_ADC_Sampling_v2/lib\arm_fir_example_f32/arm_fir_example_f32.c:202: undefined reference to `arm_fir_f32'
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\black_f407zg\firmware.elf] Error 1

image.png

经典的符号未定义问题,而且是在link阶段出现的,说明DSP库没有正确链接上。也就是说这个库对于编译器来说是不知道在哪个位置的,需要自己添加上。
定位库文件:
image.png

用的GCC编译器,进去发现问题来了,选哪个呢?
image.png

搜索了解后知道,l是指little endian,f是指带有硬件除法器,那么没有疑问,用带lf的那个,也就是libarm_cortexM4lf_math.a

5. 导入DSP静态库
问题又来了,怎么导入到PIO呢?

打开platformio.ini文件,在环境中添加一行:

build_flags = 
    -LC:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC
    -larm_cortexM4lf_math

第N次编译,结果:


image.png
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_q15.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_q15.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_f32.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_f32.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_f32.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_f32.o)
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\black_f407zg\firmware.elf] Error 1

6. 启用编译器(MCU)浮点数扩展


这里理一下思路:我们要用F4的浮点数运算能力,但是浮点数运算需要用到VFP指令和寄存器,但是libarm_cortexM4lf_math.a使用了VFP寄存器,而.pio\build\black_f407zg\firmware.elf没有使用....

这个问题的解读就是,生成一个可执行文件elf时,其所有的中间对象*.o都要是同样的编译条件,至少对于VFP指令集的设置应该是相同的。但是目前的ELF文件没有启用这个选项。

那么问题就简单了,启用一下不就可以了,而方式应该就是通过对编译器传参,在platformIO中就对应于,设置platformio.ini文件中的build_flags

查过以上资料后,发现我应该这么设置:

[env:black_f407zg]
build_type = debug
platform = ststm32
board = black_f407zg
framework = stm32cube
upload_protocol = stlink
debug_tool = stlink
monitor_speed = 115200
build_flags = 
    -LC:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\ARM
    -larm_cortexM4lf_math
    -mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16

也就是添加一行5个flag
-mthumb -mcpu=cortex-m4 -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16

使用过后,编译结果:

c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\FrameworkHALDriver\Src\stm32f4xx_ll_fsmc.o
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: .pio\build\black_f407zg\src\system_stm32f4xx.o uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\src\system_stm32f4xx.o
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(arm_fir_example_f32.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
...
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(arm_fir_example_f32.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(fft.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(fft.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(math_helper.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(math_helper.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(arm_fir_data.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file .pio\build\black_f407zg\liba89\libarm_fir_example_f32.a(arm_fir_data.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_q15.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_q15.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_f32.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_init_f32.o)
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: error: C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_f32.o) uses VFP register arguments, .pio\build\black_f407zg\firmware.elf does not
c:/users/joy/.platformio/packages/toolchain-gccarmnoneeabi@2.00000.0/bin/../lib/gcc/arm-none-eabi/10.2.1/../../../../arm-none-eabi/bin/ld.exe: failed to merge target specific data of file C:\Users\JOY\.platformio\packages\framework-stm32cubef4\Drivers\CMSIS\Lib\GCC\libarm_cortexM4lf_math.a(arm_fir_f32.o)
collect2.exe: error: ld returned 1 exit status
*** [.pio\build\black_f407zg\firmware.elf] Error 1

使用过后,不但出现了前面的静态链接库链接不上的问题,其他所有文件都链接不上了。。。???


image.png

7. 大地的灾难

查查资料,发现众说纷纭,讲什么的都有,其中比较有代表性和参考价值的几篇:

8. 迟来的春天

这地方卡了我一整天,真的调到后面脑袋都大了。。。查了很多资料都没有解决,直到后来想到既然其他IDE没问题,会不会是PlatformIO的问题?然后看到这篇:

[env:disco_f407vg]
platform = ststm32
framework = spl
board = disco_f407vg
build_flags = -mthumb -march=armv7e-m -mfloat-abi=hard -mfpu=fpv4-sp-d16
extra_script = update_link_flags.py

然后在项目根目录中添加一个文件:update_link_flags.py,内容为:

# Custom settings, as referred to as "extra_script" in platformio.ini
#
# See http://docs.platformio.org/en/latest/projectconf.html#extra-script

from SCons.Script import DefaultEnvironment

env = DefaultEnvironment()

env.Append(
    LINKFLAGS=[
        "-mthumb",
        "-march=armv7e-m",
        "-mfloat-abi=hard",
        "-mfpu=fpv4-sp-d16"
    ]
)

9. 原因分析
总的来说,就是一颗老鼠屎,毁了整锅汤的案例重现,因为PlatformIO或者stm32插件的一个漏网的bug,导致后来成吨不明所以的编译错误。
表现为:

image.png

如图中描述的,编译过程的Flags不会对所有中间对象都生效,也就是说,这里的firmware.elf文件,在编译过程中是没有拿到build_flags中的参数的,也就导致最终所有中间对象*.o文件无法链接到这个target中来。
而解决办法就是,使用一个额外的脚本(extra_script),用于在编译时,对每个编译对象重新添加一遍flags,从而规避了这个bug。

可是这都16年的bug了,怎么留到现在的???哭死o(╥﹏╥)o

其他解决方案

其实也有折中的办法,但是都不能完美解决问题。

  1. 使用softfp浮点数指令,这样可以解决,但是效率会成问题,尤其是DSP这类密集性计算算法。
  2. 使用libarm_cortexM4l_math.a库文件,实测是可以正常编译且正常执行的。但是这种方案也是没有用到浮点数指令的,所以还是类似软件浮点数的方法,损失了效率。
  3. 换其他IDE,比如Keil或者STM32CubeIDE,似乎都不会遇到这个问题,但是代码编写体验不如VSCode。

总结

解决这个问题的过程是比较曲折的,甚至我试着按照PlatformIO格式更新了STM32Cube库到最新版,还有去ARM官网下载了新版的编译器,也没有效果。但是中间还是有比较多的收获,掌握了STM32 DSP库的用法,PIO插件的格式,以及查阅文档、检索资料的习惯等等。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容