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

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

[复制链接]
baiyongbin2009 发布时间:2015-4-17 10:26
特别说明:完整45期数字信号处理教程,原创高性能示波器代码全开源地址:链接% Z/ H! M: L/ j& `
第32章 实数FFT的实现

! ~1 c- ?. D) T- J. V/ Q5 D9 e3 ]
    本章主要讲解实数的浮点和定点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 总结

  w, C( E" y  }' ~8 z1 {9 a) ^9 ~; K8 _; c+ A5 ^" ^# r& X
32.1 实数FFT" ]& G! X/ D  ^, I  x
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三种数据类型。

/ q0 v, s' V. p
32.2 实数FFT
4 q/ ^) S+ j$ I9 f
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 ;
$ P8 l$ Y) ^) ?# X4 Z1 F5 D
    下面通过在开发板上运行函数arm_rfft_fast_f32和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
  1. /*
    " f) Y5 [. z( j$ g$ v% w( k: x& y
  2. *********************************************************************************************************  v  Q+ D6 x7 {" \$ ~
  3. *        函 数 名: arm_rfft_fast_f32_app4 d, P1 F& M0 [% J4 r1 W
  4. *        功能说明: 调用函数arm_rfft_fast_f32计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算结果做对比1 {9 T* e/ i$ n) T' D& |
  5. *        形    参:无
    ' V# X! ^* \1 p& u& T
  6. *        返 回 值: 无3 P8 B, o5 A& t6 r( t4 s- f0 m
  7. *********************************************************************************************************
    / t* n4 S; b# {, c6 y5 N
  8. */
    3 }; ?# v- i7 p* [  f. b
  9. static void arm_rfft_fast_f32_app(void)
    0 T  a# H" @3 e7 Y2 U
  10. {  {( e# z9 Y  `# n6 v& ]
  11. uint16_t i;3 l- M3 [9 l2 I+ j' @6 i/ m7 F
  12. arm_rfft_fast_instance_f32 S;
    3 l# Q; M0 e* K  f* R: f
  13. /* 实数序列FFT长度 */
    ; T! I& J, _% \6 `2 ~, g" {
  14. fftSize = 1024;
    . e& O( F/ w. B% r) k0 M$ u
  15. /* 正变换 */6 V3 t$ F+ L% v, X( Q0 @) [4 j6 F. H
  16.     ifftFlag = 0; + l  e0 \$ J" O  B  \9 `
  17. /* 初始化结构体S中的参数 */
    ( q4 w7 ~- R' y) E: o
  18.          arm_rfft_fast_init_f32(&S, fftSize);) k. `! E2 j0 A2 x* k6 d
  19. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    7 v) o; |3 p" r8 Z
  20. for(i=0; i<1024; i++)
    9 i" ?( O  D! [6 Z, k. n( Q
  21. {5 L. m6 A( U1 L) I' n/ E% e
  22. /* 50Hz正弦波,采样率1KHz */
    * G0 U) X- ~- z% u
  23. testInput_f32_10khz[i] = 1.2f*arm_sin_f32(2*3.1415926f*50*i/1000)+1;+ w, i/ _! W4 _8 Z& q
  24. }6 A  i, Q7 `8 l9 M- k
  25. /* 1024点实序列快速FFT */ 4 j; E/ z+ g3 f8 L5 U# V
  26. arm_rfft_fast_f32(&S, testInput_f32_10khz, testOutput_f32_10khz, ifftFlag);) \! U. y7 R: w$ Q- u  Y
  27. /* 为了方便跟函数arm_cfft_f32计算的结果做对比,这里求解了1024组模值,实际函数arm_rfft_fast_f328 |, Y: v( ^  I" k$ B0 D9 b! ?- K
  28.    只求解出了512组  
    " o" F7 L6 L$ P2 N
  29. */
    ) b6 f4 V6 q% ]  \1 E$ [) M
  30.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
    & U$ C5 M3 z5 f2 K& x- Y
  31. . O$ X& w5 C* T
  32. /* 串口打印求解的模值 */
    1 J4 Y% U) b: l$ f6 Q
  33. for(i=0; i<fftSize; i++)
    4 d1 B( x- e# p
  34. {# T3 |1 z% E4 m: i! B$ b  K& S3 Y  @6 T2 t
  35. printf("%f\r\n", testOutput[i]);
    * }7 `& P& F5 U$ |8 X0 h
  36. }2 Q  {3 l" \# |1 O4 T

  37. : i" l% V+ T: {4 @
  38. printf("****************************分割线***************************************\r\n");1 O  q$ O' r6 M) R( w- q2 a3 j/ u

  39. . Q; \( Y+ s  S- S( q" K* a' r
  40. for(i=0; i<1024; i++)
    5 \6 d7 Y1 e$ b  f" q
  41. {; H4 m' B0 J: I' R
  42. /* 虚部全部置零 */
    7 s6 `1 \" p- R8 N: m
  43. testInput_f32_10khz[i*2+1] = 0;- Q, q2 n4 I- l9 B9 i& E1 |
  44. /* 50Hz正弦波,采样率1KHz ,作为实部 */( A5 y" E* l5 }8 O  U
  45. testInput_f32_10khz[i*2] = 1.2f*arm_sin_f32(2*3.1415926f*50*i/1000)+1;
    5 z0 s0 M/ B; v4 ]/ N# j  q' J
  46. }% M. m1 m  A4 ~: X
  47. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);
    9 g8 ~. k$ k$ A6 {" i
  48. /* 求解模值  */
    ; i0 l0 o: e1 q
  49.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
    * T* f& Z. b5 s# }' B  H8 H

  50. 5 S1 g" F$ Q7 X8 A& o3 A2 L. k
  51. /* 串口打印求解的模值 */
    / C% a' s6 G' B' o5 ?5 n: e
  52. for(i=0; i<fftSize; i++)1 a( s2 y7 h" d5 ?8 L7 p' P
  53. {
    3 V6 ?4 \& t% e! K+ H" A
  54. printf("%f\r\n", testOutput[i]);
    ) A, R6 q4 }3 j  {
  55. }
    ) R7 q  f# ^2 K: e
  56. }
