之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下; L) s% X! M& t7 P# B
STM32F767IGT6
; j$ w d" c$ R) {5 |Keil5.21
$ W& x1 _- z2 A! T- ~" GCube MX4.24) n- V5 c, B; u- {* f% ?4 A* p
arm_cortexM7lfdp_math.lib
; i) a4 D8 T. O2 O% Y0 ]正点原子阿波罗开发板# N, {3 Q9 z- e) w* P5 D
$ C2 _( X l( v+ H
准备DSP库( u7 |' ?- P3 f* H
打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,& R1 V$ B* r5 s; T$ O% Q
; F/ x9 n8 X* x
6 @6 k) H5 v( {0 s1 _
! @6 \, p2 s: q7 W- d$ H- XDrivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。% R' Y( E/ H7 D6 \& R" C* V
Examples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):
: X& i5 z9 }- _& k; E$ h4 z, t- `: D) l
8 Y; i" |( r u V3 A7 M; k2 L' |4 P6 N& A) X3 C
Source 中的文件如下(这些是 DSP库的源文件):
: ] g8 M, u4 B8 n, q1 j* k" N5 K( u' W, d! _1 B
/ u/ i: \5 @; [( n9 w
& Y4 b/ V- l: v( m& Z, r) H& r源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。) R" u( ^3 _% W% ^- ~" C3 e
/ H/ X4 j* [! I% A$ y5 |# i+ S) G7 f5 S
\9 ^& B W& H9 @/ y5 Z5 ]" Q
" p5 X2 e9 h. o8 Y' u/ b0 |$ r+ n4 q
库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。. w& f- `1 N: @/ P" b+ E
. n, i, X( y- H
6 D* u' k+ M! ?# w; Q; W5 x) q1 ?$ q( G2 U q X, W; e* S
确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。
B, l, l8 _1 ^: w6 e/ S' X) l0 Z! L, b
7 a& j, p3 [# m- B$ ?( p
0 }% H# ^/ P4 {, _下图为DSP库已添加到工程中。' h' t- M% U0 e3 C
, P# Z$ t4 z$ w0 q' j6 g& Z0 X
$ U5 u4 _6 t, X$ G$ \8 r: [$ a& q e. y: o# w& f' Y6 {5 v6 @8 \
库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。
9 g7 D: b( f$ c1 ]
6 f! ?( ?. D' q5 \/ B0 W9 b
0 x Z/ U( ^9 |. }! ?" V
( y1 `1 N$ A' h* O函数说明
" ^: l5 |8 N4 }+ _我用到的是实数FFT,即rfft。相关函数参见官方说明。
4 ]% X8 l$ m$ ?! H# Y函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
0 X7 B4 o2 f4 u6 ~& qarm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。
5 M* r) o3 X6 U1 B- G+ r. p5 [
2 W. @9 F2 c- L6 P: V; U
+ D/ {" v( D) J5 L. r
$ u: S* K2 M) i1 y2 yS是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。
, V3 p: E; a! e9 B2 u8 D+ l另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。
7 `8 B8 Z' D. m
0 S6 ]" p6 y9 I$ K, Z2 E2 Z; i# D! \
7 r. e: K. B2 k/ E" C
官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
8 d! j' M9 p, B除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。 R: P' \6 c6 V, w( ~. p1 v
2 u0 P1 k+ `% s4 A% s/ _- v( H3 J9 ], U2 L5 p
( [+ E: ?' S7 d: g$ K' i9 }3 e4 C另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
. U- f0 C) h) A$ U( g; H2 v
7 n( X7 z; x+ \) y+ L7 S/ i/ _, ?+ ]/ E: {( n; O
2 C9 ~* o0 i$ }' ^0 f" E
代码示例
+ Q% F2 ^4 Z7 d4 c0 ] B }4 G- include "DSP.h"8 A2 h: b0 c% B0 G' z' }* h: a4 c v
- include "arm_math.h"
0 i( [$ D- e) p) N; d - define NPT 1024 //1024点FFT
" {6 L. L7 u/ y" b5 i& U7 D x - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz. I* d8 x2 Z) r, b1 h
- define PI2 6.28318530717959
- {( w$ \6 I% E& Y5 f% z - float32_t testInput_f32[NPT];$ W- V& L8 U, s
- float32_t testOutput_f32[NPT];6 A) U2 j+ @; D# I3 l, b
- float32_t testOutput[NPT];- z& E, W: Z2 n$ F
5 d2 w* l3 i4 G% c$ G- /* 2 U2 w7 }: T. n3 h1 J! w
- ********************************************************************************************************* 1 j' Z1 u2 ?7 @2 \- D" x) y, d( @6 N' m
- * 函 数 名: arm_rfft_fast_f32_app
h: z5 T J! y ` - * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比 8 p9 l! I; S0 q* y$ a, _
- * 形 参:无 n- L+ z) t) F( h
- * 返 回 值: 无
/ h! V; Y5 B8 Q - *********************************************************************************************************
1 F+ J) l' f7 M - */
' W4 \; m% L% @7 [% A/ H- q. ? - void arm_rfft_fast_f32_app(void) $ \2 S' J4 k$ M X( X- Z1 \
- { 2 g5 H/ ?5 q. |( D _0 J
- uint16_t i;
3 R* S; L. P4 Z7 W+ v - arm_rfft_fast_instance_f32 S;
( G) Y$ R }& E, d/ ^ {3 C - 1 ^ ^6 L; L8 G4 [+ h$ m: ~
- /* 实数序列FFT长度 */ : E. _* F. Z$ ?: _2 }4 A& T; s
- uint16_t fftSize = NPT; - e4 E% A% v3 j. p
- /* 正变换 */ 4 s7 C* S" L/ J5 [! G$ l( v: L+ ]
- uint8_t ifftFlag = 0; + B% I; A) Q' d; W# T" Y
- ( w4 o" l& I( K, U$ S) O
- /* 初始化结构体S中的参数 */
" W5 k, Y4 c8 G1 q' l0 X* U - arm_rfft_fast_init_f32(&S, fftSize);
( ?3 K/ D5 U2 W# d! c% [
" l% p- P7 a, s7 u$ `5 w- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
3 l' K! @& n9 v& x - for(i=0; i<1024; i++)
4 c3 u& p P2 o G& H8 |; q - {
# C% R$ k+ Z z1 ~+ W! c3 e - /*3种频率 50Hz 2500Hz 2550Hz */
( ^; X. ]6 ], { - testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) +
$ o2 y/ Z9 j4 ]* {) X* h9 K( s s - </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) +
2 J4 N% N7 [3 K; v$ A- Q4 ~$ q - 3000*arm_sin_f32(PI2*i*2550.0/Fs);
g2 t/ v: b" }& \7 M& q6 J - } 4 [, S: y& Z3 Y$ m5 n- \/ e
- 8 z1 H8 V$ Q# r* l1 B
- /* 1024点实序列快速FFT */
0 R1 ?3 P# Y2 a - arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag);
- P) y) ? ?' c$ I+ B - q: o4 y1 {% Q1 f
- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32 & y: d) i; }1 m, n( J
- 只求解出了512组
. F6 [9 r9 G7 U0 @ - */
( B! j1 v" L8 `2 ~ - arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize);
; q4 ]' r& o4 A8 c* K - / _% R" I# t9 ~" L% {5 X
- /* 串口打印求解的模值 */
5 O6 f I& C( _( S" {8 W! Q: C% V - for(i=0; i<fftSize/2; i++) : D8 u. d0 ]7 p! m; W
- {
% q# b: \4 W* F - printf("%f\r\n", testOutput);
) [: r- x( `6 _- [! ~! a) x1 G - }
( {: M0 Z& }9 z7 z, T5 @ - }
复制代码 . w' u( M# P6 k
结果
" A% k4 }3 n w3 v3 o+ _& k在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。
3 R# u5 C* h) y7 K- P- s/ H, c! Q
0 F& g' t0 n# b# B; u6 M$ Y: {$ V
3 D* ~' Y, S; |6 k+ K! ^3 N9 }" ?8 l( H
1 D `/ R3 E! f3 `$ q0 d% P" g! Y5 h
( K' }. p% p: [$ K6 S
) r d5 v! k$ I+ I对数据plot画图,结果如下
) p- s& K& F% ~" {
: @) \7 Z$ I" I$ V. o9 |& }# S. Q. |; u0 \3 Y [: K% n
. N ?; U+ z8 f0 I可以看出,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);
, x; t& t+ t( F7 X8 T9 A7 x
: a3 `; F. `6 L4 P# R& F' _
x$ L7 }, R3 `% E* Q- n" n" o
7 s! y' L# e% r5 Y6 n3 F' L$ k |