你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32的FFT运算实例

[复制链接]
yumeii 发布时间:2019-12-19 13:28
ST公司的STM32F411discovery开发板,就如何实现浮点FFT运算做个简单应用示例,并给些应用提醒。下面用到的IDE是ARM MDK。! e9 T; v7 u, d0 e% |/ b  p+ R" q2 M6 ~/ J
) C& J8 E- u, m: i: V" b3 P

% C2 V3 g5 P( `; `) @因为要用到ARM公司提供的DSP库,所以需先开启一些必要的宏开关并添加相关库文件。STM32F4芯片支持DSP指令且自带浮点运算单元即FPU。
$ x2 _8 ~- l7 D/ y7 v) W2 `3 e% m4 v2 P% H

2 m1 c% r$ \* w% v0 Z$ S. T4 X在MDK集成开发环境的相关配置栏添加__CC_ARM,__TARGET_FPU_VFP,ARM_MATH_CM4,并启用了硬件FPU。
7 {+ X; U4 m- x: r  H4 y: X% r2 ^# u) k7 e( ]" N6 D0 T

9 U  }) [  ^! V, N4 J 1.png . @% `4 Y3 O8 v3 s; r+ t
# V& C3 L1 A  P6 Z. E

$ k2 y$ ?2 s; L! E$ Y添加ARM提供的DSP库文件。如果使用STM32Cube库的话,该库文件一般在这个目录下【以STM32F4cube库为例】,有4个库文件。0 u  V/ F- y) j- p
' P/ d/ ]& z+ ^# e
0 m. s/ C' B3 l: _& R1 G% q( J
2.png
; k- u% m3 [  F3 I. @
9 P/ E  a: N. v: D/ c  H; A, P4 [- K

8 K3 K7 M3 T: ^/ i# V其中cortexM4b是基于大端存储模式但不支持硬件FPU时所用的DSP库文件,cortexM4l是基于小端存储模式但不支持硬件FPU时所用的DSP库文件。而cortexM4bf是基于大端存储模式且支持硬件FPU的芯片所用到的库文件,显然,cortexM4lf是基于小端模式且支持硬件FPU的芯片所用到的库文件。2 k! `3 R9 ^. A6 [  ?

" X2 u, |) X1 i$ S8 g; ?

4 u0 t* k: Z: {; _6 A; h; f这里我们添加arm_cortexM4lf_math.lib即可,STM32芯片采用小端存储模式,且STM32F4内置FPU硬件单元。
) c! b. G( B3 A, o$ O" I4 s. v% N
( z3 F# i: E9 f9 u2 k& j. v8 |9 x& _
2 Z5 u& I# A$ i7 w- e
这里提醒下,首先,DSP库不要选择错了。其次,在添加.lib库文件时,添加时注意选择文件类型为lib。如果以别的文件类型强行添加进去后,编译可能提示出错。有时一会半会还找不到出错原因。( y5 c5 {# Z% U+ Q" I' b2 R, F
7 i& Q% _" d  A7 |+ b# |2 j
" A$ F9 M3 D3 Q! p, A
3.png
$ \$ O  m5 N6 D( L/ w4 Z1 M2 ^$ K0 }9 [. S. z2 c( u3 T

* Q- D( h8 e" n* E, I另外,我们还需在用户代码里添加包含文件:#include"arm_math.h"。至于其它头文件按需添加即可,比方math.h头文件。) u( B/ B3 |1 U  M

4 |0 q+ B. M: V, z! r: u

; }( n! c, i" ^) z/ C3 F( d下面开始看看具体的代码实现。
: l6 S+ U( b. G8 p0 ^* i2 K
7 V! s  G1 a# X( N1 p! L

8 m+ Z  e% Q% {( t9 R先做基本变量的准备及定义,比如用于FFT运算的点数,用于FFT运算的输入数据数组,输出数据数组、存放各频点初始相位的数据数组等。# d2 E! ~1 F, E; z) Z+ ~& l# F7 d
  s9 b8 d; i2 l0 d/ a% }
9 L  G$ N+ p) E" J
4.png " K& Q8 d) G% ?8 z. w2 O: W7 q
# ]* q' s+ n+ S% U+ u+ U
( T: \: S0 z; @5 I% j$ I9 d$ J8 n
这里使用几个正、余弦信号函数叠加产生一组信号序列。其中,有一个幅度为3的直流分量。另外几个分别是频率为200Hz、400Hz、600Hz、800Hz、1000H,幅度值分别是1、2、4、8、16的正、余弦信号函数。各输入数据项的虚部均设置为0。& N+ x) p5 u& B6 Q' P8 u8 o! H+ k% X
# R% G! b7 q: m" L# G& ^0 F
! Q8 Y6 b. Z3 G* G. t+ j- ~
5.png
5 O- P, W& ~1 i) T
; V; T$ c- ~$ F

- m8 A/ E: i- v这里假设采样频率Fs=5120Hz,期望的最小频率分辨率为20Hz,则最小采样点数不要少于5120/20,即这里选择256点。【如果希望频率分辨率更高,可以加多采样点数】。5 U- }; V' c5 k4 {$ V8 [" L

% W2 t; E5 ^$ r
3 t% Z' a3 T! F, E* o, {
其中,采样频率不得低于最高被采样信号频率的2倍。关于这个采样频率及采样点数的选择给几点提醒供参考:
, o" c, Y, e3 a6 k7 {3 z: l  O/ i3 E4 t0 m+ I% r% ~

8 k& M* |5 P: P3 D1、采样点数它往往有个最小值需求,由采样频率和所期望的最小频率分辨率决定。+ R; k" x- J7 d( y, Q" H
) s6 \& r9 w+ L' A. E4 P- y

6 C6 i  ^2 l6 ]2 i% ]0 X) U2、整体上讲,增加采样点数有利于提高频率分辨率或改善运算结果。不过,增大样点数时既要考虑它对计算速度影响,同时还得考虑对微处理器内存的开销。比方128点和1024点的内存开销差别就较大了。
) _) n$ J8 w: R! y/ V/ X
1 B( t9 S) [7 g) k3 b& f0 X

2 T2 U7 v5 [. u6 e; ^" T% r3、在选择采样点数时,除了考虑满足最小值要求外,还得结合具体选用的FFT相关函数类型,比方说是基4还是基2的。如果使用基4的FFT运算函数,则样点数须满足4的整数幂。如果使用基2的FFT运算函数,则样点数须满足2的整数幂。3 F$ U5 P% @% s  i: O- U) a

1 S# {; C, ]5 @0 ~
5 a  M7 H8 S4 {: D( U7 a( p5 I
4、具体到使用ARM的DSP库函数进行FFT运算时,还得遵循函数本身所支持的样点数约定,并不是你想多少就多少。比如,以下面函数为例:
2 S  G  T2 U$ O/ L5 x
/ h, @# b# M3 R; A

( v9 N* j" y/ `8 A: O 6.png
) k: Z, F7 }. ^% c. U( U
& g  m" U7 H* m  S+ X% {9 f
/ i" B/ n" y) @, B) O5 C. x: ~
基于上面FFT函数,如果你使用128点、512点、甚至高于1024点都不支持,或者说计算结果误差很大。【当你点数选择不当时,编译时目前是没有任何提示的。】
+ u; \7 J) D: G4 E* W6 w3 Y5 D
" `( f& q/ C+ G8 Q

+ j( S$ F& f3 f* n5、有些场合为了满足FFT运算要求,计算所需样点数可能多于实际数据量,这时我们在数据序列尾部补零凑齐。比方经过评估希望512点,结果实际数据才500个点,我们可以考虑补零以便于计算。这样做可以减小栅栏效应,但不会提高实际频率分辨率。3 r. O; O" `& S8 A7 U3 Y+ i) c# P

& G6 y: r+ j9 P: W

  E) |( k5 S. P# I, E7 D6、采样频率要满足不低于最高被采样信号频率的2倍的要求,但并非单纯的越高越好。一般取其3~6倍,具体应用时合理选择。
: W9 @2 c/ O& p9 l
# K( h  A8 J+ n2 ^2 N- a: r8 |

& L6 @6 }! r  T% t$ A8 U关于采样点数的拟定就提醒这些,尤其上面的第3点、第4点,在使用相关函数时,最好看看函数原型说明。【注:我们在PC端使用诸如Matlab、Octave等数学工具时样点数往往可以做到比较随意、自由。】+ |3 w8 k* O2 y1 U

2 i" q( o- Q' N5 P' T4 |

- }8 r5 M# b: O+ n好,基本数据准备工作做完了,下面就是调用几个函数来完成FFT运算工作。
: T% D" a) p- L, m# M
2 i! M( f" A; I9 l: W
$ W( N4 N8 n5 Q0 C5 R
7.png 5 a5 f0 I' D" S2 t! Z
& z$ M6 \, W/ r& h0 x6 P

9 {- z: B0 O. D! r运行相关代码后,我们可以看到不同频点所对应的模值。0 s/ ^# J1 R. a

, X2 R8 m+ r8 A  W7 a; E) _
  G7 b3 V. R* y9 o8 p" a% R9 l
对于直流信号,其对应的频点位为0,计算出来的模值是其实际幅度的N倍,【N即FFT运算用到的样点数,这里就是256】,其它非直流频点的模值经FFT计算后的结果为实际幅度值的N/2倍。% q2 s6 _2 \: S( f! p% h$ F

  t1 X: W( @+ E4 B( s9 L8 b! ?
/ P. l# U8 F% |6 W1 H
我们可以先使用Matlab或Octave模拟一下输出结果。下面是用Octave模拟出来的结果。4 Q" S9 e& E- h0 T' J

5 ^0 P0 x  m2 X" I1 G  U( m

& \, l! z4 g  ?' u" ]' L: e6 F 8.png
! s! {2 [* M% F
$ n4 Y* \! r5 C

; z: v( E2 k' E/ ?! m5 p2 G# r5 {因为经过FFT运算后的输出结果由对称的两部分组成,我们只需查看前面半部分即可。
2 G1 B; U  H, Y0 k2 L6 u4 ^; ^0 c
7 C$ \: i1 q/ z/ b
# @1 l" j& l/ D3 d  U' O: K9 x6 S5 R8 C
如下图所示:
& ]# ^0 u! N: A$ E: l& k% J9 D4 b1 w1 {
( u9 g- ~  b4 Q$ P0 D. q3 U) X
9.png
5 W0 h" ~# X% r$ e. m/ R, I6 H  s4 x  n
9 x& x$ d- X; Z* ?
因为采样频率是5120,采样点数为256,即每两个采样点的频率间隔为20Hz.我们不难从上图看出直流分量0Hz以及200Hz、400Hz、600Hz、800Hz、1000Hz几个频点信号的位置及模值。图中0点位处所对应的模值就是直流分量的模值。因为各点位的频率间隔为20Hz,所以图中点位50处所对应的模值就是原信号中1000Hz余弦信号的模值。【前面提过了,FFT计算所得模值跟各频点信号实际幅度值还有个对应关系】
& R. G8 S# Y1 ~6 J7 _
0 w0 Z+ L: Y: \1 M* V

2 _( k8 y, B1 ]8 ?0 y我们再来看看经过STM32F4芯片进行FFT运算后的实际结果。计算后各频点的模值存放在数组fft_output[256]里,我们可以进行查看、比较。不难理解,如果点数越多,数组数据量也就越多,看起来并不太直观方便。我这里将FFT输出结果按照频点顺序在调试界面周期性地显示出来,如下图所示。其中,红色波形是信号频谱及模值,绿色斜线是频点递增变化曲线。
4 H8 E& s% O! \2 E5 m' x4 }9 y: z+ H8 V; C$ q) n) o
! [* R* a( w* }4 B% I, z3 H# t- ]
10.png
1 _% g5 S8 P, w9 q9 A3 U% l. v) ^" }5 Z$ X6 ?0 w! x

' _8 E, G" K; P9 e- w6 C1 _同样,我们只需查看FFT完整输出的前半部分结果。6 g8 `& r" N3 _8 @$ D. m
% T# ]2 o# X: O* e  P# M# a$ H

, H2 q- L2 ?2 d& l% m 11.png
8 _8 e& u+ H# x( G/ I3 g6 Z
; ?& @/ e( D/ a- H
: l* C7 O* C: R* V0 y! j5 ?# D
这里的零点处所对应的模值就是直流分量的模值,理论值应该为3*N,即3*256=768,实际计算结果也是768;其它5根线分别对应200Hz、400Hz、600Hz、800Hz、1000Hz这几个频点信号的模值。在显示界面轻点鼠标即可看到各处的频点位置及对应的模值。
1 d/ t/ j, p# b3 E
" s4 m; a/ {- `6 ~: Q

5 i) y( ]  u  `; B  I# G; _* H当然,我们也可以到经FFT运算后的输出数组里去查看数据,相比之下,只是没有这么直观快捷。【下图是部分数据截图】
! m7 x3 t5 ~7 @
; k. K* J9 a1 {# p' h  C( e
& F" e" R! K8 c$ b1 @, O$ G8 Q
12.png
/ T- ]" E6 D' L$ n0 Z* `* x, V) r& F
1 s# r: P  C7 \  r; W& ?. M+ ?& ?
另外,我们还可以看看各个频点信号的初始相位值。同样,我们也可以先用Octave模拟运算结果。基于数学工具的运算结果如下:
. E' e3 ~$ J! z! V
6 ]+ t- N  c2 d- h; h% m" u* d
1 m" j0 _" {$ m) R
13.png . v3 O- V$ G9 k7 r/ }
5 H* H( r0 Z. Y4 p, `2 T

. @; n% e; X0 }% C" ~) a8 u我们再看看基于STM32F4芯片的FFT计算结果:
* N" i; ~: q$ p5 U2 `5 M1 w4 u
" c. T2 V1 p- J& P( [

9 \* O( \- @7 C6 m. ]) E+ v& N 14.png
: x8 h! C& \- u) a# X% Y4 m( D5 f( f& F# |' B  c' g/ V* g( c
  ~% ]* {  }, q. F/ O8 C" x" q! F7 q7 W
这里提醒下,初始相位的计算结果是按照余弦函数计算的,所以如果使用正弦函数产生的信号的话,就涉及到一个三角函数的转换关系sinx=cos(pi/2-x),经过转换后所得的余弦函数的初始相位值跟计算结果就一致了。3 ]" {9 m8 B" s' w" m$ M

8 m2 M6 r' j1 L0 f6 r

9 S6 V, p6 g7 N$ Z" e; X整体上,基于上面的参数所得FFT计算结果,跟理论值还是非常一致的。不过,要提醒的是,即使对于同一个被采样信号,因选取了不同的采样频率和采样点数做频谱分析时,可能会出现不同程度的误差。所以,为了减小误差,我们要选择合适的采样频率与采样点数,更多细节可以查看相关资料。2 q* c3 ^1 X% y
! y" q6 R2 _+ ?# P, x
赞 收藏 2 评论2 发布时间:2019-12-19 13:28

举报

2个回答
慎微 回答时间:2019-12-19 17:50:21
感恩,收藏
Glenxu 回答时间:2021-8-21 23:05:57
很专业,感谢分享!  J& J5 @3 r( U  g0 I

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版