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

STM32的FFT运算实例

[复制链接]
yumeii 发布时间:2019-12-19 13:28
ST公司的STM32F411discovery开发板,就如何实现浮点FFT运算做个简单应用示例,并给些应用提醒。下面用到的IDE是ARM MDK。4 \0 C3 A, i# f* {6 R5 |
* j* \1 l# ]5 G5 m4 W! T; p

5 L9 ^6 _! }- ?2 P9 N. V( N2 D' X因为要用到ARM公司提供的DSP库,所以需先开启一些必要的宏开关并添加相关库文件。STM32F4芯片支持DSP指令且自带浮点运算单元即FPU。
1 p& X" X3 c7 F1 C. p, Y- }; ?2 D0 p

- K4 ]6 n% ?9 Y& b! N在MDK集成开发环境的相关配置栏添加__CC_ARM,__TARGET_FPU_VFP,ARM_MATH_CM4,并启用了硬件FPU。
2 b: t+ ~0 V/ K) V/ C
/ P- i; O  _* J/ V
  J7 e0 {' [- y- C- I* r
1.png + O/ o7 q; I/ h! E* I2 n9 X7 q
5 Y( L; T2 Z2 H/ F
% s2 ]2 c0 @) Z- F9 N* V
添加ARM提供的DSP库文件。如果使用STM32Cube库的话,该库文件一般在这个目录下【以STM32F4cube库为例】,有4个库文件。  ^0 e+ d  r& Q: D0 U: J" F

