特别说明:完整45期数字信号处理教程,原创高性能示波器代码全开源地址:链接
7 J3 x( y/ c8 ?; e! j第32章 实数FFT的实现 1 Z" O1 N$ {. ]1 u, j
本章主要讲解实数的浮点和定点Q31,Q15的实现。关于这部分的知识点和函数的计算结果上,官方的文档有一些小错误,在章节中会跟大家详细讲述,还有一个要注意的问题,调用实数FFT函数一定要使用CMSIS-DSP V1.4.4及其以上版本,以前的版本有bug。 本章节使用的复数FFT函数来自ARM官方库的TransformFunctions部分 32.1 复数FFT 32.2 复数FFT-基2算法 32.3 复数FFT-基4算法 32.4 总结
1 V1 E3 J Y- K9 a; ]
3 P' I' e8 l) I3 T- g32.1 实数FFT7 v( L2 v3 }) Z$ c+ B5 ]
32.1.1 描述 CMSIS DSP库里面包含一个专门用于计算实数序列的FFT库,很多情况下,用户只需要计算实数序列即可。计算同样点数FFT的实数序列要比计算同样点数的虚数序列有速度上的优势。 快速的rfft算法是基于混合基cfft算法实现的。 一个N点的实数序列FFT正变换采用下面的步骤实现: 由上面的框图可以看出,实数序列的FFT是先计算N/2个实数的CFFT,然后再重塑数据进行处理从而获得半个FFT频谱即可(利用了FFT变换后频谱的对称性)。 一个N点的实数序列FFT逆变换采用下面的步骤实现: 实数FFT支持浮点,Q31和Q15三种数据类型。 & k. b/ Y, g, }* x
32.2 实数FFT, C R4 `* ~+ M
32.2.1 arm_rfft_fast_f32函数定义如下: void arm_rfft_fast_f32( arm_rfft_fast_instance_f32 * S, float32_t * p, float32_t * pOut, uint8_t ifftFlag) 参数定义: [in] *S points to an arm_rfft_fast_instance_f32 structure. [in] *p points to the input buffer. [in] *pOut points to the output buffer. [in] ifftFlag RFFT if flag is 0, RIFFT if flag is 1 注意事项: 结构arm_rfft_fast_instance_f32的定义如下(在文件arm_math.h文件): typedef struct { arm_cfft_instance_f32 Sint; /**< Internal CFFT structure. */ uint16_t fftLenRFFT; /**< length of the real sequence */ float32_t * pTwiddleRFFT; /**< Twiddle factors real stage */ } arm_rfft_fast_instance_f32 ; ) w, J/ G2 |: x1 s5 T
下面通过在开发板上运行函数arm_rfft_fast_f32和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。 - /*
6 i3 c% {7 X* ?/ C - *********************************************************************************************************
4 }$ i- G; M% t0 K7 b - * 函 数 名: arm_rfft_fast_f32_app
7 C' H7 @8 O( s" U - * 功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比
% k, X$ M* n" D- \' Y% p - * 形 参:无 e0 Y2 ]' m% S( h, ]. u2 y" c
- * 返 回 值: 无
) o, R5 P; d- z+ v% r - *********************************************************************************************************8 f/ X- T: l/ l H, _$ C# |
- */4 G8 F) N' [$ Q7 {' T# z7 ^$ w
- static void arm_rfft_fast_f32_app(void)
! G( w# ]: N5 S' D+ f - {
1 ]1 S* }3 d4 ]- Z, U& J - uint16_t i;; h( C" N% I, z
- arm_rfft_fast_instance_f32 S;# F8 `4 L9 o" S) B/ t' \/ s
- /* 实数序列FFT长度 */7 D! w; E1 }7 j
- fftSize = 1024; 1 a$ u* U: I6 a# Q# j. ~; s
- /* 正变换 */
4 U! Q0 e8 O8 A3 s/ { - ifftFlag = 0;
& ]$ e* e+ z: [ R% a7 Q - /* 初始化结构体S中的参数 */
1 s6 C0 a' g: W% u. y+ Y, v/ W8 B - arm_rfft_fast_init_f32(&S, fftSize); Q& m3 }+ s- O4 w1 c# T3 K
- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */9 U: a" J1 r2 D# K9 u
- for(i=0; i<1024; i++)( _, i* E7 l8 m' P* C# I) ]1 s" t
- {2 u( N3 {9 x" H( ~1 i* d% v
- /* 50Hz正弦波,采样率1KHz */. S4 `; m2 G8 Y E6 I- E: r5 O
- testInput_f32_10khz[i] = 1.2f*arm_sin_f32(2*3.1415926f*50*i/1000)+1;* V5 Z0 R. T+ d2 C# p& @& p- u
- }+ q; d9 N* Z1 D
- /* 1024点实序列快速FFT */ 6 o5 v7 M5 N- g# f) Z; ?$ L
- arm_rfft_fast_f32(&S, testInput_f32_10khz, testOutput_f32_10khz, ifftFlag);
+ ?. ]5 _ Y* O, D - /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32
" H ]( j) l4 `1 A0 c, b - 只求解出了512组
% S0 o2 n' h6 c# W6 x) f" G - */
. ?, n# q! h& `: o - arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);& s4 J" Z* g7 e7 V j, _
- 3 ?* b9 a! a, \( P( {
- /* 串口打印求解的模值 */6 Q; y1 w8 `% V4 V& X$ U( N# P
- for(i=0; i<fftSize; i++)% c* R8 K6 M u3 Z
- {- W) C$ a1 z' C0 r }# C1 L6 I
- printf("%f\r\n", testOutput[i]);3 P" x0 n8 Q" o* k: @# q
- }
/ n/ ]$ g4 g: o$ h - 3 i$ @; S) d8 T: g9 }( R- p Y% J1 J
- printf("****************************分割线***************************************\r\n");
9 ]- k# N8 n1 q+ l! ?9 |; `) \; a -
6 l0 x& l5 g- x5 ]0 X; G9 d - for(i=0; i<1024; i++)
9 b& @& u# N$ G/ F6 o8 t- m - {/ c5 C* z1 W0 P0 L! J
- /* 虚部全部置零 */9 M+ ], ?0 m0 v K! _( |
- testInput_f32_10khz[i*2+1] = 0;
. c2 E; W7 `0 n% b; v - /* 50Hz正弦波,采样率1KHz ,作为实部 */0 P& E% ^8 \& V5 Y- F9 v. \
- testInput_f32_10khz[i*2] = 1.2f*arm_sin_f32(2*3.1415926f*50*i/1000)+1;
8 b& \3 { j R - }
* i. _+ Z, L. H- k+ g! m - arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);- [6 t& d) R' K
- /* 求解模值 */
9 S; y( E* E4 R0 W7 b - arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
! g$ Q2 i0 S& P: d -
) W5 z2 S* ~, @ - /* 串口打印求解的模值 */- Q- z. Q) N- J" x
- for(i=0; i<fftSize; i++)% E( X( x, _# o) T% _0 a
- {
$ S8 Z" R- x" m! }; G7 p5 f, Y - printf("%f\r\n", testOutput[i]);
n b: D5 B5 S2 |7 ` - }) p* b* Q! K0 i7 {
- }
复制代码运行如上函数可以通过串口打印出函数arm_rfft_fast_f32和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_fast_f32的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数
5 \$ P, _" b5 o- X! f9 R, |9 D4 R8 ?" D" n4 p
n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率 4 F H6 S, i$ Z9 p. `: t# r/ J
# D6 b# K1 o: x) }7 w9 ]subplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); , F8 S0 ~& H: `. i4 v( {& B6 ~
+ R ]. E3 j% @# Ssubplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值'); # T" r% J0 b( P9 ?1 a7 V
( M4 C& V w; O% ?) P
Matlab运行结果如下: 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_fast_f32输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_fast_f32的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。 函数arm_rfft_fast_f32在计算直流分量(也就是频率为0的值)的虚部上是有错误的。关于这点大家可以将实际的实部和虚部输出结果打印出来做对比,但差别很小,基本可以忽略。
& [$ E, S( {: ^8 m9 X8 e32.2.2 arm_rfft_q15函数定义如下: void arm_rfft_q15( const arm_rfft_instance_q15 * S, q15_t * pSrc, q15_t * pDst) 参数定义: [in] *S points to an instance of the Q15 RFFT/RIFFT structure. [in] *pSrc points to the input buffer. [out] *pDst points to the output buffer. return none. 注意事项: 结构arm_rfft_instance_q15的定义如下(在文件arm_math.h文件): typedef struct { uint32_t fftLenReal; uint8_t ifftFlagR; uint8_t bitReverseFlagR; uint32_t twidCoefRModifier; q15_t *pTwiddleAReal; q15_t *pTwiddleBReal; const arm_cfft_instance_q15 *pCfft; } arm_rfft_instance_q15; 8 d& K6 K6 Y$ d1 Y# y, B7 L
下面通过在开发板上运行函数arm_rfft_q15和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。 - /*# F% P5 ]; U) q, s8 l) z
- *********************************************************************************************************
) N$ b: M4 R! I2 u- w) A - * 函 数 名: arm_rfft_q15_app- X& N; H3 g& a
- * 功能说明: 调用函数arm_rfft_q15计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。# z; ~3 H+ C. M% J/ L5 n3 f5 e* V
- * 形 参:无
# x1 F4 ]. g3 G! @ - * 返 回 值: 无# y a: j$ b- W% ?9 w$ U
- *********************************************************************************************************
& Y V) c: h. _% \9 W - */8 G- p h1 t7 S8 o$ j! ^
- static void arm_rfft_q15_app(void)
- P( X% m9 L- j1 O" }$ L - {
! X. l2 f: z1 J, f' X0 @/ [ - uint16_t i,j;
$ Y o: P6 f0 G- O5 t( O; X - arm_rfft_instance_q15 S;. H$ j( [( X r
- /* 实数序列FFT长度 */9 v1 S$ z5 i% F/ K' e. y1 ?/ a
- fftSize = 1024; 7 a/ ^3 B5 e; R- A- `# x1 E& m6 @* O
- /* 正变换 */
& O; Z" l) h$ j) H0 I+ H6 D- |" n - ifftFlag = 0;
1 }! D! j# ~! V) k6 |+ M! s# _) ? - /* 码位倒序 */
: i9 L$ m0 O8 @( b7 |# g& K - doBitReverse = 1; $ R; R* B/ X9 K# Q" B. ]
- /* 初始化结构体S */. v; z0 @7 h* z1 l" B/ H
- arm_rfft_init_q15(&S, fftSize, ifftFlag, doBitReverse);8 V1 x8 \! `. {
- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */- p% R6 G. O4 P5 p
- for(i=0; i<1024; i++) |0 d6 c; m+ G3 h% g% X, j( r
- {$ j% T0 S( d1 o' c7 Z" s q
- /* 51.2Hz正弦波,采样率1024Hz。 * S0 [! p. V/ B+ ?2 k
- arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,+ j6 Q1 q5 o! }' o% G
- 32768 / 20 = 1638.4- B" Z) u" K4 p& N
- */+ j4 z! _ r* G+ i% A2 K* a
- j = i % 20;
$ [( e: v& @; K! z+ v+ h$ C - testInput_q15_50hz[i] = arm_sin_q15(1638*j);
3 B9 n r: s, t- K# x- V% T - }
V, R+ ~# I7 V! x& c - /* 1024点实序列快速FFT */
: E: F* h" ~5 u1 X0 {# U% s - arm_rfft_q15(&S, testInput_q15_50hz, testOutput_q15_50hz);& X7 w& F% m! `) \
- /* 由于输出结果的格式是Q5,所以这里将定点数转换为浮点数 */
5 d" X& f, C: N" L6 ]+ H0 R - for(i = 0; i < fftSize; i++)4 ? V3 h9 \$ d" ]" x
- {! K) z+ c$ ~7 @1 y7 V9 R
- testOutput_f32_10khz[i] = (float32_t)testOutput_q15_50hz[i]/32;/ a3 i+ h$ |8 B! q* x. g
- }
# S. L+ l' L0 E" S! `, [ - /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组 , O1 H; ?0 u, C5 M' v3 s( F
- 实际函数arm_rfft_q15只求解出了512组 */ 2 Y7 v: {( ]8 C; U* H$ S7 E
- arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
1 ^; X( ]$ ~6 @$ a& g/ s - : [) `) ~/ e. i+ o# \% b) L
- /* 串口打印求解的模值 */
5 d6 I; T6 k7 N - for(i=0; i<fftSize; i++)
5 n) e' U Y9 W! \, z* f1 d0 y - {1 K1 d, U2 r p$ |9 o7 Z: R
- printf("%f\r\n", testOutput[i]);# `0 \3 P' M& n8 ^) |
- }3 V; K* Q; x: |6 W3 \
- printf("****************************分割线***************************************\r\n");
8 T! A4 x- F7 o6 T1 m3 }/ j, k- g# h9 A W - for(i=0; i<1024; i++)
1 d- L4 K* n3 ]1 H - {
+ O5 i5 \2 V" w: V. ^ - /* 51.2Hz正弦波,采样率1024Hz。
/ ^/ _( U' e" @- b8 T - arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,, s3 T8 @/ Z- |, p) `
- 32768 / 20 = 1638.4
- n+ t+ z* k" L# r1 X - */
% E! u7 r2 ~ A. a' Z! ^5 Y - j = i % 20;5 e q; G6 O, J8 u; o- f
- testInput_f32_10khz[i*2] = (float32_t) arm_sin_q15(1638*j)/32768;
4 E b2 W3 N7 k$ s6 W - /* 虚部全部置零 */+ C# V3 d( s) }
- testInput_f32_10khz[i*2+1] = 0;
% W6 {2 C4 y! a+ |3 O - }
, Y/ A# j6 |5 ~$ f. N - arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);
$ u2 m% t7 Z* B* L - /* 求解模值 */
( z2 X$ K- P3 S - arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
j# r0 ?. Q( J; k/ C+ q - /* 串口打印求解的模值 */5 _. P0 l: ^6 v, t
- for(i=0; i<fftSize; i++)9 C" q7 B/ ]! j- z
- {
5 W( N0 [* P% @, R - printf("%f\r\n", testOutput[i]);; e& z: z# C7 d, U. K- S' ?
- }
2 q6 h, J, H8 t* c7 | - }
复制代码运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数
$ ?0 i. Q5 |2 U6 W; {; L/ s8 J1 J3 l: ~2 _
n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率
0 z2 G- W- k: @8 a9 @& m4 q; q" d8 K8 {- E6 @( F5 ~. n- \
subplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); , [' ]+ S2 D6 _
) H' }) C/ _' @$ ~subplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值');
4 F9 h0 Z& R4 O5 y) f) t
& Q+ y8 R' x5 \- `! z' h, [Matlab运行结果如下: 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。
+ E! M- w. _+ s: E; h; G$ ~32.2.3 arm_rfft_q31函数定义如下: void arm_rfft_q31( const arm_rfft_instance_q31 * S, q31_t * pSrc, q31_t * pDst) 参数定义: [in] *S points to an instance of the Q31 RFFT/RIFFT structure. [in] *pSrc points to the input buffer. [out] *pDst points to the output buffer. return none. 注意事项: 结构arm_rfft_instance_q31的定义如下(在文件arm_math.h文件): typedef struct { uint32_t fftLenReal; uint8_t ifftFlagR; uint8_t bitReverseFlagR; uint32_t twidCoefRModifier; q31_t *pTwiddleAReal; q31_t *pTwiddleBReal; const arm_cfft_instance_q31 *pCfft; } arm_rfft_instance_q31; , v, i) H& r) V* k6 J8 C4 P
下面通过在开发板上运行函数arm_rfft_q31和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。 - /*' V2 Z' g( _4 x8 i9 I
- ********************************************************************************************************** x+ Z2 v( S# ?1 X6 B8 |5 V
- * 函 数 名: arm_rfft_q31_app) w' R2 L8 W! B' B- B' D# S
- * 功能说明: 调用函数arm_rfft_q31计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。
% K5 C i9 l& i5 b& I8 s2 b9 s: Z - * 形 参:无% v' ^1 B; b3 }/ s8 S" k0 ^
- * 返 回 值: 无
T1 Q1 f5 l( O) w - *********************************************************************************************************
7 w& Q; u: ^: p! l% f. q0 O - */
' D) j/ T* J) B- T& X8 p0 k% d - static void arm_rfft_q31_app(void)
! ?: h" f! z4 `, r1 \ - {/ X6 ?5 X8 q) \
- uint16_t i,j;3 X' h l* j7 c0 Q/ X
- arm_rfft_instance_q31 S;, ? u' ^' g! H `+ W. L
- /* 实数序列FFT长度 */6 w# Z; F- r: F$ N w* H+ {
- fftSize = 1024; ' {9 i: C: h3 v
- /* 正变换 */4 p: J* K3 I+ w3 j( g5 W2 ?
- ifftFlag = 0; % Q" k, T- E, ^, p+ P
- /* 码位倒序 */
! r2 u8 a5 p! N; ?% w4 ^, \ - doBitReverse = 1; * ^+ S- r& s$ C; }" Y& V
- /* 初始化结构体S */
( ?2 {1 R! U3 A0 ^8 B - arm_rfft_init_q31(&S, fftSize, ifftFlag, doBitReverse);5 H0 c* e/ H" j
- /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
. A* H k" ?$ i% u0 b0 l - for(i=0; i<1024; i++)6 @$ k% w& n' y* ~
- {0 V# i9 H. c, Z% y% W) U! i
- /* 51.2Hz正弦波,采样率1024Hz。
% F5 \5 o5 g8 { - arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
& N: ?2 M: ^: v+ W - 2^31 / 20 = 107374182.4
# c& \6 i, d, I" m- h - */6 f9 z: x2 s7 M! k* P( r
- j = i % 20;: ?/ L, N# T3 A) @* Q0 x- X6 O" b
- testInput_q31_50hz[i] = arm_sin_q31(107374182*j);" [! _8 ^% I4 s/ a! e
- }8 u0 ~$ j! }$ Z6 o
- /* 1024点实序列快速FFT */ ( ^1 i; i, A( i! a
- arm_rfft_q31(&S, testInput_q31_50hz, testOutput_q31_50hz);3 k) I4 X. E% K
- /* 由于输出结果的格式是Q21,所以这里将定点数转换为浮点数 */8 U) F$ g0 m$ M* V
- for(i = 0; i < fftSize; i++)1 R4 l0 V3 @/ k% `' x. ^7 d
- {2 m/ Y4 ^& W. v0 \+ a
- /* 输出的数据是11.21格式,2^21 = 4194304*/2 i0 F8 f0 l! \ v+ m$ @5 c% g1 n
- testOutput_f32_10khz[i] = (float32_t)testOutput_q31_50hz[i]/2097152;" X# X9 h. V+ U5 |% a6 w; ~
- }
/ S9 \6 L" `9 U. ^ - /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组 ; U, ~& u) j$ X3 ?% L+ q
- 实际函数arm_rfft_q31只求解出了512组 */
: {: S& o, P4 U3 ^ - arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
) E4 e6 F* |9 s8 M2 W - /* 串口打印求解的模值 */
) M! f6 g( q$ D4 p; x. M - for(i=0; i<fftSize; i++)3 {/ X$ U2 z& @* R: l$ v" F+ O
- { z z g1 z& L' W1 r6 V
- printf("%f\r\n", testOutput[i]);3 b9 o9 I! B; o
- }
* E& b' M' d4 R/ T$ f3 s4 O - printf("****************************分割线***************************************\r\n");3 Y) a. l7 K! q% @
- for(i=0; i<1024; i++)
. r$ {4 Q$ K: d8 ? - {$ }7 a5 s* B6 V$ b d
- /* 51.2Hz正弦波,采样率1024Hz。 4 M/ G! E, e5 Y! e$ }0 J4 z9 t
- arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
2 t" n' q( @& L' S - 2^31 / 20 = 107374182.4
' Y( `3 Q* R; o - */
" Y0 s5 B. K( d. S - j = i % 20;
2 U$ I2 n# \- a! w* ]' T - testInput_f32_10khz[i*2] = (float32_t)arm_sin_q31(107374182*j)/2147483648;7 ^1 G5 I% [0 }& @3 x6 q& ^
- /* 虚部全部置零 */
5 A e6 R/ [8 l/ \4 d - testInput_f32_10khz[i*2+1] = 0;* R: z" d4 b7 K( S. y( G% A6 N
- }: ]( U; M# j. K
- arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);, n) M3 P. }8 y. K( x6 ?* @
- /* 求解模值 */
& @, D( T( C+ H- b$ L- Q; O; Z - arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);" A4 N( I7 ?/ T1 M ]
- /* 串口打印求解的模值 */! i1 |( b ?5 C( U
- for(i=0; i<fftSize; i++)
! [9 u( t- F' z& Q - {. T/ {6 X. c( `! s( { n1 K E
- printf("%f\r\n", testOutput[i]);0 M- L K3 O' E, y
- }
I8 n( m( e+ d - }
复制代码运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。 对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下: Fs = 1000; % 采样率 N = 1024; % 采样点数 b: g; c) |$ z/ ]+ [
" N% O5 I. o9 }n = 0:N-1; % 采样序列 f = n * Fs / N; %真实的频率
8 H1 o5 Z& C$ a; @
, X3 T- ? R( C8 x9 u. ssubplot(3,1,1); plot(f, signal); %绘制RFFT结果 title('实数FFT'); xlabel('时间'); ylabel('幅值'); : y9 Z8 }' {6 R! b5 v6 V6 i5 B
/ R2 [' e$ A4 Z! D% z& rsubplot(3,1,2); plot(f, sampledata); %CFFT结果 title('复数FFT'); xlabel('时间'); ylabel('幅值'); : a, R: x2 v& b4 s/ \( T# U: r8 G
6 O: c2 {! ^# K4 }" UMatlab运行结果如下: 从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。
3 a2 j: b" k. Z5 p32.3 总结 使用实数FFT计算的时候要特别的注意本章节提到的几个错误点。
H; h R" v4 e- [- s |
谢谢!希望继续支持啊~~
程序是这样子:/ I% `* o% e* l: E+ A# g
用ADC采集值填入fft输入buff
void ADC_proc(void)5 m" I5 J* Z+ F8 `1 T5 g9 P
{
uint16_t ai,cnt; 5 C1 J- A7 D& R8 }0 b3 }7 U7 d
if(adc_conv_done)# D6 H# r4 ]: j: q5 |
{, h$ D* L( V6 |6 f) |( B$ Y
adc_conv_done = 0;
. R, ~0 d& M! B& @! M( R1 ]: C& x- C
for(ai=0;ai<NPT;ai++)! i% K% L- c: O* B- _' Y5 F
{# X* l' C! }, k4 J+ i
lbufin[ai*2] = (float)(adc_buf[ai*2]-2048);/ {* R8 l6 A+ F5 X% L
lbufin[ai*2+1] = (float)0;9 C' u- t- A4 K8 A) g- H+ v
}
FFT_proc(); $ {/ q' S6 | B
HAL_ADC_Start_DMA(&hadc,(uint32_t*)adc_buf,sizeof(adc_buf)/2);
}% O6 @% j+ q. W
}
2 W" P5 n- j0 M! G6 ?. l. Y
FFT处理
1 p4 j+ A7 Z8 s3 q: b( x3 n7 i4 a
float lbufin[NPT*2]; /* Complex input vector */
float lbufout[NPT]; /* Complex output vector */. b2 f' Y h, j2 Q) _8 h
float lbufmag; /* Magnitude vector */% q( j1 ~) Q/ o/ V( w0 `
uint16_t fftSize = 64;
H0 b! A7 K/ J$ z
uint8_t ifftFlag = 0; p: R# S: Z/ q/ b" E
uint8_t doBitReverse = 1;+ v+ g& ]/ }& }3 H1 G0 s; v
uint16_t audio_mag;8 p6 k/ M+ v7 x- o$ M0 t
extern uint8_t audio_intf_flag;7 Y4 b4 R/ [, _
//uint32_t refIndex = 213,2 j. X9 {& p! y$ W2 {" z8 a" G, L. ^
uint32_t testIndex = 0;
__IO uint8_t new_mag_flag;
/ j, C {6 s3 `6 R; s# Z* [' U
void FFT_proc()
{
arm_cfft_f32(&arm_cfft_sR_f32_len64, lbufin, ifftFlag, doBitReverse);1 j g( o9 \! S( Y1 T1 ^
arm_cmplx_mag_f32(lbufin,lbufout,fftSize);9 O( o, O; x3 @2 g$ P8 Y
arm_max_f32(lbufout, NPT, &lbufmag, &testIndex);
} % s) o- s+ G; S4 }