复制代码
运行如上函数可以通过串口打印出函数arm_rfft_fast_f32和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。
    对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_fast_f32的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1000;              % 采样率
N  = 1024;            % 采样点数

' s. T6 e' N1 m) x- v6 G) q3 n2 Q4 L+ G1 A# a
n  = 0:N-1;            % 采样序列
f = n * Fs / N;          %真实的频率

! Z% o; q( g& a+ L
: P. c" x+ V! z3 M1 b( M
subplot(3,1,1);
plot(f,  signal);        %绘制RFFT结果
title('实数FFT');
xlabel('时间');
ylabel('幅值');
0 Q2 l) w* P: [$ ]
' r& u; g* G- R  k
subplot(3,1,2);
plot(f,  sampledata);   %CFFT结果
title('复数FFT');
xlabel('时间');
ylabel('幅值');

* j/ n% h# m6 w! q6 `
1 k$ p3 V4 a. f5 ]' G: ~, ^" `1 m
Matlab运行结果如下:
32.3.png
    从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_fast_f32输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_fast_f32的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。
    函数arm_rfft_fast_f32在计算直流分量(也就是频率为0的值)的虚部上是有错误的。关于这点大家可以将实际的实部和虚部输出结果打印出来做对比,但差别很小,基本可以忽略。
