之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下
" N1 b% d p7 S7 ^STM32F767IGT6+ o& T/ k4 d* {2 ~2 h# F7 }: H* K& u
Keil5.21
/ f4 X6 C h* p+ }* Q3 c; oCube MX4.24) y Y. P! k9 {; J8 ]
arm_cortexM7lfdp_math.lib$ C# P7 {0 B$ c1 @/ n( b9 l
正点原子阿波罗开发板
8 J: ]0 ]; a9 T0 L: ^4 K& {2 k0 n- p, V1 d0 \3 t
准备DSP库, c8 L1 H7 M3 l4 z
打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
) n- `/ s) h3 R) _, N; r- H
. l% z' R4 |2 r& y2 P: o, V
$ c( |; {/ [: C( h$ T' \8 j M
+ I5 m8 |2 k! }Drivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。6 S# C3 K' t9 n, j6 F$ S4 W
Examples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):; o& T7 ?& P% K) I
2 G( X8 J5 h- |% g C5 x4 K [8 x
0 S& c3 c" A# j3 ?% p7 Y0 J7 X
4 b$ N5 ]8 l: n `! i, X jSource 中的文件如下(这些是 DSP库的源文件):+ a+ e' t; M8 `0 ]2 h5 w& o
+ V" E0 w, V) K }* [0 E
& A, w+ i! I- x( g* L% C8 b' k0 [- r1 X' w* M: ?& @
源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。& m/ G, |+ p$ b7 n4 D2 W5 I/ R
/ h, L* f# W7 g# t8 `& N4 Y6 C$ F
% w/ t* ?1 p, m5 {$ f
. n1 C% N( C S, u% }! M( [5 \
库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。
0 t+ c7 f7 k2 f* s( G: e9 N& N" \3 j5 _' H( X b5 h2 ?
/ o' ^2 l/ t# ?1 f3 e
% f0 I9 Q+ s: Y% H# T确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。
3 f' S; h& X$ j
, S' W& {+ ?! |1 r
: h8 b' j4 x$ W
0 y! d$ `8 c% a" p' x下图为DSP库已添加到工程中。+ f+ t I. c" C. H
" W7 j, s7 E1 I" t6 V1 u8 d" @
2 O; W7 o" {9 ^0 o- e) j$ e" a, c6 [
3 H$ D5 c6 P9 O库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。. K& H4 `; }1 H; Y8 H8 s4 b/ ?3 M
; @+ T- u7 ] F1 N) S; D* t
0 G9 e+ p, H' O/ R, M, |$ o. {" N; b5 ]3 P$ s9 @
函数说明6 y- z, C0 i: H. u1 ?7 c
我用到的是实数FFT,即rfft。相关函数参见官方说明。! R6 I# y9 a7 G! m1 ~* o2 J( Y
函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
2 [% \$ z1 }, w( h5 ^arm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。0 k) X% R+ a0 p1 i! V
`+ @/ `2 {+ B5 t& i# a" i
. Q9 u; B# V; I# ?
- v7 A5 U9 G% C6 US是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。
* t. U! C2 [: e* U另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。) M: j# c" u+ R0 k3 ]+ C
( r T" i9 T: M$ L9 b
' b" H' o G- v% {/ n- \, M! Q$ F
) U" q- c( |# {7 u1 x# `% g
官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
: r1 S0 [5 a& U y除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。. s5 v M# ^' @1 U& d
0 ]9 c t# O$ [) I% _) q: H
: w9 p* x8 C5 c2 ^; \9 P$ Z1 r* ~4 B3 d; X/ U B9 `
另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。" y' r+ P+ _1 l2 u; @
! m$ f! I9 [6 M, a& m5 y: M( i" p) s9 e
) C) [0 z/ @. m+ ^代码示例2 |4 I( U. z+ G* Y+ s4 x/ [1 P
- include "DSP.h"- L$ Z/ {6 I" P: [/ R
- include "arm_math.h"! c/ U! b& E3 Q/ b5 c$ s
- define NPT 1024 //1024点FFT
& ~$ M+ j% |$ H( {+ H3 \/ @9 C3 L - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz
+ P6 r* ~4 y i1 c) z - define PI2 6.28318530717959
g, u8 v: ^5 ~# \5 Y8 ]8 w. _ - float32_t testInput_f32[NPT];0 }/ Y1 ~, a9 P' `: ~
- float32_t testOutput_f32[NPT];
; u+ E4 ^9 w) } - float32_t testOutput[NPT];
) c; B0 r( U% r - 6 u8 ~! O3 A# O5 E& K+ _
- /*
. T2 R6 P$ o1 i ? - *********************************************************************************************************
, f6 f5 X6 c0 F, d' u - * 函 数 名: arm_rfft_fast_f32_app , A! V* `0 F% L1 q+ O& f4 |3 v. I2 S" Z: H
- * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比 5 u: i" ]; m$ _* F
- * 形 参:无
1 r- H: P' A3 Z - * 返 回 值: 无 ! Z; | q+ ^0 E, z
- ********************************************************************************************************* ( U8 b8 T) u. |) i* N% B
- */ $ t# k/ g0 v) z7 k& E0 O& L& ]
- void arm_rfft_fast_f32_app(void) $ O* }% ?' A( L/ }- Z! M
- { ) B/ p8 j6 R5 D F; d
- uint16_t i;
. F) V$ n' f1 K6 x$ W5 D0 l; }0 L - arm_rfft_fast_instance_f32 S;
2 \+ g$ [6 B9 b6 L
+ F# y9 b7 P* e. J4 m% V- /* 实数序列FFT长度 */ 1 z. y+ ~; f( V
- uint16_t fftSize = NPT; % n( B9 ?% W6 t9 Q3 u
- /* 正变换 */
4 T" Q8 A" D) c' l - uint8_t ifftFlag = 0; * N& I) ~( e% `7 I2 d# n- K
5 N& i6 B4 [/ x: x5 c- /* 初始化结构体S中的参数 */
( s- L5 s( ]* D! T. s, @ - arm_rfft_fast_init_f32(&S, fftSize); * d! G8 Q0 x% T
6 }/ ?! v K( | X7 a7 @- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */ 8 u9 l$ Y0 |( |) V
- for(i=0; i<1024; i++) 5 @5 q1 Y4 f0 i* \9 N
- {
: I, I% w# ?, p5 B - /*3种频率 50Hz 2500Hz 2550Hz */ # U. a9 l5 j. @$ l
- testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) + 2 m" B5 M$ s) Y- c# K* z7 F e2 W1 M
- </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) + 2 E& v( P6 c9 k6 j
- 3000*arm_sin_f32(PI2*i*2550.0/Fs);
- Q# _- P3 w; Y8 v - }
5 B4 U/ c& a2 C0 h! ~% M/ B - ) A# I$ {3 x. H$ h6 G& F' j) x X
- /* 1024点实序列快速FFT */
" _, P4 k. _, ]" O6 g/ S - arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag);
, P. Q4 W {- i5 V# h
, B/ m, A1 `/ X) R+ w- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32 _ C7 S6 e" {6 E C: u: s
- 只求解出了512组 5 }8 t2 o" Y' `7 u* a0 j
- */
1 n( q, I* {7 y, ] - arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
2 P8 `! J" S, W2 F. a - ; F' R9 K+ [6 N. B; c
- /* 串口打印求解的模值 */
! h+ x; z5 F% t; a0 e5 `$ M - for(i=0; i<fftSize/2; i++)
" G. W2 z1 {$ L6 Q* d - {
/ w. m0 ], ]* A' g5 [ - printf("%f\r\n", testOutput); . S/ A% d; U, f
- }
# O# T4 b; i- @ u9 Y. U - }
复制代码
. h9 U# l# @2 u8 S结果
' i* T2 U5 B; o# S( O4 w+ O在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。
& |+ L B6 Z) V x( O
6 r6 T) @( I( h9 u' k3 `7 ]$ N1 }; c; D- p3 S( u3 k
! D; E+ ^8 T2 R* e# C
3 O0 e( d+ Z% U# \# M; g: u
2 f; @! a4 j5 f- Z% b k- `2 K/ u# m* m0 ]
" F% m+ d2 ~4 }# T: S& g2 {, R1 M P2 p* Q- b
对数据plot画图,结果如下* o# k# z& ~/ C) Z7 R6 O/ {, Z
* i1 c1 T9 g3 w( E" X
" n1 C) \( D/ o# J5 v8 d' o1 ]2 W6 o n8 ]5 T# ~
可以看出,FFT之后分析出包含信号的频率为50Hz,2500Hz,2550Hz,与生成信号 testInput_f32 = 1000*arm_sin_f32(PI2*i*50.0/Fs) + 2000*arm_sin_f32(PI2*i*2500.0/Fs) + 3000*arm_sin_f32(PI2*i*2550.0/Fs);
! B6 C* W/ C, [& a
- t; ?5 Q/ M+ f' d; H2 _6 C
$ H9 D! S, h6 F3 @& r8 \. a- |" e6 ?* V1 M3 J' x* A2 p
|