之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下+ x8 v/ v$ S) ^6 w$ c
STM32F767IGT6
* I2 \4 j6 q5 _Keil5.21- i# {' X9 M2 E# A6 q: O7 T: N
Cube MX4.24
' w# X5 D3 ]) H3 `0 n2 J2 M) m0 K/ `arm_cortexM7lfdp_math.lib
2 N5 G; L H: q6 L: X正点原子阿波罗开发板6 ~( t) N3 g2 [$ c7 Y7 K8 d9 q
: y2 [- D' p% m5 h4 @9 x
准备DSP库( ^6 x# @: [& k# F
打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
5 A5 I6 g( o+ S8 U. K3 T4 ^: I+ r7 O7 W: ~$ j& f5 p0 G% b/ x
$ X, i" {% m& p: t# O* e& j" R# R+ z# \6 P4 f1 R
Drivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。* f) `+ ^$ a0 C8 }7 i( O0 Q, a
Examples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):: a5 K# e" g" G \
# u7 U, |3 W5 |( b
; I- M4 U( s0 X9 O+ S# _; X- |3 Z- H) f
Source 中的文件如下(这些是 DSP库的源文件):
& `! G) ~8 h* q. B* y7 t9 ?' y) M
9 Q+ h5 f& z% @# o
6 w+ @& h, Y3 {5 S0 x0 G源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。
3 W0 @- b: D q& `! t8 j; o5 ]0 d4 {! s& m: q t! f
2 R' ~& K" T5 \6 ^& ], W9 M% s' @) w/ F2 X$ P% _0 X( ?
库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。. o5 `9 k f; D; V% Z
2 Y. z( _. K/ c1 d$ g. D. n
% x! s! Y2 u# B! H. ~: U4 r6 m) Z' ?3 f# i/ L4 j
确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。
0 Y3 n& a- A& }" m, K' B0 q( X, p: J, a0 b- U7 t, z Q
, Y3 B7 d" d7 h6 o. B* I' _0 |, \
, z' j: o4 ?; u1 }$ A下图为DSP库已添加到工程中。
8 z. ]* @( j# X. d- V: Q5 {0 T* p# n% W1 b/ n `, Q. `9 ?8 N) Q
9 _, V! N, q- k" i7 n* J+ p6 ?
; T# _) z1 l; u9 x: M; @库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。3 W }! r3 {$ N& l+ Q$ R
4 N4 [& x4 P& U7 ?! R
6 P# Q3 L: O. O" |& N& o1 L4 [
3 V6 j9 p# @1 j函数说明! [% a$ W% a6 O1 b4 y! q' F; b( _7 c
我用到的是实数FFT,即rfft。相关函数参见官方说明。
! D |2 y+ O0 S) J5 X8 p2 u函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。' \$ d5 G* r, U9 o$ N. q9 b" m
arm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。
+ P: T% }9 R4 I6 t4 n( K2 R: T' q( E/ Z' \, D
) w; D3 k$ i2 }1 k ?2 q
+ i6 J7 I# @$ nS是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。: W4 I( N* F6 @4 [; t1 I
另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。
2 c* O+ _4 n0 `% y& f8 A/ d# s- Q! T* L, j4 Z5 ~ C) x
8 {- S) J/ \3 Z: |' U; I
& c3 M) n5 u. E. x6 H' Q- \官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”! q) M% b* l6 N! u% @8 ?4 y
除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。
" [* Q3 [/ @4 q4 M2 a4 P9 B9 [. Y: t/ s2 G
( b; E: J7 t0 n; l* Q' W
! n8 P- m; H. o0 h' h, h$ e: g另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
3 v5 q, a6 h3 f2 M, H
+ s+ D |$ h/ {1 j
* u( o l X* m; N0 w0 e/ R$ F7 q4 r3 F" w |
代码示例
$ ^; V5 o# a$ O% k3 [' ` X, W- include "DSP.h"
* j% u- K" E1 p- } - include "arm_math.h"" J+ ~. O5 u' f7 f9 P8 \# l
- define NPT 1024 //1024点FFT
5 M$ ?) }* {) ]2 D U$ m - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz
. S8 u+ q- p6 r8 d- _. d l( ? - define PI2 6.28318530717959
! {- ?# j6 k- l2 H - float32_t testInput_f32[NPT];; {9 O) W' C7 w0 c4 I* G* [
- float32_t testOutput_f32[NPT];
8 Y8 J6 Z i& P0 p( ^5 {( c5 e - float32_t testOutput[NPT]; i) z! _& S a4 L7 Z6 d
5 [' A. q. x; a/ j- /* $ @ @1 B7 y: V4 I2 m
- *********************************************************************************************************
: N$ K/ J; _4 [6 M1 _6 E - * 函 数 名: arm_rfft_fast_f32_app 9 k: O- R* F* L6 r- |; M' C
- * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比
9 t2 B, ]% B; y; ?9 {$ A - * 形 参:无 0 K) F$ S; V9 p0 q4 y4 }4 Y
- * 返 回 值: 无
6 [* x' a1 H9 d9 `8 {* w - ********************************************************************************************************* ; p7 p# ?& c& k; Q4 ^
- */
/ U0 J& U9 S, ? - void arm_rfft_fast_f32_app(void)
# c- N! E/ C/ Q) B. s3 C7 P+ b - {
. ~" i& W) |1 v( Y+ c1 S! F - uint16_t i;
, y& t R! `9 w' x* e, s* c - arm_rfft_fast_instance_f32 S; ^; N+ [- _! j" t8 M; ?
- ) D5 y3 s2 ^, w! h
- /* 实数序列FFT长度 */
, {3 }- ^% `" @2 C4 D% Z$ R - uint16_t fftSize = NPT; 6 W. F r) t/ R, }
- /* 正变换 */
$ w" k. b$ ~. c+ i* b. L6 x' u - uint8_t ifftFlag = 0;
: Y7 i5 u9 @* P2 ~1 Z - 3 I& J* e) Z& X; v
- /* 初始化结构体S中的参数 */
0 o. a* @# y3 `! i; l( [ - arm_rfft_fast_init_f32(&S, fftSize); 7 d# J2 c- u, V; B$ ?
' B9 t' \) K" B' }3 Y( u- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */ 7 F$ C; o5 y# d4 A' j
- for(i=0; i<1024; i++) 4 [$ z* Q3 o1 T' J. X* z, Z& B( D
- {
6 o8 p2 ~7 [; _" g; y! J - /*3种频率 50Hz 2500Hz 2550Hz */ 1 ?0 Q" s# }5 k! \) z) m- L% b
- testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) + 4 g: z' A L, _, i) i
- </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) +
5 N1 E0 D+ B7 u6 f: L; l - 3000*arm_sin_f32(PI2*i*2550.0/Fs);
4 \3 @/ m0 P0 n( o - }
) @& \% Z) `% ?4 U/ ~
8 ]" c t3 Y2 i! g6 d' L; U- /* 1024点实序列快速FFT */ ' g1 D$ M4 ~8 Q. Q+ Y1 T/ J
- arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag);
9 h0 Z6 l# A- y! y" f8 N - 9 h! q" u) `1 }3 d$ P2 C3 m7 u
- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32 / K+ x! @9 Y7 a/ }& f
- 只求解出了512组 5 ~: B% U% A. p6 X, R: _
- */
+ I3 w# [5 B4 R( W* E - arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
# z u& J4 q: j4 ?. e6 B - * g$ m2 v2 c) ^, u$ C( M/ Z
- /* 串口打印求解的模值 */ |$ R9 [8 }( D% i4 }: `
- for(i=0; i<fftSize/2; i++)
. s W' I% N8 b5 W. o% H, _. l3 n - { # p! M: p6 J( r, f9 Y9 B$ c; ~" L. b" h! ^
- printf("%f\r\n", testOutput);
) @& \0 ?# ^( i: J - }
' `" h# I ~# }. h - }
复制代码 3 }8 q2 f! S5 }5 v5 c
结果
' h( U1 M! [) u; p在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。
$ L1 [3 _1 i+ L* @1 S
5 I0 e' r1 W1 |5 E" L/ Q
8 `; p% p, o& b$ g, o5 Z7 S! H# p7 s( p+ x( s
$ h, o: ^: O, C* u- A% ~
2 R+ }# O1 a9 _" x q5 h! n$ m* q& s0 {" J9 Y3 \
* Z9 {, Y) p: w8 X* Q+ _# Z+ v5 j
6 a* L3 M( G. V) L) n4 S对数据plot画图,结果如下7 O3 ^. n" G7 @" P
( R |. m1 |. ]8 E0 K# p
6 C8 W7 Z7 l; A# {& s# C4 G0 v) A& C/ g
可以看出,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' N' X! I! x7 p+ e
& z/ r0 ]; C! y7 \9 d
+ _; z1 b& \# y! `% _4 A! A6 \; D+ U
|