之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下
: P8 L, ^/ j2 K" X3 ^1 `: sSTM32F767IGT6
; N* I7 a9 J6 y) \% r$ @, PKeil5.21
2 P7 Z' I% M F# g5 WCube MX4.245 ~4 ?/ C( U" ^+ j+ a7 X
arm_cortexM7lfdp_math.lib
v$ o2 T1 i; G3 P( }正点原子阿波罗开发板
$ ]: l1 ]2 f% q& j/ L) L4 A- j. z) z) A9 [3 s, e
准备DSP库- B9 {" ~% ~0 Q7 T0 @+ A
打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
& h3 r( b5 D5 v0 I* q+ C0 k
: @, |. L/ L: P
1 U0 w0 P, Z7 v6 y! @4 y
) Q4 R' }! F' |! r, }& UDrivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。
; n W$ u( l9 I. A) UExamples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):5 Z: p [ C% ]2 j7 @( D
1 ]5 B5 {. n5 Q y' h( T
. Y# |; z' ]3 Z- ~2 U& X/ Z
2 I" {$ `. l0 L8 {5 jSource 中的文件如下(这些是 DSP库的源文件):3 }- N: I6 ?4 j0 s; t
# ?" k- T9 p( f: H9 m, W" g
: K% P* O" P5 O7 Q4 |$ \5 P
8 | S3 q( t" d5 c Z7 k n% l* ~ Z: {源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。
3 Y/ X1 C" W! u, _1 x6 q1 @) P" ^
) M# F+ u6 V8 a* ~
% N3 K& E+ J: l, @8 _6 Z! K5 D
库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。* U2 M, O& x* Q7 _5 G
" W4 c8 d* P0 N M
: j9 q: g" Z1 r% Y3 |* H/ G% ?# A
' j# c. R) N7 G9 v" i% S# N确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。" x/ @" {7 F/ z+ i) O
?# F" i+ h6 B$ W& v1 x
H6 T2 V% o& C$ g3 x3 T5 R- E# a+ c
! r' @; B6 F6 b I, |# U' Z下图为DSP库已添加到工程中。! n, x4 w0 Z/ f7 ?
) P. v1 U- ?" T5 [; Y% Q$ W0 M
$ G* O$ P8 n. U$ ~+ Z3 L, X# ]0 ]
: j6 w# R8 e$ X4 @& e b3 o库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。
- E! ?* P& f; f& i3 Q! ]* i! E$ o9 F( p! c+ `# K, x! Z7 Z- |
2 G) t8 O9 \8 v+ O$ i N
" L* v* m- r6 R( \4 Y c: t0 F/ R函数说明
- K! m- n" k" d% ~0 b我用到的是实数FFT,即rfft。相关函数参见官方说明。
: h& {. L, A1 |* E4 A$ Q函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
: ]+ v' N0 h3 b, y7 i' r& ^arm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。/ W) R, _3 Y i* F2 h) L; U
- r5 k: \' x# y( O* h. ^: ?& z t$ `, f
0 d" [% H4 t( _5 _0 q+ S1 aS是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。- r: r6 b. w( p
另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。
5 A5 E" k8 q$ g* t5 A6 v: R4 W
- n& U8 L/ R. Y. G7 D! J o
( ?0 s9 o9 D' I* h
: W- R3 P$ |" c$ ]& u( B5 l官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
6 \/ W$ c1 c( a除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。
; v/ x! h; O0 i$ B5 Z
5 z- [" b" O- E7 c
6 ]- T( o4 z. {& C9 X/ K7 q& A% A' v5 }/ B: h- `
另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
* h( u8 B- |+ z4 F/ |. u' B7 T& M+ H, j2 y" V, ?
; _, r% |! t4 N4 R1 x+ x: `
, H. N" c7 |2 L H代码示例! N# ^4 E a& q9 v; @/ w
- include "DSP.h") ~* q* z, e3 f- x# }* ?; Z
- include "arm_math.h"
; w+ s, K% A$ d2 ?8 H - define NPT 1024 //1024点FFT
9 k6 B9 s% N1 T& h3 s$ h# u6 V - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz5 ~# w; D' g O* B d
- define PI2 6.28318530717959
" r- e8 u8 w3 |. ~6 O - float32_t testInput_f32[NPT];
8 H& N F/ c: o3 r - float32_t testOutput_f32[NPT];
& }- f- Y# w0 U& c: f - float32_t testOutput[NPT];+ I) i! Y3 O% x. L5 k
0 n7 g, K- `+ S- /*
! l/ _1 z; c( w7 m) N! F- t - *********************************************************************************************************
" m/ ]& Q9 I* L' y1 K - * 函 数 名: arm_rfft_fast_f32_app
$ k* _9 G P% r - * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比 ! J' ?( w: m. T C( i' K) r, b6 T2 T D
- * 形 参:无
4 J. r4 w% }* L# S$ z4 v, Q p - * 返 回 值: 无 , M, |1 u# ^* e9 Y- V1 _
- ********************************************************************************************************* & U, Z8 `, k! ] L; q8 t, a! F" V& l8 ]
- */ - V! u8 `# B1 f6 q" V# I
- void arm_rfft_fast_f32_app(void) # o5 j' i& f0 G* M: k
- { " c3 H, K- h- U- D
- uint16_t i; ' Z+ I- O d; C0 c; R0 l
- arm_rfft_fast_instance_f32 S;
; q+ ]2 }! `: h0 [/ o8 J, k - 5 K" b3 S0 R/ }8 z9 H
- /* 实数序列FFT长度 */ ( W4 W1 w3 W' A- Q& n( j% H
- uint16_t fftSize = NPT;
+ `. A% F$ m3 o7 L% d( u+ v8 D - /* 正变换 */
: K* u3 h, m e+ N6 b z - uint8_t ifftFlag = 0; * p1 P4 F6 a1 L' _
, \* E- J6 E Y5 u+ `( E- /* 初始化结构体S中的参数 */
\7 R+ N }/ ~& | - arm_rfft_fast_init_f32(&S, fftSize); 8 k! S v7 w- E7 c+ R- x
- + N; w/ j2 f& z
- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */ 4 P" ?" \- @ g* p7 o
- for(i=0; i<1024; i++) 4 Y; g( a2 C4 o# y
- { : b# j5 x# `, V
- /*3种频率 50Hz 2500Hz 2550Hz */
, |0 | W4 o: b! y: m6 A1 ]9 k - testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) + ( p" D! B" c$ {0 l' B. |
- </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) + * d$ @' N0 @+ Z x8 J4 a
- 3000*arm_sin_f32(PI2*i*2550.0/Fs);
8 c" }* E2 s8 M7 i Q \: y; E - } ! l( U8 o2 F( I+ M
- # \* o* ^' _0 ~. y& f1 A0 a0 l
- /* 1024点实序列快速FFT */ B& [; Y$ r6 o/ B; O
- arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag);
3 D, p7 R( N6 E+ q
, ]4 ^5 h: E5 y1 K/ v- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32 4 h1 t1 \# v3 k2 f* x; `! m. W
- 只求解出了512组
3 a" B0 z) h# O4 R o - */ , o, l, x& P3 n" U$ k. [5 D
- arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
4 \0 v; B4 m! S; j
0 f0 K( h1 m/ d3 n; H' e: ?7 `- /* 串口打印求解的模值 */ ( Q8 [5 B2 p9 {$ Q/ R) R3 u$ w
- for(i=0; i<fftSize/2; i++) X$ N( V0 t! P7 d
- {
2 |' t( E( P, e& s( }: D - printf("%f\r\n", testOutput); 3 P3 k3 t% G3 e* F+ p
- }
) I7 t- @5 Y" @ - }
复制代码 4 l4 V% t0 { J: [
结果
! o* X7 t# a0 T4 `! z在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。
) M* m' J! {* W2 v7 R# P/ l$ B6 _) P: n- u( ]5 m
, r/ _$ g5 l* K( p/ t1 F. B5 x
7 M3 ^+ u9 w" m# H( }" g( p7 w4 u e9 H3 s
3 z% f# d6 l8 K
1 K$ \4 q) J; X0 N0 e- y
. b& O3 H. {8 V) m# o* O" Q) r; M2 w
对数据plot画图,结果如下4 |: v, J- I, ]
" e- p3 X/ ^) X+ R O( F2 |& n" Q3 M! k; [
/ C* }6 T, M% f y% Y: a$ z可以看出,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);
- h& B$ ?/ W9 e' A' a% v( v6 d H. k' e8 G/ T1 z
% J$ J$ n* J9 L% i
; I2 c$ ]) M0 U
|