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平台,在ST产品线中定位为高性能计算产品,带有FPU和DSP指令集,这不用来做数字信号处理属实浪费,刚好近期工作需求要用到FFT或者FIR,所以,万事俱备,开搞!!
1. DSP库在哪?文档?
看了一下PlatformIO自带的STM32CubeF4库,为了节约服务器带宽和用户下载时间,所以包很精简,可是...这用起来很不方便呀,没有文档怎么写代码?
于是去官网下了一个标准版本,1.6.1最新版,带了文档。然而后来才发现,原来CMSIS文档是有在线版的:>>> CMSIS DSP Software Library <<<
官网:STMicroelectronics/STM32CubeF4: STM32Cube MCU Full Package for the STM32F4 series - Github
2. 编译Demo
按理说三分钟出demo的事,结果一开始情况就不太妙...首先找到自带例程(为了要例程还是得下完整库)。拿一个FIR的例子开刀:
复制进自己的项目,该函数名,烧录运行?运行效果:
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'
编译过后,错误少了一大筐:
但依然会有类似这种错误:
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
文件有定义:
根据导入链,查看
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. 大地的灾难
查查资料,发现众说纷纭,讲什么的都有,其中比较有代表性和参考价值的几篇:
-
Cortex-M4F: xxx.elf uses VFP register arguments, yyy.o does not · Issue #2660 · RIOT-OS/RIOT
这篇内容相当精炼,是用GCC+Makefile写的,编译参数透明,参考价值比较大。里面用到的编译参数是:CCFLAGS=-mcpu=cortex-m4 -mthumb -g -mfloat-abi=hard -fsingle-precision-constant -mfpu=fpv4-sp-d16 -I ../../CMSIS/CMSIS-master/CMSIS/Include -D ARM_MATH_CM4 -D __FPU_PRESENT=1
-
Simple DSP with ARM CMSIS and GCC on the STM32F303
用Keil用DSP库是基本没太大问题的,至少不会有奇怪到难以解决的问题。 -
Using cortex-m4 FPU with gcc toolchain
这篇是一个日本人写的,用谷歌翻译了一下,证明用STM32CubeIDE是可以直接用没问题的。 -
如何在STM32 Cube IDE环境中使用CMSIS-DSP | ioloa.com
总结:这里查了一堆资料,然而并没有什么卵用。
8. 迟来的春天
这地方卡了我一整天,真的调到后面脑袋都大了。。。查了很多资料都没有解决,直到后来想到既然其他IDE没问题,会不会是PlatformIO的问题?然后看到这篇:
-
How to pass flags to final firmware.elf linkage? - PlatformIO Core - PlatformIO Community
这个标题似乎没有什么,但后来才反应过来这就是我想要的...不过里面提到的一个bug issue链接参考价值很大,: -
Flags specified in platformio.ini build_flags variable not applied to all necessary steps in the build process · Issue #594 · platformio/platformio-core
这里讲的是PlatformIO的一个bug,而且是2016年5月就有人提了issue,而且被官方人员关闭了。我当然会以为早已经修复好了,但是还是抱着试试的心态测试了一下,里面提到的方案是:
在platformio.ini
文件中添加一行extra_script = update_link_flags.py
:
[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,导致后来成吨不明所以的编译错误。
表现为:
如图中描述的,编译过程的Flags不会对所有中间对象都生效,也就是说,这里的
firmware.elf
文件,在编译过程中是没有拿到build_flags
中的参数的,也就导致最终所有中间对象*.o
文件无法链接到这个target中来。而解决办法就是,使用一个额外的脚本(extra_script),用于在编译时,对每个编译对象重新添加一遍flags,从而规避了这个bug。
可是这都16年的bug了,怎么留到现在的???哭死o(╥﹏╥)o
其他解决方案
其实也有折中的办法,但是都不能完美解决问题。
- 使用softfp浮点数指令,这样可以解决,但是效率会成问题,尤其是DSP这类密集性计算算法。
- 使用
libarm_cortexM4l_math.a
库文件,实测是可以正常编译且正常执行的。但是这种方案也是没有用到浮点数指令的,所以还是类似软件浮点数的方法,损失了效率。 - 换其他IDE,比如Keil或者STM32CubeIDE,似乎都不会遇到这个问题,但是代码编写体验不如VSCode。
总结
解决这个问题的过程是比较曲折的,甚至我试着按照PlatformIO格式更新了STM32Cube库到最新版,还有去ARM官网下载了新版的编译器,也没有效果。但是中间还是有比较多的收获,掌握了STM32 DSP库的用法,PIO插件的格式,以及查阅文档、检索资料的习惯等等。
网友评论