8 K8 [6 L  ]. `- I; Q& v% n
4 l! s& {1 R2 n6 m7 O: d! f
2.png 3 ]5 n. J( i/ J4 _* U

7 z( _' N/ F, @- w6 }5 D  D

2 w" I. }! q5 @. P3 D7 O* r6 o* I其中cortexM4b是基于大端存储模式但不支持硬件FPU时所用的DSP库文件,cortexM4l是基于小端存储模式但不支持硬件FPU时所用的DSP库文件。而cortexM4bf是基于大端存储模式且支持硬件FPU的芯片所用到的库文件,显然,cortexM4lf是基于小端模式且支持硬件FPU的芯片所用到的库文件。
3 P/ g# \2 X& a. j" e& _, g8 ^* \
4 R5 g7 n+ Y4 i0 p6 ~$ F5 D
' m# `- r( t/ L9 R
这里我们添加arm_cortexM4lf_math.lib即可,STM32芯片采用小端存储模式,且STM32F4内置FPU硬件单元。
8 b* `( W7 W6 `. Q% G1 J8 V" t$ x( N7 q) O
! A. o, P2 J* q& q6 e1 l% U
这里提醒下,首先,DSP库不要选择错了。其次,在添加.lib库文件时,添加时注意选择文件类型为lib。如果以别的文件类型强行添加进去后,编译可能提示出错。有时一会半会还找不到出错原因。8 Q3 L; u* T8 q* e% w3 ?
5 B* y1 P& A: k: [! X

0 ]2 V# `. c, E6 ?0 d0 G; ^ 3.png
" P% l+ n0 B; w) r9 `1 y2 a: F# J* {1 y9 j$ \
) o, P- V+ j+ U( w
另外,我们还需在用户代码里添加包含文件:#include"arm_math.h"。至于其它头文件按需添加即可,比方math.h头文件。
" K% ^; O2 L$ I8 U  K' ^6 u
, V5 V3 t$ m- F0 D) Y6 D
6 m' _& m4 Q( R: W) `% C
下面开始看看具体的代码实现。, w% @- W: H8 ^( v3 C2 i7 r

1 f! P) |! G5 [

4 V% _7 X$ b9 C5 T" B# u先做基本变量的准备及定义,比如用于FFT运算的点数,用于FFT运算的输入数据数组,输出数据数组、存放各频点初始相位的数据数组等。: E: }2 a$ m& W
8 L1 |" W4 z' g- f3 M" }
6 n% ]0 I5 `& i  A3 Q; R: F& h. f
4.png
1 \9 H1 o' r: Y& ~. ]3 [* W% F4 y1 J
  M8 C$ ~" c" I# p4 v& ^
4 K- v  x* U$ v& |( @2 z( k6 e6 b
这里使用几个正、余弦信号函数叠加产生一组信号序列。其中,有一个幅度为3的直流分量。另外几个分别是频率为200Hz、400Hz、600Hz、800Hz、1000H,幅度值分别是1、2、4、8、16的正、余弦信号函数。各输入数据项的虚部均设置为0。
7 x* L( |) I' I$ L8 x  B
6 m# B6 m, v2 I2 ?1 E* N8 ~9 J

3 [2 t5 A, w5 ~( \$ \ 5.png
5 H6 n* M4 g1 J2 h8 s0 @. [5 n4 P# b) s7 g7 ~
) _' @/ _5 ~: K1 F  \5 `7 _
这里假设采样频率Fs=5120Hz,期望的最小频率分辨率为20Hz,则最小采样点数不要少于5120/20,即这里选择256点。【如果希望频率分辨率更高,可以加多采样点数】。
9 [9 J0 {& m" J  s  Z8 @- J- T% c* f1 T) V& D

+ z+ Q+ m# t3 G) r% D7 v8 ^- K其中,采样频率不得低于最高被采样信号频率的2倍。关于这个采样频率及采样点数的选择给几点提醒供参考:
7 f$ f5 d: a# \2 U. n0 N+ I0 {" _- ^# E% `) ~3 g3 S: e7 [# o" U- \
7 s% u# q* C% I0 T5 N
1、采样点数它往往有个最小值需求,由采样频率和所期望的最小频率分辨率决定。( b8 o' o( F  d" M! @) o/ H
8 u: E  i( e1 Y8 h

& D. q$ b( k  @! v* `: o2、整体上讲,增加采样点数有利于提高频率分辨率或改善运算结果。不过,增大样点数时既要考虑它对计算速度影响,同时还得考虑对微处理器内存的开销。比方128点和1024点的内存开销差别就较大了。
: w' j$ M0 N# n: r9 w: d  M) v; H! z2 L3 X6 B) D' x4 u* g, g' |- n

- ^2 C6 b# ?1 g+ U( \8 Y3、在选择采样点数时,除了考虑满足最小值要求外,还得结合具体选用的FFT相关函数类型,比方说是基4还是基2的。如果使用基4的FFT运算函数,则样点数须满足4的整数幂。如果使用基2的FFT运算函数,则样点数须满足2的整数幂。
, ?0 g9 e" _  {) w& z' R' O
7 e$ S* u" Q( u6 v
2 b) Q- [9 g" {& |: b
4、具体到使用ARM的DSP库函数进行FFT运算时,还得遵循函数本身所支持的样点数约定,并不是你想多少就多少。比如,以下面函数为例:
9 c, d! g/ s* U) ]; k! s8 i4 K
9 A. v+ l: p6 D2 o+ L4 a4 s

/ v" c0 E- y) ^% o" J 6.png
9 W6 I. w0 a% {% h& n
4 s4 K) I% K% A, V- Z

8 l. v5 i. }+ b* i基于上面FFT函数,如果你使用128点、512点、甚至高于1024点都不支持,或者说计算结果误差很大。【当你点数选择不当时,编译时目前是没有任何提示的。】
2 X& n1 d; X, f. N9 M# ]8 ~& K1 q- ?% U+ t1 V3 C3 q( |7 B

4 z& q. l1 G* Z7 S6 @" P5、有些场合为了满足FFT运算要求,计算所需样点数可能多于实际数据量,这时我们在数据序列尾部补零凑齐。比方经过评估希望512点,结果实际数据才500个点,我们可以考虑补零以便于计算。这样做可以减小栅栏效应,但不会提高实际频率分辨率。
" n+ e, i. J& U- f' n4 W
5 m; d% ~/ t9 `" E9 \7 I

' w- _- r( V. a3 H4 }' z6、采样频率要满足不低于最高被采样信号频率的2倍的要求,但并非单纯的越高越好。一般取其3~6倍,具体应用时合理选择。
5 n* T: h4 M! E; K
; k6 y# K+ m' j; l( w
" v5 A- _# A. d: E; Y1 L
关于采样点数的拟定就提醒这些,尤其上面的第3点、第4点,在使用相关函数时,最好看看函数原型说明。【注:我们在PC端使用诸如Matlab、Octave等数学工具时样点数往往可以做到比较随意、自由。】
9 B2 G7 m; I0 z1 ?' B9 K" S4 k7 a5 c

  W) ^, J- o; E5 [) A9 k好,基本数据准备工作做完了,下面就是调用几个函数来完成FFT运算工作。
9 D: u/ L/ c3 d0 q$ K( k& [$ S9 v( |; B. Q. U1 D

9 \, o1 }! E( Y5 F' J# X 7.png 6 e3 x* i! A+ ^$ ?
8 W: j3 C. r7 z
7 b0 _. O3 h1 j
运行相关代码后,我们可以看到不同频点所对应的模值。2 l+ o9 ~' \; g- v
3 x& z. `$ b$ Y7 g1 m! u* e
4 G  V2 ]& R/ v" L  ]' K; q! s
对于直流信号,其对应的频点位为0,计算出来的模值是其实际幅度的N倍,【N即FFT运算用到的样点数,这里就是256】,其它非直流频点的模值经FFT计算后的结果为实际幅度值的N/2倍。8 _. ]0 }2 @8 n/ O$ B$ b. ]2 D4 o
9 ^+ e- w7 A( f$ x5 p* k% B' h
9 X, @. o( ~, f5 l* p  \" A- Z1 B
我们可以先使用Matlab或Octave模拟一下输出结果。下面是用Octave模拟出来的结果。9 V9 b7 M% o' v
) R3 X1 _* |' [$ P2 S
* X2 f: f, _  |9 r
8.png
" V6 t- a% t6 }2 v5 }* E
$ w; P9 F* S* e- L2 B
2 M2 A6 P* Q/ h  n# J
因为经过FFT运算后的输出结果由对称的两部分组成,我们只需查看前面半部分即可。- r; L; F( R. K/ d% q

: {/ j" t8 B4 t( u6 Y) A" X

2 y, _( G# ~6 V& b( n  c如下图所示:
1 ^+ e5 l5 F; b( H9 _- h3 Q. n6 z# N; i) o& u3 }' _) v) p
. z' ^& M' s9 T# _& H5 D
9.png
1 i0 u: [5 D) h; V8 |6 m1 X7 C7 \: r* A, {2 F! u

' E- [4 P6 C6 N8 @6 \* H因为采样频率是5120,采样点数为256,即每两个采样点的频率间隔为20Hz.我们不难从上图看出直流分量0Hz以及200Hz、400Hz、600Hz、800Hz、1000Hz几个频点信号的位置及模值。图中0点位处所对应的模值就是直流分量的模值。因为各点位的频率间隔为20Hz,所以图中点位50处所对应的模值就是原信号中1000Hz余弦信号的模值。【前面提过了,FFT计算所得模值跟各频点信号实际幅度值还有个对应关系】+ t# y8 j# }0 |3 q; _
6 V8 a# B9 n' Y, E) D# p, H+ f
' p5 d1 d7 q& @8 x3 p% |
我们再来看看经过STM32F4芯片进行FFT运算后的实际结果。计算后各频点的模值存放在数组fft_output[256]里,我们可以进行查看、比较。不难理解,如果点数越多,数组数据量也就越多,看起来并不太直观方便。我这里将FFT输出结果按照频点顺序在调试界面周期性地显示出来,如下图所示。其中,红色波形是信号频谱及模值,绿色斜线是频点递增变化曲线。$ j* x0 q( C% [. V
* E- e3 K+ ]5 C
' F; v# }4 Q+ s1 v# K3 F
10.png
7 z/ E' o: u2 o/ \
; i; |% p: I% n) H* Y2 r' c

( H6 ?% I0 x( v3 L同样,我们只需查看FFT完整输出的前半部分结果。6 U! T. z  K  \3 \2 C. J
) j' w4 _. v2 H
5 B, ~/ X" E4 b; q5 j9 _  R, r
11.png 9 w3 U8 b) b5 a! K

' G9 y+ y- t# f1 y2 r
/ p1 |& }8 w% d) w
这里的零点处所对应的模值就是直流分量的模值,理论值应该为3*N,即3*256=768,实际计算结果也是768;其它5根线分别对应200Hz、400Hz、600Hz、800Hz、1000Hz这几个频点信号的模值。在显示界面轻点鼠标即可看到各处的频点位置及对应的模值。
" B- ?, ^$ i5 V# L1 I1 r" a0 k  {0 I" `  S( D: w. Q
9 N) }+ T( @$ l; E- I
当然,我们也可以到经FFT运算后的输出数组里去查看数据,相比之下,只是没有这么直观快捷。【下图是部分数据截图】
2 R. l! _% W# D/ f8 |; D2 }
9 ]7 Z5 Q% j' g8 p. N

  s) \9 g: x4 o# k 12.png
9 V* R/ y& T1 b& h6 ~& D2 i. {; P
( k+ u4 O2 j6 X4 C( c
2 D$ [& s& Z/ L
另外,我们还可以看看各个频点信号的初始相位值。同样,我们也可以先用Octave模拟运算结果。基于数学工具的运算结果如下:
$ p, r( @5 y  ?' L' |) ?8 t# |  K$ D/ b2 j2 Y, {9 e
) M6 m7 V; v6 i' x  S( x
13.png
& v$ U& _5 q" |6 B5 K
; \& }: B3 a5 ]% n8 ]+ ?

: G& [0 `) Y0 n$ @  B我们再看看基于STM32F4芯片的FFT计算结果:7 F0 m$ j- [8 c5 d! ]& g& i* j; h, Q
/ ]; M( S4 T8 f1 T0 d

; ?# P1 ?: |7 C5 M  g" h$ q 14.png
7 ?2 \( H, J: F$ B- f5 S' \" `5 i+ s- v  m' e' q

: k! a$ }8 ~' f这里提醒下,初始相位的计算结果是按照余弦函数计算的,所以如果使用正弦函数产生的信号的话,就涉及到一个三角函数的转换关系sinx=cos(pi/2-x),经过转换后所得的余弦函数的初始相位值跟计算结果就一致了。
: [* a$ r2 J: \* Y2 A4 K
( ]$ J, e1 s% D( Z2 C
4 O6 B+ J* H% `0 m+ |: p" k: t
整体上,基于上面的参数所得FFT计算结果,跟理论值还是非常一致的。不过,要提醒的是,即使对于同一个被采样信号,因选取了不同的采样频率和采样点数做频谱分析时,可能会出现不同程度的误差。所以,为了减小误差,我们要选择合适的采样频率与采样点数,更多细节可以查看相关资料。
1 U: R% C. t1 B  n7 o' l
$ z( Q1 L; @2 H& m3 ?2 l( |) X
收藏 2 评论2 发布时间:2019-12-19 13:28

举报

2个回答
慎微 回答时间:2019-12-19 17:50:21
感恩,收藏
Glenxu 回答时间:2021-8-21 23:05:57
很专业,感谢分享!
5 g2 ^! u5 S% S+ |; w

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版