+ N  H& f% N2 G) A# |
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;
" Q6 I! ]! {) v- F- O
    下面通过在开发板上运行函数arm_rfft_q15和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
  1. /*
      e: V' [: o( d! z
  2. *********************************************************************************************************; N% [* h8 b, L( F8 G
  3. *        函 数 名: arm_rfft_q15_app  U% Q2 s* ]0 W( ?9 {- s
  4. *        功能说明: 调用函数arm_rfft_q15计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。
    - l/ i! _6 v- z; n1 X
  5. *        形    参:无
    , C" l. D5 x" ]9 e( |1 @
  6. *        返 回 值: 无. n' j4 x4 i: k0 R
  7. *********************************************************************************************************
    7 w) ]- l/ {2 l) J  \. G/ u" f" \
  8. */
    ; j' _+ Q0 t/ @8 Q( m! k5 ]
  9. static void arm_rfft_q15_app(void)
    8 y  P' {% {2 _, |: ^9 D
  10. {
    2 |6 `% k0 @+ K
  11. uint16_t i,j;4 G% y/ Q: {# P' V
  12. arm_rfft_instance_q15 S;
    8 @$ j1 e+ [; X3 ], W% s+ N
  13. /* 实数序列FFT长度 */
    8 X1 f5 i* n, Z/ r# J4 j" H0 ]
  14. fftSize = 1024;
    - Q6 q+ A+ v3 W3 U
  15. /* 正变换 */4 B* t! \, w" M6 h
  16.     ifftFlag = 0;
    / l$ K5 n+ r1 F# A+ n% X3 @# o6 q6 ]) c
  17. /* 码位倒序 */
    ) t; \( N. W" f) |5 l2 k9 P4 Q
  18.     doBitReverse = 1;
    . S8 g. K& M4 X
  19. /* 初始化结构体S */
    1 [# o# J! u% a* V" A6 d
  20.          arm_rfft_init_q15(&S, fftSize, ifftFlag, doBitReverse);/ s+ I, m9 [! I3 \; l6 ^8 m
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */# q3 I% z/ G3 l3 _. B# }
  22. for(i=0; i<1024; i++)$ m, }9 {$ F2 w' v7 `+ ?& F
  23. {
    , ~' S7 @) x" O
  24. /* 51.2Hz正弦波,采样率1024Hz。
    " x, n" r$ u8 T: L' R5 u% Q
  25.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,) D- `2 S" I# D" n/ U7 z
  26.    32768 / 20 = 1638.4# I$ M7 G5 [+ t- F2 J
  27. */
    & W6 s& v  F  Q" K9 Z% `
  28. j = i % 20;8 _8 r- a- n& p& H# l
  29.           testInput_q15_50hz[i] = arm_sin_q15(1638*j);$ \2 F( B" Q1 ]: V/ o
  30. }6 N& K7 N% a2 s5 ^% {/ W  T
  31. /* 1024点实序列快速FFT */
    . c- W# c0 X( j7 Z- l
  32. arm_rfft_q15(&S, testInput_q15_50hz, testOutput_q15_50hz);; w' p! |: w: ^/ E# ^0 m
  33. /* 由于输出结果的格式是Q5,所以这里将定点数转换为浮点数 */+ ]* E; {( F; {$ {1 |( b( e
  34. for(i = 0; i < fftSize; i++)  ~& z* v2 Y0 O* i& s3 x' ]
  35. {/ p, y# ~6 ~! j) S8 G
  36. testOutput_f32_10khz[i] = (float32_t)testOutput_q15_50hz[i]/32;) x; z; D5 y$ Q3 [. C+ j) P" N
  37. }' s4 B: q  f( V& m+ M' e' Q& R& q
  38. /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组  " ~# ^0 S5 R" [/ C3 f( Y9 Q
  39.    实际函数arm_rfft_q15只求解出了512组  */
    3 W  L5 I8 d& W& [; S/ S& N! K' O
  40.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);
    5 U& `' @9 r0 X6 s9 @
  41. " W  y4 U) c4 e8 V- S9 G: z0 V8 H
  42. /* 串口打印求解的模值 */
    2 c3 y" y7 |/ ^
  43. for(i=0; i<fftSize; i++)
    * c5 `) ^4 R& c6 |' I/ ^$ W
  44. {! r' m% v) ?  I' Y
  45. printf("%f\r\n", testOutput[i]);
    4 c8 a0 m- R( e7 o6 _( c" L6 z
  46. }
    . H( N+ o9 z2 F
  47. printf("****************************分割线***************************************\r\n");
    : R% n: E  z8 f* X$ T5 V
  48. for(i=0; i<1024; i++). ~5 T( v1 m0 J
  49. {
    8 o" y( V5 E6 y" |- M3 w
  50. /* 51.2Hz正弦波,采样率1024Hz。
    , l5 u4 g$ a$ p1 I
  51.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,
    ) o' w1 d# M/ ?. r3 m* e
  52.    32768 / 20 = 1638.4$ b4 Q) ]% P3 I' ?- U$ h
  53. */9 @  n5 i1 ^  R
  54. j = i % 20;
    % y1 }" e- q/ _5 l  e) a8 F
  55. testInput_f32_10khz[i*2] = (float32_t) arm_sin_q15(1638*j)/32768;/ p, b0 `3 X' O" q( u% t
  56. /* 虚部全部置零 */
    + b+ C* W' a: v2 @7 j
  57. testInput_f32_10khz[i*2+1] = 0;
    ) b  Z+ A8 P* V' K
  58. }
    ' g7 q) S8 r# ?9 c5 }
  59. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);) w: a0 ?. [! n  w, i% z1 d- G
  60. /* 求解模值  */
    + ?5 O5 a5 k+ `( j: {8 y& V* V
  61.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
    - F9 ~8 n* l- ~% Q2 z& q
  62. /* 串口打印求解的模值 */
    : N) M4 b' W" x- m! ]
  63. for(i=0; i<fftSize; i++)
    % Q, l/ n  g1 a! a  O8 T5 {5 O3 z+ C
  64. {
    $ C8 H2 l5 |8 @+ R7 w
  65. printf("%f\r\n", testOutput[i]);
    % s& w9 Y: ?9 i0 y5 V& F
  66. }
    4 N4 k9 e% h% v
  67. }
复制代码
运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。
    对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1000;             % 采样率
N  = 1024;            % 采样点数
+ Y! R! \) I, [' Y7 r* q

4 ]' P" C3 ?& p& ?! C
n  = 0:N-1;            % 采样序列
f = n * Fs / N;          %真实的频率

& Z$ j2 [% s6 e. f% l* h/ ]2 s
& z5 I/ S' E6 |! T: @
subplot(3,1,1);
plot(f,  signal);        %绘制RFFT结果
title('实数FFT');
xlabel('时间');
ylabel('幅值');

* F( h! z4 B2 M6 g- u: D3 w3 U. y
subplot(3,1,2);
plot(f,  sampledata);   %CFFT结果
title('复数FFT');
xlabel('时间');
ylabel('幅值');

$ ?) H! ]8 f8 O. K7 U9 E) m; n: u+ U. Z/ Y# D, R# l& C
Matlab运行结果如下:
32.4.png
    从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。
" D3 j1 O$ |, U' u
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;
4 G% @  c0 K8 D7 @' R3 Y
    下面通过在开发板上运行函数arm_rfft_q31和arm_cfft_f32计算幅频响应,然后将相应的频率响应结果在Matlab上面绘制出来。
  1. /*
    ; \" U8 T- \' }' O! P
  2. *********************************************************************************************************% O: i! r2 b7 Q2 w' t
  3. *        函 数 名: arm_rfft_q31_app
    . C1 J! d+ \; E- S2 q
  4. *        功能说明: 调用函数arm_rfft_q31计算1024点实数序列的幅频响应并跟使用函数arm_cfft_f32计算的结果做对比。
    ( r! s1 U9 r8 c3 E
  5. *        形    参:无4 p& Z! S7 Z6 i$ ~0 N% x
  6. *        返 回 值: 无2 \6 t$ M: {& _, Y+ N
  7. *********************************************************************************************************
    ! q/ {" L2 H4 l8 h( W
  8. */
    5 S# U# {. b( P: L/ ^2 X- ?+ q
  9. static void arm_rfft_q31_app(void)
    : D) U: P) d6 \
  10. {
    ! R, w2 X) s/ r; s! X0 h
  11. uint16_t i,j;
    5 D& N0 n! w) @4 Q( u
  12. arm_rfft_instance_q31 S;! j: S6 r. V& Q* D3 L0 M9 {
  13. /* 实数序列FFT长度 */( j  j3 _+ t- M, L  ?: P
  14. fftSize = 1024; 2 Q+ g& `. F% T
  15. /* 正变换 */% J0 K8 P1 R8 l/ I% [( b
  16.     ifftFlag = 0; - u1 W' q) Z' B' W
  17. /* 码位倒序 *// q" M8 k' w* H
  18.     doBitReverse = 1; / g% s0 P% [+ w% N4 |% D2 ?6 F
  19. /* 初始化结构体S */& `1 ]* C2 j! b* |: \: q0 v
  20.          arm_rfft_init_q31(&S, fftSize, ifftFlag, doBitReverse);' t, _7 {- n4 v0 A, @
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    8 Y4 I: S; l# P- o& L! d( p$ [' w4 Q
  22. for(i=0; i<1024; i++)
    3 R% b; H4 e8 K! S* `( n2 G( c
  23. {5 }& R" J) Q4 U- Q) |
  24. /* 51.2Hz正弦波,采样率1024Hz。
    ( z+ c, K3 r9 _2 w
  25.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
    / S' \+ ^- I6 k4 ~
  26.    2^31 / 20 = 107374182.4
    1 u% u% @0 O+ i6 |: z# d! p$ K. t
  27. */- j% {- V; z; {& I. U
  28. j = i % 20;
    ) F! o1 M3 ?. N( t6 d
  29.           testInput_q31_50hz[i] = arm_sin_q31(107374182*j);2 c8 x4 A2 M$ H- F7 E
  30. }$ Q$ }3 t; S) B
  31. /* 1024点实序列快速FFT */ + E" U2 _  @4 e( ]$ M4 i
  32. arm_rfft_q31(&S, testInput_q31_50hz, testOutput_q31_50hz);. _3 x0 V$ {7 e6 G! Y; y4 H) n2 h
  33. /* 由于输出结果的格式是Q21,所以这里将定点数转换为浮点数 */
    0 s, d7 t3 I% n9 t# R. G1 S: x/ z- j
  34. for(i = 0; i < fftSize; i++)! ?% h! B& n/ l' j( T
  35. {
    5 n; F2 x& W9 }7 j
  36. /* 输出的数据是11.21格式,2^21 = 4194304*/
    " o4 g  X/ h- j0 U
  37. testOutput_f32_10khz[i] = (float32_t)testOutput_q31_50hz[i]/2097152;9 h7 E% e! t2 x: {! \! b* r% P: t
  38. }
    . i' L- o* H# M: }( q! Z
  39. /* 为了方便对比,这里求解了1024组复数,实际上面的变化只有512组  4 g. r! q/ b0 A# J" S& F
  40.    实际函数arm_rfft_q31只求解出了512组  */3 F# }; ~' d; ^+ F. g2 ^
  41.          arm_cmplx_mag_f32(testOutput_f32_10khz, testOutput, fftSize);$ X! L( ^$ V$ V3 s/ @% {( V
  42. /* 串口打印求解的模值 */: w# u$ r. N7 G- }5 k
  43. for(i=0; i<fftSize; i++)
    " q+ S9 C# \6 n" W
  44. {
    9 L( s+ q6 R: t
  45. printf("%f\r\n", testOutput[i]);6 {; H; l" M* W" d# V  f
  46. }
    + N  e( [2 w+ n) x$ u% v! m
  47. printf("****************************分割线***************************************\r\n");
    9 a/ B+ J+ a$ l& {3 M, _. y7 R
  48. for(i=0; i<1024; i++)! F- `2 Z: m& D' F% `
  49. {
    ( V7 @. a' j7 ^, d
  50. /* 51.2Hz正弦波,采样率1024Hz。
    & Y( p. R! z" I; w
  51.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,
    + s- z" Q3 m& S7 e
  52.    2^31 / 20 = 107374182.4
    : p+ {( o/ q6 f2 I0 ~
  53. */6 s* S& W  p, C8 q( _1 N4 F6 q
  54. j = i % 20;
    3 M( J% N. Z1 W/ Q( [
  55.           testInput_f32_10khz[i*2] = (float32_t)arm_sin_q31(107374182*j)/2147483648;0 Y/ D) x3 l+ K% |
  56. /* 虚部全部置零 */
    ( p& V8 d4 |0 i+ Y5 X" `3 [
  57. testInput_f32_10khz[i*2+1] = 0;
    , Y/ R# Q% O9 p4 c
  58. }1 \0 H9 @/ K4 P; e" t
  59. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);6 v$ q" e3 X3 `4 a0 ?9 q9 c/ }+ P
  60. /* 求解模值  */
    5 \& J2 [6 I2 I; q0 s, {! Z1 ^4 x
  61.          arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);. _/ K, l9 w( e# z( I
  62. /* 串口打印求解的模值 */6 m( u$ b0 s: t' q7 i0 S3 }" c; p
  63. for(i=0; i<fftSize; i++)% x  t4 i* X2 H* o$ y; f$ J
  64. {2 p& z. w. l8 W( ?$ p3 e7 c
  65. printf("%f\r\n", testOutput[i]);
    8 c- r- O0 f. B5 x' }/ h8 ]) R( ^
  66. }
    - p: m; @  n; t- T- V
  67. }
复制代码
运行如上函数可以通过串口打印出函数arm_rfft_q15和arm_cfft_f32计算的幅频模值,下面通过Matlab绘制波形来对比这两种模值。
    对比前需要先将串口打印出的两组数据加载到Matlab中,arm_rfft_q15的计算结果起名signal,arm_cfft_f32的计算结果起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1000;             % 采样率
N  = 1024;            % 采样点数

" h3 ?' d8 R; t* y; u* @
7 ]9 R- K. ^$ C- |: g# X
n  = 0:N-1;            % 采样序列
f = n * Fs / N;          %真实的频率
, O- _: B2 A5 x% {" B% x$ p
  s6 [+ x; ~, F& i8 U- a& ]
subplot(3,1,1);
plot(f,  signal);        %绘制RFFT结果
title('实数FFT');
xlabel('时间');
ylabel('幅值');

" g4 ~, x- c! ?  N
' q4 r! L5 O6 z
subplot(3,1,2);
plot(f,  sampledata);   %CFFT结果
title('复数FFT');
xlabel('时间');
ylabel('幅值');
  S: @1 u. b6 `, Q' R

3 y; \- {& d  k, \8 U6 J. V
Matlab运行结果如下:
32.5.png
    从上面的前512点对比中,我们可以看出两者的计算结果是相符的。这里有一点要特别注意,官方文档中对于函数arm_rfft_q31输出结果的实部,虚部排列顺序说明是错误的。函数arm_rfft_q31的输出结果仍然是实部,虚部,实部,虚部….. 依次排列下去。

7 j) ?0 i3 Q$ e, Z& `
32.3 总结
    使用实数FFT计算的时候要特别的注意本章节提到的几个错误点。

9 R! Y7 W+ V9 a; k6 t( ~; C+ h+ t
赞 收藏 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
3 Y6 e  ]! w1 @0 X( o3 j8 V5 M2 Q沙发,楼主对算法很有研究,赞一个

2 }- h. p& l; e% D谢谢!希望继续支持啊~~
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
请教一下:我这样计算出来的值是不是对的?为什么每个频率上都有值啊?
) X9 Y$ g' N" Z% }/ r) x fft2.png
, K6 ~  k# H) ?0 v5 T( t程序是这样子:
/ q- m9 h+ k. T5 R/ X用ADC采集值填入fft输入buff+ ?5 V1 j  g# u
void ADC_proc(void)( p! M: Y7 {  F& C7 i0 ?2 o
{ / ^- i: @; ^1 A
    uint16_t ai,cnt;
3 E5 M, P- l! H/ F! ^8 W        5 l5 X7 m  d! |# j: m
    if(adc_conv_done)8 F" T6 @) r+ e
    {# J8 E( ~, L$ f/ L0 }
       adc_conv_done = 0;
# T$ u, e3 x$ L# i8 o8 T6 j, [7 x$ d- G- j: T: s
. r# _5 E0 g( _! m, K8 ?; Y& l
            for(ai=0;ai<NPT;ai++)' W! [* L  T# O4 u: [* f$ c! `( p
                {7 V7 l: F5 {( R! m
                      lbufin[ai*2] = (float)(adc_buf[ai*2]-2048);
  Y* Z6 P- ^1 _2 }                          lbufin[ai*2+1] = (float)0;+ a% D5 r" B% M: ~
                }
( h6 R7 a; U) A: Q6 e8 y7 S6 {          FFT_proc();      
2 l/ T( q0 F! e7 Q3 q- R0 D         HAL_ADC_Start_DMA(&hadc,(uint32_t*)adc_buf,sizeof(adc_buf)/2);
2 P  ?  K" a" z/ ^$ E    }, T& ~! W) Q/ Y" [3 B- e
}
2 d/ t+ |; u4 c" A6 m( g# y9 m# g5 A

: M$ O6 h+ Y" a, \" c! E- v$ H
  i/ [5 G$ B) R
FFT处理
# H$ g: ^0 @- w" B, F
9 R" s% {2 }0 H' M) K

' x6 j( B5 z- V" h( Q- [* p9 c. k6 i. Z4 C- l9 t
float  lbufin[NPT*2];                                                           /* Complex input vector */
5 N. V" l4 u! }0 f" F+ @float  lbufout[NPT];                                                          /* Complex output vector */, A* C8 p: R1 l% ?
float  lbufmag;                                                                  /* Magnitude vector */: h' Y% D# X! `3 {
uint16_t fftSize = 64;
3 |: Y* [- H: k
: Y# O& A8 ~( y% F8 y+ d
/ w* F" U) q/ s: `/ S8 n
uint8_t ifftFlag = 0;
1 e2 A. b" C2 V" {- I, f( juint8_t doBitReverse = 1;6 Y; g9 v* ]7 H
uint16_t audio_mag;) k" k) w( x( S5 ?2 }: `
extern  uint8_t  audio_intf_flag;
7 Z. l. G8 u( v& \5 ?//uint32_t refIndex = 213,$ c1 \1 G% R0 c- F
uint32_t testIndex = 0;3 z7 K/ @8 n) b0 M- j, z8 n
__IO uint8_t  new_mag_flag;) @. q% C* G' Q* G! b+ b. v
0 u1 n4 Q" a4 v5 [' K) W

! F9 t+ ?. q) @void FFT_proc()' e0 X& N# o% i; Z' J
{                      1 o- `; u/ v9 |1 P
    arm_cfft_f32(&arm_cfft_sR_f32_len64, lbufin, ifftFlag, doBitReverse);9 C$ [3 y7 Q& Y* x
        arm_cmplx_mag_f32(lbufin,lbufout,fftSize);2 u( b$ U1 i1 t3 i( T6 t
    arm_max_f32(lbufout, NPT, &lbufmag, &testIndex);
! d1 m" c9 \3 v+ Y* A& B! n}  9 V6 a  m2 Z" F% U# o
" n8 H. v, [* ]
adc.png
fft1.png
whyil 回答时间:2019-8-20 16:17:55

所属标签

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