之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下
4 R/ o6 I& e ^0 tSTM32F767IGT6$ ~5 y: G# e* d$ Z# W
Keil5.21
$ {3 N" k1 f6 K$ ^Cube MX4.24& b3 J- b; G4 J5 W# C1 Y' N7 a! i# d
arm_cortexM7lfdp_math.lib* Q( e4 `* Q, {
正点原子阿波罗开发板$ t. I) s' ~( n4 Q
( h4 O6 k" s( t, q准备DSP库! @4 O! p* ?5 M( s
打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
* M# B( T- b. h# P: e2 `
9 G$ K+ S5 `! c5 |/ w* U
7 ^, v. N0 x# l" j. `+ \' @* g. r4 \+ f# K- Y! ]
Drivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。
' b' t& c& a7 }- V* J$ tExamples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):
' I- D' t% b# X" I7 P$ r4 k9 [: n5 }1 |5 D2 r3 `" A
. z* u4 x- x! }& M- j1 A/ d
- O/ H( D/ _8 D" h. U2 ASource 中的文件如下(这些是 DSP库的源文件):# [6 I; a1 z, s# u
2 h- w* J' q! ?; G. A. T% r6 F2 T j+ B; m1 E7 ^3 P
+ d$ k3 Y# T% ~7 |0 w+ m$ [; }源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。6 r* m6 Q& w( E9 [7 f9 Q; d9 O
- u6 Y) V: J& m9 M2 h6 `
+ J( z8 B" L, ?: ]" f$ n
0 L# q. D$ g/ i3 E7 @库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。! p6 v/ {& O# o9 p4 k, q
( N6 H( N; I. E9 c b, J& Y
+ I ~9 q$ g$ a) c8 J1 Q
( L# [: Q0 U, n# S7 }确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。( J0 A& n, U/ `* n
& h6 Z( u K c) R
2 Q+ A9 \) {+ a1 }9 _% Y0 I$ K
. J- A3 ?! F& j8 o# Y' E, d
下图为DSP库已添加到工程中。
8 K7 x2 e$ H" j( S/ ?# M7 C1 c
) O/ y, T0 x) A5 { B
+ j% n: |' C( \' ] a1 x6 Y! m+ {& g, W
库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。; e8 ~! }: [) q( R: r( ?
3 @6 W7 W+ c1 |
- F j) @. U7 e* t2 [8 W* H$ }2 W) G5 c- p$ P1 C* j& }/ Q
函数说明5 A1 H; n0 q+ i, g- c4 b( q
我用到的是实数FFT,即rfft。相关函数参见官方说明。
H5 ^/ e9 A. f6 t6 `6 Z函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
9 j. _. K' e$ l6 qarm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。
) | c1 d+ p8 z1 g5 P5 }3 \+ f) Z. D0 H+ c' r, N5 l
W: `& {/ _9 C7 D \! B% Y) @' k
# h4 T% d* ?3 O+ K H; v1 F4 f
S是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。$ q/ d. w6 O# Q2 E! s' N
另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。2 A; P5 D- M0 w" W
4 ]4 k% L. n' v2 T; M4 n' h! i( @2 J( r
: T& u9 h7 b" l, C5 b- O' Q! M官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
) B' T* d1 M: i ^! H- A( N除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。( ], ]2 q4 f: ]" G' T
: T3 d" ~: e& x5 y8 w7 Y
. @* r2 v5 w7 f T
9 w6 Z' e/ A2 T/ U另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
% G' i' Z8 c9 ~' ?
6 p2 c6 P& {4 E( _4 F* O7 O, {; A) d. Z" r+ r: F$ V
* h0 Q# Q1 t1 ], m( t" ?, Y t
代码示例
% X+ o2 e1 E' O1 ~0 Y- include "DSP.h"
, O! }+ C/ A9 m6 g8 q4 C2 B - include "arm_math.h"1 h6 ~1 g9 Q- A
- define NPT 1024 //1024点FFT
% F5 W) F4 @3 r2 N' P4 E5 g - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz$ _* p) s7 k( X
- define PI2 6.28318530717959- J( z9 A5 u% s0 s B V
- float32_t testInput_f32[NPT];
& S( j9 ~8 O! I6 T - float32_t testOutput_f32[NPT];
7 \5 L5 Q' u% P5 O9 c - float32_t testOutput[NPT];$ Z# L w" Z3 `. [; J0 }
- 0 P) |6 N7 m* d+ o9 R6 M- c
- /* % ?* c4 s0 T! ^; r( i, |
- *********************************************************************************************************
3 U! W/ P! p9 Y4 ?( S/ V - * 函 数 名: arm_rfft_fast_f32_app & N, }0 L3 M( F+ f7 T9 V
- * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比 4 ^8 L0 K3 I9 m% i# L- F
- * 形 参:无 ) x" a2 o' A# ^! M
- * 返 回 值: 无 ; ?/ c2 d' K8 e# M! T- }
- ********************************************************************************************************* * U# @; H1 ^0 }0 F- P& H
- */
4 Q* o% { U+ I& N) e! X1 J - void arm_rfft_fast_f32_app(void)
' I% u1 a P1 A4 ^* K; l - {
3 n- N; Q+ t2 W0 { - uint16_t i;
3 K( a& [3 j( @4 s+ J! L - arm_rfft_fast_instance_f32 S;
+ P, k( q, R7 w" S4 w+ Z - ' T' U* R# ?! E! g- K7 P2 \
- /* 实数序列FFT长度 */ 9 h5 r* ~3 {2 r' l0 R
- uint16_t fftSize = NPT; : I& ?2 j' C6 U
- /* 正变换 */ $ \( v9 J X- W- w
- uint8_t ifftFlag = 0; " c3 U" c2 h5 g C9 ~ T9 q0 M
- 0 ?4 @2 R8 m5 V) _
- /* 初始化结构体S中的参数 */ 4 Z5 Z3 k! W% {5 j
- arm_rfft_fast_init_f32(&S, fftSize);
9 P% W& K5 ^( T8 f8 y* S; N- w) M
2 w# l. E1 \. M- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
. }/ V; A/ `% G9 j# i x - for(i=0; i<1024; i++) + @' y0 V z/ w/ ^) F. C
- { " o6 U; j. E8 j" n2 P3 j
- /*3种频率 50Hz 2500Hz 2550Hz */ " E ], U: D1 u& g" w) a! D$ E
- testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) + ( \+ V8 L% b9 W2 o
- </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) +
; \9 G* X/ z6 O - 3000*arm_sin_f32(PI2*i*2550.0/Fs);
' J, _# B' ~' T* h - } , z2 B! e& h1 J4 _6 Y
$ l5 w" L9 t' B) t. I* {+ I0 f- /* 1024点实序列快速FFT */
+ k8 H: y; w R3 I$ ? o& Y; e - arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag); 3 ~1 a+ M; C1 X, ]
- . I# P2 e' |) T6 a
- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32 0 N0 S: w8 Z% U6 \ g3 t
- 只求解出了512组 / K8 P) z1 R+ w; O% \5 l
- */
3 x! ^" [* O ] - arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
, E f! [$ J& u0 F" Z& |/ M( }
F: X1 O4 m" A" O* ~& a6 x- /* 串口打印求解的模值 */ : @# z( P, \, N$ y
- for(i=0; i<fftSize/2; i++)
( }1 x) A j! M. @* y; \ - {
" Q' g3 \( {1 `9 Z7 @ t - printf("%f\r\n", testOutput); 1 W" g% l# L h4 Y; @% w0 Y
- } 9 K o7 e9 D7 z( Y7 a/ j" U+ ]0 E. r: y
- }
复制代码 $ W; G, q& L1 v' C' a
结果
* o& V8 I* w' ?( X$ U在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。, X( Z; o5 l7 Y1 c
* N% A9 {0 s% p' j8 ]1 u# q3 ^ u8 A+ w* e/ Z3 R; ~+ O7 B
: t2 _/ a/ R C4 R5 y/ f) G
0 z9 I0 K9 f7 M: i- {) g, N: f- Z1 V/ M6 y
/ v2 G# o$ K+ f: z
9 b& Z3 o1 E6 {5 j9 K( {! t
$ h- `) w+ f8 J; X" _7 ]1 F3 S' {5 n对数据plot画图,结果如下
& o8 M* n. G4 w$ Z7 I' ?2 G0 t4 Y& O7 D# T3 u$ H& n) C# [; j
- D8 Q- P% d' w! w# P
8 C5 n8 h7 v1 M" J8 u
可以看出,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);1 C6 w. x2 F2 C4 ~. p
. @* W% I3 W W2 h' ~9 p2 O1 H9 Q% S; }. |. N
1 U; V3 @: O4 ?7 a, u D: ~) c |