之前做课设的时候接触过FFT,看了一些资料,没整明白,现在做项目又用到FFT了,花了点时间整明白了。但此处的明白并非把FFT的原理啥的整明白了,只是明白在STM32上怎么用了。开发环境如下
2 }* Q# d' X* p* E6 r, eSTM32F767IGT64 c& @% |) o* z) I! m( ? \
Keil5.21
0 i' p+ s& \0 ?: W/ X2 r' PCube MX4.243 K7 \4 F) m$ U4 j& w2 B( ^& t
arm_cortexM7lfdp_math.lib, R0 w# h8 N1 l3 o, G
正点原子阿波罗开发板$ ]% F9 N) M+ x* V, E. `- Q
4 D* X" |" D- U5 y% p
准备DSP库
, s/ N) X% Q" H. m& @" s& D% N# J打开下载的固件库,文件名为STM32Cube_FW_F7_V1.9.0,
' D' n" d6 L; G$ ?8 L, V. F3 h6 L: i; y9 Y6 J4 K
0 J+ v: M2 S. D% }: x; X
0 @2 h; v( {6 W- d' _0 HDrivers文件夹下,CMISIS文件夹中包含上图所示文件夹,DSP_Lib和Lib是DSP库相关文件,其中DSP_Lib又包含两个文件夹,Examples和Source。" T6 @4 \! R$ x5 t
Examples 中的文件如下(这些是 ARM 官方提供的 DSP 实例):
. ~( f$ l5 {7 I' B" L2 z$ _0 u) e# O+ ?; L+ u
* t) a6 v8 Q8 c, b* D
0 A' x7 n) }" | U* w/ s }Source 中的文件如下(这些是 DSP库的源文件):5 F, i. h* M! t9 W" l3 u! e- b) S
; @! y& M' C3 w- g5 C
- g: F3 X. C0 y; C
" N; Q! y5 m( } {源文件不需要添加到工程中,真正需要添加到工程中的是官方提供的DSP库,文件格式为.lib。库文件位于Lib文件夹中,有ARM和GCC两种开发环境的库,我们选择ARM。如图所示。6 W0 d+ S: U# J1 B
3 M6 M. b% `! H9 v) P( ]
7 p; s3 l7 V. ?0 `! ~1 A
7 S( G/ ~# n3 \+ H7 v库文件有好多种,关于每种库文件的说明参见官方说明。 CMSIS DSP Software Library。该网站上有详细说明。如下图所示。, z% m* `1 p; Y, W A
& B& P' d: l ^, q2 ^) b
( @4 d8 F% v+ @3 S
8 A: m+ ~" x6 o- j! i; i8 h/ C确定了用哪个库后,添加到工程中。我的工程是用CubeMX建的,因此添加DSP库的时候在软件中操作,勾选后自动添加,如图。3 X7 S+ q# R+ M: y; g( U5 [
+ ] K" n9 ] S, c# ~: c/ Z$ B( f* [3 r9 g1 w* j0 `9 N
9 z' c8 Q8 ?4 u7 G) E下图为DSP库已添加到工程中。& i" R) a$ Y$ M& d
9 J: Y+ V& y E; M$ v' x
0 H, ~* ^3 w; [# H0 Y1 T% T& c9 G
9 `( \4 R P ^库添加到工程中,在需要用到库函数的文件中引用头文件,#include “arm_math.h”,即可调用所需函数。
! S$ ]; |9 Y) Q/ s
" f0 [; y; R* g& x% j. f; l2 o' A! y/ d: }% H8 G& \
4 x: q, X6 E. @. [+ ]函数说明
& n% b' `9 }' S/ q% e我用到的是实数FFT,即rfft。相关函数参见官方说明。
2 Y2 l* v& V* d* p2 q0 R函数名中f32代表32位浮点数,q31代表32位定点数,q15代表16位定点数,q7代表8位定点数。
) D7 I/ `0 ?) U' `% |( _arm_rfft_fast_f32是FFT实现的主要函数,函数原型如下。
: t9 d' u% | R3 ^' S6 q( w) _, E( y$ U
) n) W. O9 }- i7 j# @' \: T
4 \+ y% u& k! w' F' K) n( WS是 arm_rfft_instance_f32 类型的结构体,p和pOut是输入和输出的缓冲区,ifftFlag是变换的标志位。
: i) \( ^5 \+ R& P/ G另外还有一个实现rfft的函数 arm_rfft_instance_f32,函数原型如下。
' F. Z1 z6 B' u0 L& P
' U% T) b$ ~. Y) {8 {7 k/ r
: ~8 k/ N# P. C. z$ L- L( ?, _) K& w5 q' Q
官方不推荐使用此函数。“Do not use this function. It has been superceded by arm_rfft_fast_f32 and will be removed in the future. ”
5 |1 N0 q) k% A3 R0 n6 `9 g除了FFT函数之外,还要用到一个函数对arm_rfft_fast_f32中的参数S进行初始化,该函数为 arm_rfft_fast_init_f32,用于 初始化结构体S中的参数 ,函数原型如下。% D+ f2 ]; \. w7 M. G, n8 L0 d% ?
1 W$ K8 M6 r, n* H0 U* |4 n" {" `" ^8 p O1 i& \! ~# {7 U
4 v9 y& L; ~- v0 L% V5 z7 Z) p3 [另外一个重要的函数是arm_cmplx_mag_f32,计算频率的幅值。函数原型如下。
; S' g2 u: c' |) O) r0 J
1 {6 Q+ E9 E- Z$ Y/ s( ]
, {# D7 w8 M2 \. D9 C$ {
2 D- r- r2 }8 T5 [7 [+ D代码示例 M9 |% f+ v0 v; g3 a; V" y( J
- include "DSP.h"
9 n: z, f% l4 | - include "arm_math.h"
' R) L+ i2 W4 n, N" S - define NPT 1024 //1024点FFT
" \; `$ ?! q0 k- c# R4 [! ~% ` - define Fs 5120 //采样频率 5120Hz 频率分辨率 5Hz+ C1 L/ h/ K8 z( p& ^
- define PI2 6.28318530717959: t; D* s! @- Z9 j2 D3 \6 I
- float32_t testInput_f32[NPT];; L2 L9 y E8 Z7 Z1 H1 j
- float32_t testOutput_f32[NPT];, L0 x: m2 Q. c
- float32_t testOutput[NPT];# t: n- h& M. u% }, `" U
- ( j* l+ l: } v2 Y8 I, C4 B. R
- /* 4 n8 i3 S3 ~8 K+ H5 C) c
- ********************************************************************************************************* " a& ?2 n3 {% U. _+ U# B
- * 函 数 名: arm_rfft_fast_f32_app
' F+ c+ f7 H" N9 n# d - * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比
4 T, L: }$ R+ Z& G1 W: {( V - * 形 参:无 ; c. m' `- g) N2 @: D
- * 返 回 值: 无 ! Y6 p { i1 M6 E* d. P( H1 V4 @& @
- *********************************************************************************************************
1 i3 [+ Q; S) C! y } - */ : b% ?4 P) n1 C c4 s
- void arm_rfft_fast_f32_app(void) : a) e% V. l) B( S- X h
- {
; O- w) `4 e, B- q; d. w+ Q - uint16_t i; 0 E( e1 O3 K9 c/ z& @% ~6 O9 [' r
- arm_rfft_fast_instance_f32 S;
1 B; X, ]2 n' ~; I6 K0 E6 |
! [2 Z" O3 j6 Y; C4 f5 e- /* 实数序列FFT长度 */
* Z' M- c5 j' S) M, y - uint16_t fftSize = NPT;
; ^ e$ P) r. O4 Q - /* 正变换 */
# r, i) ]! m( e6 P - uint8_t ifftFlag = 0;
; M& T# x0 n1 T% F4 B - 6 ~( x. ~9 v3 Q' O
- /* 初始化结构体S中的参数 */
. `/ a# k* k* H* K0 C3 F' y - arm_rfft_fast_init_f32(&S, fftSize); / |1 l5 k1 l. L1 a' z+ \4 A% n5 b
& V( q) q0 B& ]; |# j/ h- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
1 o; ` ~& ^$ ^* l; o - for(i=0; i<1024; i++) $ X! T' e* [9 B
- { 8 p J4 t: ] x9 O
- /*3种频率 50Hz 2500Hz 2550Hz */
2 U* }9 P8 o# I# u4 I7 M* [. k - testInput_f32<i> = 1000*arm_sin_f32(PI2*i*50.0/Fs) +
# ^; e5 \' y5 r2 N1 | - </i>2000*arm_sin_f32(PI2*i*2500.0/Fs) +
, u/ v5 g- l. C Q) A* B - 3000*arm_sin_f32(PI2*i*2550.0/Fs); : e* t% s: G2 Y1 F7 k; `
- }
. J$ I4 M' O+ V# c: {' ]+ ?
, k" N1 g8 V3 F, [8 d. e- /* 1024点实序列快速FFT */ 2 f& B i( Y. q8 X
- arm_rfft_fast_f32(&S, testInput_f32, testOutput_f32, ifftFlag); M2 B' |: I4 |; s, ^& o m% Q
- 8 t. O5 B0 K! b' s, X
- /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32
( ]/ I/ O; L. ]5 q& R$ ` - 只求解出了512组 ( j1 j$ s+ W; j3 A/ e' b& Z' U
- */ # w# k9 D' N% O1 g' a! n
- arm_cmplx_mag_f32(testOutput_f32, testOutput, fftSize); 8 r/ c, p, B m
- 3 X7 W% D0 }" B! M. K
- /* 串口打印求解的模值 */ 7 O8 e" p e7 |9 q. c
- for(i=0; i<fftSize/2; i++) / F2 }2 @5 L* c* g# A
- { $ }" J Q9 b" s0 P0 R7 {# d
- printf("%f\r\n", testOutput); 2 l: S8 [9 {# z* r
- }
$ x- H5 i; _9 q4 ?0 p, f! e: r* H - }
复制代码 0 k" m; H6 M) U7 G* U5 u3 d9 w6 G4 z, L
结果
0 i9 Y! i J8 L2 a; ^8 m在单片机上运行,将串口助手接收到的数据保存到TXT文件,利用matlab进行分析。9 C' B/ y; G7 a6 `% N
/ _- ^5 ]6 y" Y- s
$ A2 d0 W* I# n' n" I
& @+ H& D6 Z3 M: F: ? V% J* e( v8 w+ f W+ X+ E' p: I
3 m7 V2 l4 W7 P2 e: k
) J; u0 Q2 R& V
6 R& z( z5 i' `# I# ]* ?: y8 B% f& J8 B% O8 K2 a
对数据plot画图,结果如下+ o. P4 r# Y, h- s g: v- C
2 ^" p+ m8 P/ Y/ i
0 q, [ @! w. S- l9 y) c
9 c' T; Q! h8 ]; h( z- d5 g1 i" O% L可以看出,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);
+ u/ Z0 ]* ~4 ^; @, c4 L: Y! S+ q6 d& q- E9 R' M
; b6 e7 R2 _3 H& O; U
6 E; T6 V( [6 R( _2 } |