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

【原创】【安富莱——DSP教程】第32章 实数FFT的实现

[复制链接]
baiyongbin2009 发布时间:2015-4-17 10:26
特别说明:完整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正变换采用下面的步骤实现:
32.1.png
    由上面的框图可以看出,实数序列的FFT是先计算N/2个实数的CFFT,然后再重塑数据进行处理从而获得半个FFT频谱即可(利用了FFT变换后频谱的对称性)。
    一个N点的实数序列FFT逆变换采用下面的步骤实现:
32.2.png
    实数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上面绘制出来。
  1. /*
    6 i3 c% {7 X* ?/ C
  2. *********************************************************************************************************
    4 }$ i- G; M% t0 K7 b
  3. *        函 数 名: arm_rfft_fast_f32_app
    7 C' H7 @8 O( s" U
  4. *        功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比
    % k, X$ M* n" D- \' Y% p
  5. *        形    参:无  e0 Y2 ]' m% S( h, ]. u2 y" c
  6. *        返 回 值: 无
    ) o, R5 P; d- z+ v% r
  7. *********************************************************************************************************8 f/ X- T: l/ l  H, _$ C# |
  8. */4 G8 F) N' [$ Q7 {' T# z7 ^$ w
  9. static void arm_rfft_fast_f32_app(void)
    ! G( w# ]: N5 S' D+ f
  10. {
    1 ]1 S* }3 d4 ]- Z, U& J
  11. uint16_t i;; h( C" N% I, z
  12. arm_rfft_fast_instance_f32 S;# F8 `4 L9 o" S) B/ t' \/ s
  13. /* 实数序列FFT长度 */7 D! w; E1 }7 j
  14. fftSize = 1024; 1 a$ u* U: I6 a# Q# j. ~; s
  15. /* 正变换 */
    4 U! Q0 e8 O8 A3 s/ {
  16.     ifftFlag = 0;
    & ]$ e* e+ z: [  R% a7 Q
  17. /* 初始化结构体S中的参数 */
    1 s6 C0 a' g: W% u. y+ Y, v/ W8 B
  18.          arm_rfft_fast_init_f32(&S, fftSize);  Q& m3 }+ s- O4 w1 c# T3 K
  19. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */9 U: a" J1 r2 D# K9 u
  20. for(i=0; i<1024; i++)( _, i* E7 l8 m' P* C# I) ]1 s" t
  21. {2 u( N3 {9 x" H( ~1 i* d% v
  22. /* 50Hz正弦波,采样率1KHz */. S4 `; m2 G8 Y  E6 I- E: r5 O
  23. 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
  24. }+ q; d9 N* Z1 D
  25. /* 1024点实序列快速FFT */ 6 o5 v7 M5 N- g# f) Z; ?$ L
  26. arm_rfft_fast_f32(&S, testInput_f32_10khz, testOutput_f32_10khz, ifftFlag);
    + ?. ]5 _  Y* O, D
  27. /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f32
    " H  ]( j) l4 `1 A0 c, b
  28.    只求解出了512组  
    % S0 o2 n' h6 c# W6 x) f" G
  29. */
    . ?, n# q! h& `: o
  30.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);& s4 J" Z* g7 e7 V  j, _
  31. 3 ?* b9 a! a, \( P( {
  32. /* 串口打印求解的模值 */6 Q; y1 w8 `% V4 V& X$ U( N# P
  33. for(i=0; i<fftSize; i++)% c* R8 K6 M  u3 Z
  34. {- W) C$ a1 z' C0 r  }# C1 L6 I
  35. printf("%f\r\n", testOutput[i]);3 P" x0 n8 Q" o* k: @# q
  36. }
    / n/ ]$ g4 g: o$ h
  37. 3 i$ @; S) d8 T: g9 }( R- p  Y% J1 J
  38. printf("****************************分割线***************************************\r\n");
    9 ]- k# N8 n1 q+ l! ?9 |; `) \; a

  39. 6 l0 x& l5 g- x5 ]0 X; G9 d
  40. for(i=0; i<1024; i++)
    9 b& @& u# N$ G/ F6 o8 t- m
  41. {/ c5 C* z1 W0 P0 L! J
  42. /* 虚部全部置零 */9 M+ ], ?0 m0 v  K! _( |
  43. testInput_f32_10khz[i*2+1] = 0;
    . c2 E; W7 `0 n% b; v
  44. /* 50Hz正弦波,采样率1KHz ,作为实部 */0 P& E% ^8 \& V5 Y- F9 v. \
  45. testInput_f32_10khz[i*2] = 1.2f*arm_sin_f32(2*3.1415926f*50*i/1000)+1;
    8 b& \3 {  j  R
  46. }
    * i. _+ Z, L. H- k+ g! m
  47. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);- [6 t& d) R' K
  48. /* 求解模值  */
    9 S; y( E* E4 R0 W7 b
  49.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
    ! g$ Q2 i0 S& P: d

  50. ) W5 z2 S* ~, @
  51. /* 串口打印求解的模值 */- Q- z. Q) N- J" x
  52. for(i=0; i<fftSize; i++)% E( X( x, _# o) T% _0 a
  53. {
    $ S8 Z" R- x" m! }; G7 p5 f, Y
  54. printf("%f\r\n", testOutput[i]);
      n  b: D5 B5 S2 |7 `
  55. }) p* b* Q! K0 i7 {
  56. }
复制代码
运行如上函数可以通过串口打印出函数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% @# S
subplot(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运行结果如下:
32.3.png
    从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_fast_f32输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_fast_f32的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。
    函数arm_rfft_fast_f32在计算直流分量(也就是频率为0的值)的虚部上是有错误的。关于这点大家可以将实际的实部和虚部输出结果打印出来做对比,但差别很小,基本可以忽略。

& [$ E, S( {: ^8 m9 X8 e
32.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上面绘制出来。
  1. /*# F% P5 ]; U) q, s8 l) z
  2. *********************************************************************************************************
    ) N$ b: M4 R! I2 u- w) A
  3. *        函 数 名: arm_rfft_q15_app- X& N; H3 g& a
  4. *        功能说明: 调用函数arm_rfft_q15计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。# z; ~3 H+ C. M% J/ L5 n3 f5 e* V
  5. *        形    参:无
    # x1 F4 ]. g3 G! @
  6. *        返 回 值: 无# y  a: j$ b- W% ?9 w$ U
  7. *********************************************************************************************************
    & Y  V) c: h. _% \9 W
  8. */8 G- p  h1 t7 S8 o$ j! ^
  9. static void arm_rfft_q15_app(void)
    - P( X% m9 L- j1 O" }$ L
  10. {
    ! X. l2 f: z1 J, f' X0 @/ [
  11. uint16_t i,j;
    $ Y  o: P6 f0 G- O5 t( O; X
  12. arm_rfft_instance_q15 S;. H$ j( [( X  r
  13. /* 实数序列FFT长度 */9 v1 S$ z5 i% F/ K' e. y1 ?/ a
  14. fftSize = 1024; 7 a/ ^3 B5 e; R- A- `# x1 E& m6 @* O
  15. /* 正变换 */
    & O; Z" l) h$ j) H0 I+ H6 D- |" n
  16.     ifftFlag = 0;
    1 }! D! j# ~! V) k6 |+ M! s# _) ?
  17. /* 码位倒序 */
    : i9 L$ m0 O8 @( b7 |# g& K
  18.     doBitReverse = 1; $ R; R* B/ X9 K# Q" B. ]
  19. /* 初始化结构体S */. v; z0 @7 h* z1 l" B/ H
  20.          arm_rfft_init_q15(&S, fftSize, ifftFlag, doBitReverse);8 V1 x8 \! `. {
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */- p% R6 G. O4 P5 p
  22. for(i=0; i<1024; i++)  |0 d6 c; m+ G3 h% g% X, j( r
  23. {$ j% T0 S( d1 o' c7 Z" s  q
  24. /* 51.2Hz正弦波,采样率1024Hz。 * S0 [! p. V/ B+ ?2 k
  25.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,+ j6 Q1 q5 o! }' o% G
  26.    32768 / 20 = 1638.4- B" Z) u" K4 p& N
  27. */+ j4 z! _  r* G+ i% A2 K* a
  28. j = i % 20;
    $ [( e: v& @; K! z+ v+ h$ C
  29.           testInput_q15_50hz[i] = arm_sin_q15(1638*j);
    3 B9 n  r: s, t- K# x- V% T
  30. }
      V, R+ ~# I7 V! x& c
  31. /* 1024点实序列快速FFT */
    : E: F* h" ~5 u1 X0 {# U% s
  32. arm_rfft_q15(&S, testInput_q15_50hz, testOutput_q15_50hz);& X7 w& F% m! `) \
  33. /* 由于输出结果的格式是Q5,所以这里将定点数转换为浮点数 */
    5 d" X& f, C: N" L6 ]+ H0 R
  34. for(i = 0; i < fftSize; i++)4 ?  V3 h9 \$ d" ]" x
  35. {! K) z+ c$ ~7 @1 y7 V9 R
  36. testOutput_f32_10khz[i] = (float32_t)testOutput_q15_50hz[i]/32;/ a3 i+ h$ |8 B! q* x. g
  37. }
    # S. L+ l' L0 E" S! `, [
  38. /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组  , O1 H; ?0 u, C5 M' v3 s( F
  39.    实际函数arm_rfft_q15只求解出了512组  */ 2 Y7 v: {( ]8 C; U* H$ S7 E
  40.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
    1 ^; X( ]$ ~6 @$ a& g/ s
  41. : [) `) ~/ e. i+ o# \% b) L
  42. /* 串口打印求解的模值 */
    5 d6 I; T6 k7 N
  43. for(i=0; i<fftSize; i++)
    5 n) e' U  Y9 W! \, z* f1 d0 y
  44. {1 K1 d, U2 r  p$ |9 o7 Z: R
  45. printf("%f\r\n", testOutput[i]);# `0 \3 P' M& n8 ^) |
  46. }3 V; K* Q; x: |6 W3 \
  47. printf("****************************分割线***************************************\r\n");
    8 T! A4 x- F7 o6 T1 m3 }/ j, k- g# h9 A  W
  48. for(i=0; i<1024; i++)
    1 d- L4 K* n3 ]1 H
  49. {
    + O5 i5 \2 V" w: V. ^
  50. /* 51.2Hz正弦波,采样率1024Hz。
    / ^/ _( U' e" @- b8 T
  51.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,, s3 T8 @/ Z- |, p) `
  52.    32768 / 20 = 1638.4
    - n+ t+ z* k" L# r1 X
  53. */
    % E! u7 r2 ~  A. a' Z! ^5 Y
  54. j = i % 20;5 e  q; G6 O, J8 u; o- f
  55. testInput_f32_10khz[i*2] = (float32_t) arm_sin_q15(1638*j)/32768;
    4 E  b2 W3 N7 k$ s6 W
  56. /* 虚部全部置零 */+ C# V3 d( s) }
  57. testInput_f32_10khz[i*2+1] = 0;
    % W6 {2 C4 y! a+ |3 O
  58. }
    , Y/ A# j6 |5 ~$ f. N
  59. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);
    $ u2 m% t7 Z* B* L
  60. /* 求解模值  */
    ( z2 X$ K- P3 S
  61.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
      j# r0 ?. Q( J; k/ C+ q
  62. /* 串口打印求解的模值 */5 _. P0 l: ^6 v, t
  63. for(i=0; i<fftSize; i++)9 C" q7 B/ ]! j- z
  64. {
    5 W( N0 [* P% @, R
  65. printf("%f\r\n", testOutput[i]);; e& z: z# C7 d, U. K- S' ?
  66. }
    2 q6 h, J, H8 t* c7 |
  67. }
复制代码
运行如上函数可以通过串口打印出函数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运行结果如下:
32.4.png
    从上面的前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上面绘制出来。
  1. /*' V2 Z' g( _4 x8 i9 I
  2. ********************************************************************************************************** x+ Z2 v( S# ?1 X6 B8 |5 V
  3. *        函 数 名: arm_rfft_q31_app) w' R2 L8 W! B' B- B' D# S
  4. *        功能说明: 调用函数arm_rfft_q31计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。
    % K5 C  i9 l& i5 b& I8 s2 b9 s: Z
  5. *        形    参:无% v' ^1 B; b3 }/ s8 S" k0 ^
  6. *        返 回 值: 无
      T1 Q1 f5 l( O) w
  7. *********************************************************************************************************
    7 w& Q; u: ^: p! l% f. q0 O
  8. */
    ' D) j/ T* J) B- T& X8 p0 k% d
  9. static void arm_rfft_q31_app(void)
    ! ?: h" f! z4 `, r1 \
  10. {/ X6 ?5 X8 q) \
  11. uint16_t i,j;3 X' h  l* j7 c0 Q/ X
  12. arm_rfft_instance_q31 S;, ?  u' ^' g! H  `+ W. L
  13. /* 实数序列FFT长度 */6 w# Z; F- r: F$ N  w* H+ {
  14. fftSize = 1024; ' {9 i: C: h3 v
  15. /* 正变换 */4 p: J* K3 I+ w3 j( g5 W2 ?
  16.     ifftFlag = 0; % Q" k, T- E, ^, p+ P
  17. /* 码位倒序 */
    ! r2 u8 a5 p! N; ?% w4 ^, \
  18.     doBitReverse = 1; * ^+ S- r& s$ C; }" Y& V
  19. /* 初始化结构体S */
    ( ?2 {1 R! U3 A0 ^8 B
  20.          arm_rfft_init_q31(&S, fftSize, ifftFlag, doBitReverse);5 H0 c* e/ H" j
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    . A* H  k" ?$ i% u0 b0 l
  22. for(i=0; i<1024; i++)6 @$ k% w& n' y* ~
  23. {0 V# i9 H. c, Z% y% W) U! i
  24. /* 51.2Hz正弦波,采样率1024Hz。
    % F5 \5 o5 g8 {
  25.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
    & N: ?2 M: ^: v+ W
  26.    2^31 / 20 = 107374182.4
    # c& \6 i, d, I" m- h
  27. */6 f9 z: x2 s7 M! k* P( r
  28. j = i % 20;: ?/ L, N# T3 A) @* Q0 x- X6 O" b
  29.           testInput_q31_50hz[i] = arm_sin_q31(107374182*j);" [! _8 ^% I4 s/ a! e
  30. }8 u0 ~$ j! }$ Z6 o
  31. /* 1024点实序列快速FFT */ ( ^1 i; i, A( i! a
  32. arm_rfft_q31(&S, testInput_q31_50hz, testOutput_q31_50hz);3 k) I4 X. E% K
  33. /* 由于输出结果的格式是Q21,所以这里将定点数转换为浮点数 */8 U) F$ g0 m$ M* V
  34. for(i = 0; i < fftSize; i++)1 R4 l0 V3 @/ k% `' x. ^7 d
  35. {2 m/ Y4 ^& W. v0 \+ a
  36. /* 输出的数据是11.21格式,2^21 = 4194304*/2 i0 F8 f0 l! \  v+ m$ @5 c% g1 n
  37. testOutput_f32_10khz[i] = (float32_t)testOutput_q31_50hz[i]/2097152;" X# X9 h. V+ U5 |% a6 w; ~
  38. }
    / S9 \6 L" `9 U. ^
  39. /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组  ; U, ~& u) j$ X3 ?% L+ q
  40.    实际函数arm_rfft_q31只求解出了512组  */
    : {: S& o, P4 U3 ^
  41.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
    ) E4 e6 F* |9 s8 M2 W
  42. /* 串口打印求解的模值 */
    ) M! f6 g( q$ D4 p; x. M
  43. for(i=0; i<fftSize; i++)3 {/ X$ U2 z& @* R: l$ v" F+ O
  44. {  z  z  g1 z& L' W1 r6 V
  45. printf("%f\r\n", testOutput[i]);3 b9 o9 I! B; o
  46. }
    * E& b' M' d4 R/ T$ f3 s4 O
  47. printf("****************************分割线***************************************\r\n");3 Y) a. l7 K! q% @
  48. for(i=0; i<1024; i++)
    . r$ {4 Q$ K: d8 ?
  49. {$ }7 a5 s* B6 V$ b  d
  50. /* 51.2Hz正弦波,采样率1024Hz。 4 M/ G! E, e5 Y! e$ }0 J4 z9 t
  51.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
    2 t" n' q( @& L' S
  52.    2^31 / 20 = 107374182.4
    ' Y( `3 Q* R; o
  53. */
    " Y0 s5 B. K( d. S
  54. j = i % 20;
    2 U$ I2 n# \- a! w* ]' T
  55.           testInput_f32_10khz[i*2] = (float32_t)arm_sin_q31(107374182*j)/2147483648;7 ^1 G5 I% [0 }& @3 x6 q& ^
  56. /* 虚部全部置零 */
    5 A  e6 R/ [8 l/ \4 d
  57. testInput_f32_10khz[i*2+1] = 0;* R: z" d4 b7 K( S. y( G% A6 N
  58. }: ]( U; M# j. K
  59. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);, n) M3 P. }8 y. K( x6 ?* @
  60. /* 求解模值  */
    & @, D( T( C+ H- b$ L- Q; O; Z
  61.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);" A4 N( I7 ?/ T1 M  ]
  62. /* 串口打印求解的模值 */! i1 |( b  ?5 C( U
  63. for(i=0; i<fftSize; i++)
    ! [9 u( t- F' z& Q
  64. {. T/ {6 X. c( `! s( {  n1 K  E
  65. printf("%f\r\n", testOutput[i]);0 M- L  K3 O' E, y
  66. }
      I8 n( m( e+ d
  67. }
复制代码
运行如上函数可以通过串口打印出函数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. s
subplot(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& r
subplot(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 }" U
Matlab运行结果如下:
32.5.png
    从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。

3 a2 j: b" k. Z5 p
32.3 总结
    使用实数FFT计算的时候要特别的注意本章节提到的几个错误点。

  H; h  R" v4 e- [- s
赞 收藏 2 评论12 发布时间:2015-4-17 10:26

举报

12个回答
a003 回答时间:2019-7-23 17:45:54
楼主你好,请问怎么看出来的信号的频率以及幅值的啊?
stary666 回答时间:2015-4-17 10:30:27
沙发,楼主对算法很有研究,赞一个
baiyongbin2009 回答时间:2015-4-17 11:38:23
stary666 发表于 2015-4-17 10:30# v# ^' v& L7 M9 ^9 s
沙发,楼主对算法很有研究,赞一个

8 v# _1 V" ^  T% U" O: V谢谢!希望继续支持啊~~
wamcncn 回答时间:2015-4-17 13:55:04
算法在程序里很重要
小蚂蚁快溜跑 回答时间:2015-4-17 21:10:28
支持。。。。。。。
aderson 回答时间:2015-4-17 21:32:55
好高端,看不懂,
拼命三郎 回答时间:2015-4-17 22:27:23
xxxxxxxxxx.jpg
拼命三郎 回答时间:2015-4-17 22:27:45
dd.jpg
拼命三郎 回答时间:2015-4-17 22:38:37
stm32.jpg
jack-2054789 回答时间:2018-7-30 15:30:20
谢谢分享!!
lewe 回答时间:2019-8-19 09:49:09
请教一下:我这样计算出来的值是不是对的?为什么每个频率上都有值啊?
% w* l3 Q- ?$ n& }1 r fft2.png # x0 \( Y! j& m2 B0 K
程序是这样子:/ I% `* o% e* l: E+ A# g
用ADC采集值填入fft输入buff
; W' a; r. B  F% T* _* qvoid ADC_proc(void)5 m" I5 J* Z+ F8 `1 T5 g9 P
{
# S" d7 A4 ~* D4 [( c1 }) B- H2 N    uint16_t ai,cnt; 5 C1 J- A7 D& R8 }0 b3 }7 U7 d
       
8 _; N1 O5 b- W, {) Q% a0 Q    if(adc_conv_done)# D6 H# r4 ]: j: q5 |
    {, h$ D* L( V6 |6 f) |( B$ Y
       adc_conv_done = 0;
: q; M6 k/ s2 Y- g+ ?6 l
3 ?; E9 T- P6 ]! L9 y& o
. 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
                }
" Z3 S* ^( R  ?7 h7 a" M          FFT_proc();      $ {/ q' S6 |  B
         HAL_ADC_Start_DMA(&hadc,(uint32_t*)adc_buf,sizeof(adc_buf)/2);
' q# \+ @5 D  O; T- A3 N    }% O6 @% j+ q. W
}
5 p+ Q# w6 i% c. k. m8 U) f
' z6 v, d3 M3 e4 [9 `2 W" P5 n- j0 M! G6 ?. l. Y

% g1 }. X% s. A3 \# f$ QFFT处理
4 {* e3 H, c! T/ u% N
. r0 m# a4 S/ y# ^% U- O
1 p4 j+ A7 Z8 s3 q: b( x3 n7 i4 a

2 g* _0 ^' S7 c! kfloat  lbufin[NPT*2];                                                           /* Complex input vector */
; u  H# s/ {. @! |* n3 @4 C1 ofloat  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;
( z1 F8 J7 V$ \  H0 b! A7 K/ J$ z

! c1 f& Z) o. s, F7 ~+ t- g/ p# Uuint8_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;
3 N! ?; @& I( E/ Q7 N* R! X__IO uint8_t  new_mag_flag;
: e- W% w% H8 u, s' V
3 G2 m/ [- z! t# I8 `# u& ~
/ j, C  {6 s3 `6 R; s# Z* [' U
void FFT_proc()
7 ]! w! U' \! p" C$ y0 o7 U! q{                      
4 s; S6 \. O: j0 x    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);
+ I- G: ~( D- E+ Q7 x, y" \}  % s) o- s+ G; S4 }

/ ^- s, S; Y& C  y0 T  d1 h
adc.png
fft1.png
whyil 回答时间:2019-8-20 16:17:55

所属标签

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