之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下
8 Q, a0 H- \% x5 r& u/ o, f3 KSTM32F767IGT60 F( C8 M7 k. K6 }0 [6 D
Keil5.219 z! x) i7 n) }. t
Cube MX4.24% {% Y7 B) ~# g7 F0 `1 I
arm_cortexM7lfdp_math.lib
8 \3 W0 }8 P; ~: X- c正点原子阿波罗开发板
$ ^. u2 L4 M: i" t+ L" Z* s" ^% s! z1 z& g5 t
准备DSP库
: O+ }3 n. c) @7 u0 L+ ^打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
2 ~3 i1 f. B5 i! Z+ D/ V N- c3 d7 c
" B) e* }/ w4 { X$ q3 u
/ g( l" C* w% S& @& a+ QDrivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。
: }, U! h. s0 A- ^+ p; v' r8 i# `Examples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):% A0 a1 K: {& { I5 O
' e% v/ m9 N' p, E1 Z
) a3 k N! t) g
" g: s- _' ]+ v- d: TSource 中的文件如下(这些是 DSP库的源文件):6 r! S4 F |5 R, M3 v# R, [3 M5 Y
& | G4 R% X2 p. p' X" L
9 K1 q" S4 S `
: w- y1 `' m2 e源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。
, Q, [/ n& J" h8 P! Y' [# g0 L5 |9 g2 e
0 R7 u$ a4 {, P2 y
! Z& n0 F! ^# \" G% W* {! g
库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。% O: l0 n" C6 e7 P. S+ M
$ _" h( ?, L( r
- T4 I' A, |. {! x2 C! o# M' W0 b3 y5 O) V5 L' U I# b/ K. n
确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。
. _8 t) Y! L7 n% @/ m) b
8 l6 l! P4 S0 n; m' q4 X+ [
- e/ c# l$ T9 w2 ?" L3 x6 P+ d0 U6 s8 s/ y
下图为DSP库已添加到工程中。
6 }. W# @; Y2 e# I; [: \" T9 Y5 n
# N/ Q2 l4 E `- ?. m% c; C& n3 A9 n y3 v8 X9 q, I" r
1 O% } m1 |% s+ H3 Z8 ]( A# h, w库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。 i1 `6 Z6 B* c2 x
: N N7 u8 b5 n% |
1 L: b7 ?) k8 D5 P: ^
( w1 q% u! U5 k; ^3 B: g函数说明+ @% ^5 D7 m1 J3 `, r' f
我用到的是实数FFT,即rfft。相关函数参见官方说明。& l6 j5 T# N. I' b
函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
3 L% x5 d; \* ~0 `# Parm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。8 D) }: U2 G5 P
& Q0 ?! [2 S, W
* h* Z3 I, f. e# i* ]( S5 B9 ]/ Y5 Z3 Z6 `
S是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。/ _7 K' m1 S) U8 C) R
另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。- U# {2 Z1 }+ F# P& r) \0 ~
' D6 \/ V" ^( J6 G
; e7 g# q9 H6 ?* b S5 @
! e$ n( n* _8 G' }# X
官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
n4 m/ \, d% y; W6 {1 R. w除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。
2 o3 V9 ?4 W7 q" E' m& w1 b/ U
) p( q+ d$ q& d' i! g6 t
- t6 \$ o8 A5 ]7 j5 e) T
5 @0 ~+ _# a( ^6 d! |) N- @' B另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
% t! d2 \% |, L/ B" T9 h1 E
& ?* d4 B! g! j8 e8 o# L. k: S# v C
9 A9 D! c0 ]/ [" o: @
% T5 B6 K: c, I& m( c! e代码示例% t( \, U6 s: o( u C4 v
- include "DSP.h"7 o3 ]( ^1 t( _0 P
- include "arm_math.h"# Q+ h8 X5 z9 C
- define NPT 1024 //1024点FFT$ e; j, t" a- c, T: g/ \1 a
- define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz
# ~/ \1 D/ g! K - define PI2 6.28318530717959+ y( _$ d) x% x2 Y
- float32_t testInput_f32[NPT];) b& m: h' B. Z5 J$ G
- float32_t testOutput_f32[NPT];( O0 c" Q) k7 { J5 m
- float32_t testOutput[NPT];
; j5 M& ~- W: c7 | - ' o' T3 g7 D; V3 B( v
- /*
- |$ k# ]+ a, X2 g) q; i/ T - ********************************************************************************************************* 7 R2 Z' C3 F. X$ F0 C0 @% {* \0 c* U7 _
- * 函 数 名: arm_rfft_fast_f32_app
' ]* U' d- g0 F6 E - * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比 5 B$ F8 O! I; Y6 K: n5 M! c
- * 形 参:无
' N! V( `3 `/ x% h8 H$ |7 H4 z - * 返 回 值: 无 1 w! X* m7 G/ ]& T7 u/ I
- ********************************************************************************************************* ' T/ u. u1 U5 d. {8 ]8 \$ o2 x3 e
- */
! h0 w1 v& h% B1 Q' d' r - void arm_rfft_fast_f32_app(void)
# |- f! k5 a! `" X& f; b - { 1 H0 B! ]" b9 b) x, K5 f) p# [$ q
- uint16_t i; N1 H T4 g, m
- arm_rfft_fast_instance_f32 S;
2 J2 g# k+ q9 x( y$ r8 B - 6 ^$ k2 ]# J) `6 S3 n" g
- /* 实数序列FFT长度 */ ) [0 b6 F3 l9 U9 ?
- uint16_t fftSize = NPT;
$ i$ X" y" w1 E) h. L* O ?8 S% c - /* 正变换 */
" ?# ]# T: s9 n$ m, c4 { - uint8_t ifftFlag = 0; ' K) E( U1 {& i1 m. F; B
; K4 u# V$ B/ \: Z6 D7 c- w- /* 初始化结构体S中的参数 */ 7 O# B' Z" I% a0 e! d4 B
- arm_rfft_fast_init_f32(&S, fftSize);
" m3 Q" [: E% i' j$ ^ Q
8 I6 K j) y+ ^) S3 H. _: Y4 V- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */ : F* \; [( _9 @5 l9 u2 V. _7 W
- for(i=0; i<1024; i++)
6 ~- P( J& O- C/ U - {
; y7 b+ B& x2 c5 h - /*3种频率 50Hz 2500Hz 2550Hz */
1 l5 |1 H) `* C# J: n4 g. R, Z - testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) + 9 r2 n- t) Z2 K/ l/ i4 `
- </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) +
4 N1 @+ ~7 D6 S - 3000*arm_sin_f32(PI2*i*2550.0/Fs);
+ T/ D Q* D0 ^; d - }
' v( _! B% s# {$ |- a. U
; I+ Z8 S! r/ K# d' f- /* 1024点实序列快速FFT */
5 E9 J- ~# a9 E5 B - arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag);
! _5 O; _$ v+ i2 f: U5 }
4 O4 E9 h1 K W# M5 D: B& ]- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32
; Z/ t, p* e6 M - 只求解出了512组 0 ?4 ^% Y. }% K( ~3 |
- */ ( p( A& ]" C# }' H) }: _1 c
- arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
/ i/ x. o1 y4 d7 s* ~
/ s8 F8 c" w' t. P. G5 l- /* 串口打印求解的模值 */
0 n' O. q0 l: F: I m - for(i=0; i<fftSize/2; i++) 0 x( ?# S2 U3 t9 }; B- D" A- w
- { ' F0 u v5 {8 [. e# C
- printf("%f\r\n", testOutput); + y' K( o9 f% ]* N8 I
- }
9 f& R3 Z' V1 Z: d: P - }
复制代码 4 t+ S0 N; x, Z) I9 ^4 y
结果) W" y& Q. S2 `" a9 ~) P6 O
在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。8 `; O% F F2 u6 S* l" u& i- V
) f9 S9 e% S. O+ u
! S& I& H! @# Z/ Y
9 M3 b, q7 j3 J U8 Z$ q$ f& ~$ A
7 B# w' P; W0 @* o
e' P K: n) {6 ~4 S- n
0 U9 v; G1 F! C, t7 W7 _4 e9 [2 [4 L$ i& k* B! X6 V
. W; z" S% O, N& k) q3 P对数据plot画图,结果如下" L& A* a# A) z' m' i* x
4 l! h2 o3 ^# W2 N g2 D5 s
% X" V# V1 |- f+ O7 e5 D) m; R
' ?6 O2 o+ o/ k可以看出,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);
: f, m, v: J* b+ U8 |! d2 ^* o+ U4 Q
& Q( z; P" @; |
1 Y( @. }$ z8 s+ N
|