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

【安富莱——DSP教程】第30章 复数FFT的实现

[复制链接]
baiyongbin2009 发布时间:2015-4-15 10:31
特别说明:完整45期数字信号处理教程,原创高性能示波器代码全开源地址:链接
& R. e8 c4 B" s
第30章 复数FFT的实现
6 N, r' r6 C: a
) V6 V5 C5 Y0 j$ P+ }
    本章主要讲解复数FFT的浮点和定点Q31,Q15的实现。
    本章节使用的复数FFT函数来自ARM官方库的TransformFunctions部分
     30.1 复数FFT
    30.2 复数FFT-基2算法
    30.3 复数FFT-基4算法
    30.4 总结

1 [2 s; @" G* W+ q30.1 复数FFT30.1.1 描述
    当前复数FFT函数支持三种数据类型,分别是浮点,Q31和Q15。这些FFT函数有一个共同的特点,就是用于输入信号的缓冲,在转化结束后用来存储输出结果。这样做的好处是节省了RAM空间,不需要为输入和输出结果分别设置缓存。由于是复数FFT,所以输入和输出缓存要存储实部和虚部。存储顺序如下:{real[0], imag[0], real[1], imag[1],………………} ,在使用中切记不要搞错。
30.1.2 浮点
    浮点复数FFT使用了一个混合基数算法,通过多个基8与单个基2或基4算法实现。根据需要,该算法支持的长度[16,32,64,...,4096]和每个长度使用不同的旋转因子表。
    浮点复数FFT使用了标准的FFT定义,FFT正变换的输出结果会被放大fftLen倍数,计算FFT逆变换的时候会缩小到1/fftLen。这样就与教科书中的定义一致了。
    定义好的旋转因子和位反转表已经在头文件arm_const_structs.h中定义好了,调用浮点FFT函数arm_cfft_f32时,包含相应的头文件即可。比如:
       arm_cfft_f32(arm_cfft_sR_f32_len64, pSrc, 1, 1)
    上式就是计算一个64点的FFT逆变换包括位反转。数据结构arm_cfft_sR_f32_len64可以认为是常数,计算的过程中是不能修改的。同样是这种数据结构还能用于混合基的FFT正变换和逆变换。
    早期发布的浮点复数FFT函数版本包含基2和基4两种方法实现的,但是不推荐大家再使用了。现在全部用arm_cfft_f32代替了。
30.1.3 定点Q31和Q15
    定点库提供了基2和基4两种算法,基2算法支持的数据长度[16, 32, 64, ..., 4096],基4算法支持的数据长度[16, 32, 64, ..., 4096]。一般情况下,建议使用基4算法,基4算法比基2算法执行速度要快一些。
    为了防止计算结果溢出,定点FFT每个蝶形运算的结果都要做放缩处理。对于基2算法,每次蝶形运算的结果要做0.5倍的放缩。对于基4算法,每次蝶形运算的结果要做0.25倍的放缩。FFT逆变换也要做相同的处理。相对于标准教科书式的FFT定义,定点FFT的计算结果放缩了1/fftLen(数据)倍。定点FFT的逆变也要做放缩处理,但是跟教科书式的FFT定义是相符的。
    每个FFT变换都需要一个单独的结构体,但结构体中的旋转因子和位反转表可以被重新使用。
    每个数据类型都有一个相关的初始化函数,初始化函数主要完成如下操作:
l 初始化结构体成员。
l 初始化旋转因子和位反转表指针。
    使用初始化函数是可选的。尽管如此,如果使用了初始化函数,那么结构体不能放在const data段,如果要放在const data段,应当按照如下方法进行初始化:
*arm_cfft_radix2_instance_q31 S = {fftLen, ifftFlag, bitReverseFlag, pTwiddle, pBitRevTable, twidCoefModifier, bitRevFactor};   
*arm_cfft_radix2_instance_q15 S = {fftLen, ifftFlag, bitReverseFlag, pTwiddle, pBitRevTable, twidCoefModifier, bitRevFactor};   
*arm_cfft_radix4_instance_q31 S = {fftLen, ifftFlag, bitReverseFlag, pTwiddle, pBitRevTable, twidCoefModifier, bitRevFactor};   
*arm_cfft_radix4_instance_q15 S = {fftLen, ifftFlag, bitReverseFlag, pTwiddle, pBitRevTable, twidCoefModifier, bitRevFactor};   
*arm_cfft_instance_f32 S = {fftLen, pTwiddle, pBitRevTable, bitRevLength};
    Q15和Q31 FFT使用了一个比较大的旋转因数和位反转表。这个表定义了一个最大长度的变换,表的子集可以用于短的变化。
30.1.4 arm_cfft_f32
函数定义如下:
    void arm_cfft_f32(
         const arm_cfft_instance_f32 * S,
         float32_t * p1,
         uint8_t ifftFlag,
         uint8_t bitReverseFlag)
参数定义:
     [in]   *S    points to an instance of the floating-point CFFT structure.  
     [in, out] *p1   points to the complex data buffer of size <code>2*fftLen</code>. Processing occurs in-place.  
     [in] ifftFlag         flag that selects forward (ifftFlag=0) or inverse (ifftFlag=1) transform.  
     [in] bitReverseFlag  flag that enables (bitReverseFlag=1) or disables (bitReverseFlag=0) bit reversal of output.  
注意事项:
结构const arm_cfft_instance_f32的定义如下(在文件arm_math.h文件):
      typedef struct
      {
          uint16_t fftLen;                  
          const float32_t *pTwiddle;         
          const uint16_t *pBitRevTable;      
          uint16_t bitRevLength;            
      } arm_cfft_instance_f32;

0 s6 q8 ~# K2 a" [& H8 k6 L6 ^
    下面通过在开发板上运行这个函数并计算幅频相应,然后再与Matlab计算的结果做对比。
  1. #define TEST_LENGTH_SAMPLES 2048
    ' J7 E' a( ]1 Y+ l* C* v3 D, Z  r
  2. * P6 j3 o% z: ]' B+ f, ]' z
  3. /* 输入和输出缓冲 */
    - p, ?. Q# D& W# x' H
  4. static float32_t testOutput[TEST_LENGTH_SAMPLES/2]; & i- t. _5 |5 }1 v; w. L
  5. static float32_t testInput_f32_10khz[TEST_LENGTH_SAMPLES];$ D5 t0 L% G7 O: k: J% K* t! S' V6 _

  6. ( X* `6 M" x4 @1 h
  7. /* 变量 */! i3 H# \/ ]: d/ p+ {) G
  8. uint32_t fftSize = 1024;
    1 L9 d) A" ?' L3 ?+ i
  9. uint32_t ifftFlag = 0;
    * p$ J, v1 [" s" j) d
  10. uint32_t doBitReverse = 1;
    $ i( |  Q# v* K: F4 u, [3 Q# `

  11. ; ~, u4 ~2 v  N% o; B# i
  12. /*
    , D% A$ u7 r7 I$ c2 V4 B5 g; p
  13. *********************************************************************************************************9 o6 P* _- f2 ?# T, ^0 E
  14. *        函 数 名: arm_cfft_f32_app
    8 V  L. C' H" d
  15. *        功能说明: 调用函数arm_cfft_f32_app计算幅频。
    - |6 Q' M/ B2 O
  16. *        形    参:无$ X7 B8 O/ d7 r- ~5 K
  17. *        返 回 值: 无
    1 R" {) w2 p5 g; h
  18. *********************************************************************************************************
    1 z6 \9 A2 ?4 x; k- [
  19. */
    ; Z( K; U0 f5 w  H
  20. void arm_cfft_f32_app(void)8 ~( e  n/ J, |1 i% @) |7 `' t5 G
  21. {$ M% Y& a) @7 C2 ?
  22. uint16_t i;
    0 L( }  ~* K- e
  23. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    , w7 Z$ V+ u2 q! m) K( O3 ~6 z% [
  24. for(i=0; i<1024; i++)
    # a1 W& T' _) n3 R% p' A7 H, N
  25. {
    9 A- Y/ Y6 F0 o5 T* ]. y
  26. /* 虚部全部置零 */
    8 N5 P6 Y) g  i1 F
  27. testInput_f32_10khz[i*2+1] = 0;
    " T& p$ G( E: v! P% u
  28. /* 50Hz正弦波,采样率1KHz ,作为实部 */% O& x+ j/ p+ \5 \7 e# G/ \/ w
  29. testInput_f32_10khz[i*2] = arm_sin_f32(2*3.1415926f*50*i/1000);4 U$ y9 {: P  F2 |1 V  o- q7 t# ~
  30. }
    # o# O4 c' [) A6 c: A
  31. /* CFFT变换 */
    ) V: |: A. f* l
  32. arm_cfft_f32(&arm_cfft_sR_f32_len1024, testInput_f32_10khz, ifftFlag, doBitReverse);: p) X1 w. v# S. E8 m0 ?0 s4 O3 B/ O
  33.   a& L+ |* F, {& k- \% l! Z
  34. /* 求解模值  */ : S9 `5 w; h7 C
  35. arm_cmplx_mag_f32(testInput_f32_10khz, testOutput, fftSize);
    ! b+ U3 f% s0 G9 R
  36. /* 串口打印求解的模值 */
    1 |. k4 W2 I& p( ]7 T
  37. for(i=0; i<1024; i++)* s" p6 |0 N/ Z1 J
  38. {) W- X" `1 @& b) n
  39. printf("%f\r\n", testOutput[i]);
    * ~, ^/ A% Q; Z; a
  40. }
    * Z: H' x" \# R8 o2 r3 @' l  m7 T

  41. $ y9 N+ B- b* D2 }
  42. }
复制代码
运行如上函数可以通过串口打印出计算的模值,下面我们就通过Matlab计算的模值跟arm_cfft_f32计算的模值做对比。
    对比前需要先将串口打印出的数据加载到Matlab中,并给这个数组起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1000;                % 采样率
N  = 1024;               % 采样点数
n  = 0:N-1;               % 采样序列
t  = 0:1/Fs:1-1/Fs;        % 时间序列
f = n * Fs / N;             %真实的频率

0 ?, C: Y0 A3 O& f" Q/ k: K& N; g) N
x = sin(2*pi*50*t) ;  %原始信号
y = fft(x, N);        %对原始信号做FFT变换

: J' O, G2 Z; J& R- \: Y6 ?3 d1 C: ^2 X- C/ [+ `
subplot(2,1,2);
plot(f, abs(y));       %绘制幅频相应曲线
title('Matlab计算结果');
xlabel('频率');
ylabel('幅度');

4 R1 k$ k% Q2 _' c
- X+ ^1 j% n7 c  l8 {1 j, Z
subplot(2,1,1);
plot(f,  sampledata);       %绘制幅频相应曲线
title('复数FFT计算结果');
xlabel('频率');
ylabel('幅度');

8 M2 A3 W  I/ {, Z9 W; m; @8 O2 h( S
8 E2 I6 J& H0 w& ^
Matlab运行结果如下:
30.1.png
从上面的对比结果中可以看出,Matlab和函数arm_cfft_f32计算的结果基本是一直的。

9 N- O7 C. F" e" u3 t
收藏 评论8 发布时间:2015-4-15 10:31

举报

8个回答
baiyongbin2009 回答时间:2015-4-15 10:41:22
30.2 复数FFT基2算法# [! C/ p' i8 W" ~" v7 q
30.2.1 arm_cfft_radix2_f32
    此函数已经不推荐使用,后面的版本会被删除,故不做介绍。
, G" ]9 e- a' H
30.2.2 arm_cfft_radix2_q31
函数定义如下:
    void arm_cfft_radix2_q31(
    const arm_cfft_radix2_instance_q31 * S,
    q31_t * pSrc)
参数定义:
    [in]      *S    points to an instance of the fixed-point CFFT/CIFFT structure.  
    [in, out] *pSrc  points to the complex data buffer of size <code>2*fftLen</code>. Processing occurs in-place.
注意事项:
结构const  arm_cfft_radix2_instance_q31的定义如下(在文件arm_math.h文件):
      typedef struct
      {
          uint16_t fftLen;               
          uint8_t ifftFlag;               
          uint8_t bitReverseFlag;         
          q31_t *pTwiddle;                 
          uint16_t *pBitRevTable;         
          uint16_t twidCoefModifier;      
          uint16_t bitRevFactor;         
      } arm_cfft_radix2_instance_q31;
! E4 ?: ^! D& J+ A; C! T  z; e4 n
    下面通过在开发板上运行这个函数并计算幅频相应,然后再与Matlab计算的结果做对比。
  1. q31_t testInput_radix2_q31_50hz[TEST_LENGTH_SAMPLES];! }9 f- w' N& Q2 Z! z# g! \
  2. q31_t testOutputQ31[TEST_LENGTH_SAMPLES/2];! O5 u/ M9 r8 E# ]1 N7 l: Y& z

  3. ( h) m8 m& A5 Y! ~2 m" J/ i8 N0 Z
  4. /*5 n! T$ y0 C3 Q7 H* ^
  5. *********************************************************************************************************
    . i0 \) c' n- Z5 ^, K) E
  6. *        函 数 名: arm_cfft_radix2_q31_app2 u" O0 v* K" c9 y
  7. *        功能说明: 调用函数arm_cfft_radix2_q31计算幅频。
    " q; ~* w  F$ e0 U  x& G
  8. *        形    参:无
    3 |6 X1 z0 k) O' ^# ?" w2 R
  9. *        返 回 值: 无5 S9 v- U/ G9 Q' l# ]: K! v0 g
  10. *********************************************************************************************************
    2 l. s/ p3 D3 y: W. i3 Y4 a
  11. */
    1 m, L4 _( R, q1 U  p  O7 \' [# S
  12. void arm_cfft_radix2_q31_app(void)' E  H; S" K- b9 Z
  13. {; i! L8 ^% H  \9 Y8 f
  14. uint16_t i,j;. e- ]2 v5 d, ]( V1 e
  15. arm_cfft_radix2_instance_q31 S;
    2 A8 ]( b5 C# Y1 F1 q& e, A
  16. fftSize = 1024;
    - R9 \- y( R9 ]& }% U
  17.     ifftFlag = 0;
    , ?" T+ |/ w* t
  18.     doBitReverse = 1; . f1 v! n. @9 L% ^2 S
  19. /* 初始化结构S */% v- H9 S+ ]( m. W7 o" R; P: L
  20. arm_cfft_radix2_init_q31(&S, fftSize, ifftFlag, doBitReverse);
    ; f) q# d, c4 z# @
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 *// z5 D6 J  w8 N) g6 n
  22. for(i=0; i<1024; i++), \1 @5 c* g# N4 m
  23. {
    , }0 E7 E& S* H! x5 D1 C, q
  24. testInput_radix2_q31_50hz[i*2+1] = 0;6 a2 j& H; s& q. t! i
  25. /* 51.2Hz正弦波,采样率1024Hz。 : I! b, W1 U& Q" x
  26.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,0 I& h) U+ S. I, I. J
  27.    2^31 / 20 = 107374182.4  ~) i+ y3 @/ X/ C& O4 O- D
  28. */
    : V4 G& f$ C* t/ `# P9 t
  29. j = i % 20;
    2 _2 K8 ^( Z4 O8 i0 ], K! n
  30.           testInput_radix2_q31_50hz[i*2] = arm_sin_q31(107374182*j);
    9 t7 V' F) T0 V
  31.           printf("%d\r\n", testInput_radix2_q31_50hz[i*2]);
    , J: k$ A$ [6 I" g- i2 [  U. R# T
  32. }# }/ j* e5 q* G
  33. /* 输出结果分割线 */
    ! Y$ u& O6 p( V! f8 h' r# l1 ]( J
  34. printf("**************************************************\r\n");) z  `8 i' v! d& P+ o
  35. printf("**************************************************\r\n");
      ^- _  O8 I! O9 S2 u4 W+ f
  36. /* 计算CFFT */* _2 C6 A/ g: h! r+ Y: ^) n; U
  37. arm_cfft_radix2_q31(&S, testInput_radix2_q31_50hz);
    ' w8 _& s, U( D  f) u
  38. /* 计算模值 */# s7 w  }& `. e0 _" j
  39. arm_cmplx_mag_q31(testInput_radix2_q31_50hz, testOutputQ31, fftSize);
    # j4 C3 x* o3 }; J
  40. /* 串口打印求解的模值 */
    % @, W, u" L; q6 j* p
  41. for(i=0; i<1024; i++)" C& T" ], ~; k. S! G* v
  42. {
    , z/ d, e' d, G9 j/ [/ w9 J
  43. printf("%d\r\n", testOutputQ31[i]);
    2 O: \3 y- h/ T4 N. _
  44. }' {0 a3 E1 Q$ u( N! Y1 {0 Z
  45. }
复制代码
运行如上函数可以通过串口打印出原始信号和计算的模值,下面我们就通过Matlab计算的模值跟arm_cfft_radix2_q31计算的模值做对比。
    对比前需要先将串口打印出的数据加载到Matlab中,原始信号起名signal,函数arm_cmplx_mag_q31计算的模值起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1024;              % 采样率
N  = 1024;             % 采样点数
n  = 0:N-1;             % 采样序列
f = n * Fs / N;           %真实的频率

5 u' c: c2 H* |  ]
: F( A; O  Z: I; w3 @' }  f0 K2 y  w
y = fft(signal, N);        %对原始信号做FFT变换

% T$ G8 b7 C+ O6 t1 t. K
) S  p/ p4 Q  |1 N/ u* g
subplot(2,1,2);
plot(f, abs(y));           %绘制幅频相应曲线
title('Matlab计算结果');
xlabel('频率');
ylabel('幅度');
2 t) I" @. V; p% O) l
$ [& O/ w, c1 H3 Z6 O* V3 t) J
subplot(2,1,1);
plot(f,  sampledata);      %绘制幅频相应曲线
title('复数FFT计算结果');
xlabel('频率');
ylabel('幅度');
  h" |  z8 ~! r
0 A0 E* Y5 Q" h% e1 s! R' F
Matlab运行结果如下:
30.2.png
从上面的对比结果中可以看出,Matlab和函数arm_cfft_radix2_q31计算的频率点基本是一致的,而幅值大小不一样是因为调用函数arm_cmplx_mag_q31对数据结果做了移位处理。
6 X. G* T8 m1 `& J2 X: {
30.2.3 arm_cfft_radix2_q15
函数定义:
    void arm_cfft_radix2_q15(
        const  arm_cfft_radix2_instance_q15 * S,
        q15_t  * pSrc)
参数定义:
    [in]      *S    points to an instance of the fixed-point CFFT/CIFFT structure.  
    [in, out] *pSrc  points to the complex data buffer of size <code>2*fftLen</code>. Processing occurs in-place.  
注意事项:
结构const  arm_cfft_radix2_instance_q15的定义如下(在文件arm_math.h文件):
      typedef struct
      {
         uint16_t fftLen;               
          uint8_t ifftFlag;               
          uint8_t bitReverseFlag;         
          q15_t *pTwiddle;                    
      uint16_t *pBitRevTable;         
      uint16_t twidCoefModifier;      
          uint16_t bitRevFactor;         
      } arm_cfft_radix2_instance_q15;

8 c6 a$ G8 l6 d. l  d, `5 G/ [
    下面通过在开发板上运行这个函数并计算幅频相应,然后再与Matlab计算的结果做对比。
  1. q15_t testInput_radix2_q15_50hz[TEST_LENGTH_SAMPLES];
    2 w7 Y' |' }5 S8 V2 t
  2. q15_t testOutputQ15[TEST_LENGTH_SAMPLES/2];
    0 o$ Y$ m+ {: @. A2 m% r0 \
  3. * a' {: J$ Y% J2 J& L5 @$ V& [
  4. /*
    - i( [# w' J* ^3 k
  5. *********************************************************************************************************% `9 @8 \) n' p* W0 Y) g: H1 }* q( o
  6. *        函 数 名: arm_cfft_radix2_q15_app& s! T& O! ^9 Y
  7. *        功能说明: 调用函数arm_cfft_radix2_q15计算幅频。
    ) C6 a; M& g# o7 }3 R: p( |
  8. *        形    参:无
    8 L0 D1 D  b1 D! Q; M' S
  9. *        返 回 值: 无6 N% h! ?9 B+ y1 ]" O+ W# Q
  10. *********************************************************************************************************
    + v( B0 I0 m; {. X- j* V6 o
  11. */
    1 ?3 f: s. ]& q5 v4 q9 D  }
  12. void arm_cfft_radix2_q15_app(void)4 Y, K" T2 _+ H$ I9 s2 O$ X
  13. {" e( i, ~" v/ v
  14. uint16_t i,j;: [  u0 x: }6 E: s9 J
  15. arm_cfft_radix2_instance_q15 S;
    5 ?( j% @; P$ B- V! p
  16. fftSize = 1024;
    ' D- y# Y0 W# w7 t
  17.     ifftFlag = 0; ( _, k  K9 E8 e1 K9 g& x) O
  18.     doBitReverse = 1;
    9 G8 d; H9 L  ]( C! _7 t
  19. /* 初始化结构S */, K7 L0 [6 F. u3 X* S* s+ \3 x
  20. arm_cfft_radix2_init_q15(&S, fftSize, ifftFlag, doBitReverse);
    % Y& D# c7 i1 }8 V3 D2 v# @8 \
  21. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    ) A) X& y9 R6 r4 l! y' [0 t
  22. for(i=0; i<1024; i++)
    1 @' F0 C% j: s4 p, x, N
  23. {
    5 r: Q6 x8 a, B" o
  24. /* 虚部全部置0 */8 b* k. w1 C: D% g, C
  25. testInput_radix2_q15_50hz[i*2+1] = 0;' t5 q6 ^$ T0 p
  26. /* 51.2Hz正弦波,采样率1024Hz。
    ' T# b6 R/ M  d3 q- }2 ?
  27.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,; x3 }1 E' u: q7 p/ r
  28.    32768 / 20 = 1638.4
    2 p+ |- r9 q# s6 q9 h
  29. */
    * f& G! V$ [, l
  30. j = i % 20;  d; y) c  e, ?' o" D
  31.           testInput_radix2_q15_50hz[i*2] = arm_sin_q15(1638*j);
    0 {5 a$ i  z. u: _6 X" r4 N
  32.           printf("%d\r\n", testInput_radix2_q15_50hz[i*2]);
    : ?& N# s0 E- _5 i
  33. }
    $ N, W7 U1 g5 J9 ?( @$ h' x+ t
  34. /* 输出结果分割线 */  B0 C- O+ E9 K
  35. printf("**************************************************\r\n");
    1 E9 E& n6 [) J
  36. printf("**************************************************\r\n");
    - E: n. |& I% k  R( E0 q
  37. /* 计算CFFT */( b- [9 F1 Z# g) Y9 H6 v5 A) `
  38. arm_cfft_radix2_q15(&S, testInput_radix2_q15_50hz);
    ! l1 r9 V/ J2 C& ~0 D& t
  39. /* 计算模值 */
      o1 l# K) N! [) u) a
  40. arm_cmplx_mag_q15(testInput_radix2_q15_50hz, testOutputQ15, fftSize);
    ' ~+ D& m+ l' ?
  41. /* 串口打印求解的模值 */9 o% ?6 t8 l9 D1 I6 |5 H0 a4 v( J
  42. for(i=0; i<1024; i++)
    5 c9 V( O$ u1 T& B! }6 |5 y
  43. {
    4 p$ J- y9 {! r' e6 ?
  44. printf("%d\r\n", testOutputQ15[i]);5 U  R" C3 o+ Q
  45. }  m4 p4 e9 m) U1 R
  46. }
复制代码
运行如上函数可以通过串口打印出原始信号和计算的模值,下面我们就通过Matlab计算的模值跟arm_cfft_radix2_q15计算的模值做对比。
    对比前需要先将串口打印出的数据加载到Matlab中,原始信号起名signal,函数arm_cmplx_mag_q15计算的模值起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1024;                     % 采样率
N  = 1024;                     % 采样点数
n  = 0:N-1;                     % 采样序列
f = n * Fs / N;                %真实的频率
# L3 v( ~# d% U+ f1 v# u( Q
% a( \+ O2 Z7 r0 y1 F; g! q
y = fft(signal, N);           %对原始信号做FFT变换
* |+ `1 [1 P" [. e4 u+ e/ }

2 G9 i! Q* ^. v* t; U+ m4 C5 n- ?
subplot(2,1,2);
plot(f, abs(y));           %绘制幅频相应曲线
title('Matlab计算结果');
xlabel('频率');
ylabel('幅度');
' K8 z4 R: y! W

' N9 c% J4 u; F9 [5 ?
subplot(2,1,1);
plot(f,  sampledata);      %绘制幅频相应曲线
title('复数FFT计算结果');
xlabel('频率');
ylabel('幅度');

9 G5 w5 z. e* Q: W! r$ H& x' M, V2 Q% M9 I6 M  C: Q* {
Matlab运行结果如下:
30.3.png
从上面的对比结果中可以看出,Matlab和函数arm_cfft_radix2_q15计算的频率点基本是一致的,而幅值大小不一样是因为调用函数arm_cmplx_mag_q15对数据结果做了移位处理。
; C% z5 |7 M+ g+ C' D' D
baiyongbin2009 回答时间:2015-4-15 10:53:06
30.3 复数FFT基4算法
    如果希望直接调用FFT程序计算IFFT,可以用下面的方法:
30.3.1 arm_cfft_radix4_f32
    此函数已经不推荐使用,后面的版本会被删除,故不做介绍。
, Z  e/ {  {! U5 f" n& X
30.3.2 arm_cfft_radix4_q31
函数定义如下:
    void arm_cfft_radix4_q31(
    const arm_cfft_radix4_instance_q31 * S,
    q31_t * pSrc)
参数定义:
    [in]      *S    points to an instance of the fixed-point CFFT/CIFFT structure.  
    [in, out] *pSrc  points to the complex data buffer of size <code>2*fftLen</code>. Processing occurs in-place.
注意事项:
1. 结构const  arm_cfft_radix4_instance_q31的定义如下(在文件arm_math.h文件):
      typedef struct
      {
          uint16_t fftLen;               
          uint8_t ifftFlag;               
          uint8_t bitReverseFlag;         
          q31_t *pTwiddle;                 
          uint16_t *pBitRevTable;         
          uint16_t twidCoefModifier;      
          uint16_t bitRevFactor;         
       } arm_cfft_radix4_instance_q31;
2. 为了防止数据饱和,每次蝶形运行的结果都要除以2,故不同的长度的FFT运算的最终结果输出格式不同。具体信息如下:
    Q31 CFFT
30.4.png
    Q31 CIFFT
30.5.png
    下面通过在开发板上运行这个函数并计算幅频相应,然后再与Matlab计算的结果做对比。
  1. q31_t testInput_radix4_q31_50hz[TEST_LENGTH_SAMPLES];
    . d- N* ^; `6 l! ?5 Y) q$ k
  2. /*7 F# Q2 P" J1 [! f* c: d3 T& q
  3. *********************************************************************************************************  J+ [% J6 Z. \& j: N
  4. *        函 数 名: arm_cfft_radix4_q31_app/ ?2 m/ |5 z" I' C. _4 m
  5. *        功能说明: 调用函数arm_cfft_radix4_q31_app计算幅频。
    5 y2 `) Z% Z' N( }( w' `7 a; h! Z
  6. *        形    参:无0 Q, `1 L* h6 o# t+ Z+ R& W
  7. *        返 回 值: 无' r/ H& u; V8 ^) _7 e2 Z2 o
  8. *********************************************************************************************************1 q1 `5 k0 _% W. y" X* D% j( n
  9. */
    ; A( V3 W9 [! c9 _1 ~3 T6 ~: B
  10. void arm_cfft_radix4_q31_app(void)
    7 D. h& p, ?+ r9 ?
  11. {
      b4 j) l% S4 ~& J/ x: y
  12. uint16_t i,j;6 C" s' u' l8 o* e  H" `) s
  13. arm_cfft_radix4_instance_q31 S;
    1 v6 y, @& _; e7 J, f
  14. fftSize = 1024; 0 z0 R3 }2 n/ ~0 s$ B! r
  15.     ifftFlag = 0; 7 S- a! u7 c9 K* D- z- }3 \
  16.     doBitReverse = 1; " S/ ^& W+ @4 B: V  X& |
  17. /* 初始化结构S */7 B& `! [: z( g/ e7 {) b* V
  18. arm_cfft_radix4_init_q31(&S, fftSize, ifftFlag, doBitReverse);
    ' J/ a' e3 N7 b1 G! B1 ^
  19. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */
    4 ^! E/ ]: O) P4 p. @
  20. for(i=0; i<1024; i++)1 _3 X% \7 i% k' G  e' }! h: ~
  21. {
    : y2 S5 z# A/ w/ D1 p
  22. testInput_radix4_q31_50hz[i*2+1] = 0;: E) q' B' S, D: y$ L' T5 O
  23. /* 51.2Hz正弦波,采样率1024Hz。
    ! R4 F  ]% H- v; ~
  24.    arm_sin_q31输入参数的范围0-2^31, 这里每20次为一个完整的正弦波,! ~4 L4 ^: i9 t+ i8 i; Q9 q& G* P
  25.    2^31 / 20 = 107374182.43 a. ^' o! f0 c( L& l9 |$ |( o+ T
  26. */
    ' u, v, z/ N$ o. N( v
  27. j = i % 20;) P; _* ?0 ]0 @& B
  28.           testInput_radix4_q31_50hz[i*2] = arm_sin_q31(107374182*j);
    : {; l& p9 L" X9 L; x- b; ^
  29.           printf("%d\r\n", testInput_radix4_q31_50hz[i*2]);
    5 g/ Z/ Q! a% L3 C! }
  30. }
    # u! B! ~9 u; J0 a- `5 n
  31. /* 输出结果分割线 */
    3 |! s" h8 U6 r! x
  32. printf("**************************************************\r\n");0 ]+ o( J! z2 c) _/ P
  33. printf("**************************************************\r\n");8 y! `7 Y4 P: c" m0 d/ t. I; i
  34. /* 计算CFFT */
    + t$ c/ N( Y) y
  35. arm_cfft_radix4_q31(&S, testInput_radix4_q31_50hz);. a% c& Q3 N; l4 M% V. H: T
  36. /* 计算模值 */1 e* R8 Y' |4 E. j
  37. arm_cmplx_mag_q31(testInput_radix4_q31_50hz, testOutputQ31, fftSize);
    7 l: r8 @8 ~' y( B
  38. /* 串口打印求解的模值 */1 c+ @8 g8 b! C3 L2 G+ m% V! z
  39. for(i=0; i<1024; i++)
    & f8 }% V' J$ ~' G
  40. {
    ; I. H8 p8 S5 [' l
  41. printf("%d\r\n", testOutputQ31[i]);
    2 O( _1 u. Z% e
  42. }
    1 [1 ^4 J& L1 W% k
  43. }
复制代码
运行如上函数可以通过串口打印出原始信号和计算的模值,下面我们就通过Matlab计算的模值跟arm_cfft_radix4_q31计算的模值做对比。
    对比前需要先将串口打印出的数据加载到Matlab中,原始信号起名signal,函数arm_cmplx_mag_q31计算的模值起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1024;              % 采样率
N  = 1024;             % 采样点数
n  = 0:N-1;             % 采样序列
f = n * Fs / N;           %真实的频率
. f. Q+ O: n4 f6 N6 J

9 L9 V/ N. b. T- c8 H
y = fft(signal, N);        %对原始信号做FFT变换

. a" h, ~5 q1 c8 r% M( n4 |( }) y, U
subplot(2,1,2);
plot(f, abs(y));           %绘制幅频相应曲线
title('Matlab计算结果');
xlabel('频率');
ylabel('幅度');
  e9 D) q: w# u3 P

3 ?( `. M$ B& s
subplot(2,1,1);
plot(f,  sampledata);      %绘制幅频相应曲线
title('复数FFT计算结果');
xlabel('频率');
ylabel('幅度');

" `" w7 w. V6 y& z2 i0 L# x2 C  A3 R9 W( A* w* R9 P8 d
Matlab运行结果如下:
30.6.png
从上面的对比结果中可以看出,Matlb和函数arm_cfft_radix4_q31计算的频率点基本是一致的,而幅值大小不一样是因为调用函数arm_cmplx_mag_q31和arm_cfft_radix4_q31对数据结果做了移位处理。
, A# h& Y* w  m
30.3.3 arm_cfft_radix4_q15
函数定义:
    void arm_cfft_radix4_q15(
        const  arm_cfft_radix4_instance_q15 * S,
        q15_t  * pSrc)
参数定义:
    [in]      *S    points to an instance of the fixed-point CFFT/CIFFT structure.  
    [in, out] *pSrc  points to the complex data buffer of size <code>2*fftLen</code>. Processing occurs in-place.  
注意事项:
1. 结构const  arm_cfft_radix4_instance_q15的定义如下(在文件arm_math.h文件):
      typedef struct
      {
          uint16_t fftLen;               
          uint8_t ifftFlag;               
          uint8_t bitReverseFlag;         
          q15_t *pTwiddle;                    
      uint16_t *pBitRevTable;         
      uint16_t twidCoefModifier;      
          uint16_t bitRevFactor;         
      } arm_cfft_radix4_instance_q15;
2. 为了防止数据饱和,每次蝶形运行的结果都要除以2,故不同的长度的FFT运算的最终结果输出格式不同。具体信息如下:
    Q15 CFFT
30.7.png
    Q15 CIFFT
30.8.png
    下面通过在开发板上运行这个函数并计算幅频相应,然后再与Matlab计算的结果做对比。
  1. q15_t testInput_radix4_q15_50hz[TEST_LENGTH_SAMPLES];
    3 t/ o& J' [. r9 y
  2. /*- Q, h: e2 h1 `8 K, w. e  {% }
  3. *********************************************************************************************************
    3 w4 n( n. H% x6 T/ c2 y  v" g8 G
  4. *        函 数 名: arm_cfft_radix4_q15_app
    * G7 k  l* V& k5 ?; ]
  5. *        功能说明: 调用函数arm_cfft_radix4_q15计算幅频。
    2 c: k$ R9 Q( |4 r" M
  6. *        形    参:无
    9 P# Z# V2 m- x6 }" w! O- X
  7. *        返 回 值: 无9 u  R8 X# c8 l
  8. *********************************************************************************************************
    9 T  N' W% |! [: f! }  i
  9. */$ o) i5 N' ~3 {& l9 |2 R
  10. void arm_cfft_radix4_q15_app(void)$ F2 y( n0 U+ P/ P- D
  11. {
    6 x' o, W1 Y7 L
  12. uint16_t i,j;
    * O/ f1 o0 m5 ?% s
  13. arm_cfft_radix4_instance_q15 S;( i/ M. U4 Q* d8 D' A" v
  14. fftSize = 1024; 8 e( u$ H- a3 K, d8 z
  15.     ifftFlag = 0; 2 M( @0 w' P/ t7 g0 p
  16.     doBitReverse = 1;
    ' c) L& n) F: I2 Q$ f6 J' D: W7 W
  17. /* 初始化结构S */5 G6 {& Y; G5 J9 a+ _; ^3 H: G& s
  18. arm_cfft_radix4_init_q15(&S, fftSize, ifftFlag, doBitReverse);; A+ d# l7 K" \- M
  19. /* 按照实部,虚部,实部,虚部..... 的顺序存储数据 */6 f: A* |4 r- F8 c% L7 ^
  20. for(i=0; i<1024; i++)( U' t$ @# q8 L' {
  21. {- y0 H9 Y5 h: V% K# V" g5 m* m3 j
  22. /* 虚部全部置0 */+ L* A1 j1 I5 K
  23. testInput_radix4_q15_50hz[i*2+1] = 0;
    4 k* _4 C0 ]- D
  24. /* 51.2Hz正弦波,采样率1024Hz。
    ' _6 y5 ?" i- B
  25.    arm_sin_q15输入参数的范围[0, 32768), 这里每20次为一个完整的正弦波,
    , v# G( g6 J+ x
  26.    32768 / 20 = 1638.4' X! ]# M0 n& d& d- f. \2 i
  27. */4 P1 M  G0 |# m% z  ?1 J0 r* m
  28. j = i % 20;
    ! N  u, ^, x3 s
  29.           testInput_radix4_q15_50hz[i*2] = arm_sin_q15(1638*j);
    , B# L; b- D1 r8 X$ {
  30.           printf("%d\r\n", testInput_radix4_q15_50hz[i*2]);* [( E+ k+ D9 b& {1 `7 m! d" V
  31. }
    $ e! e& Z' O) N( Q' o. ^' J
  32. /* 输出结果分割线 */3 @- Z% a9 }7 {$ w! o. d5 p0 s
  33. printf("**************************************************\r\n");/ |. K1 ?& h& j- l
  34. printf("**************************************************\r\n");+ P9 W9 x+ _: |1 A' ~5 d
  35. /* 计算CFFT */
    2 m0 r7 a1 U) ^
  36. arm_cfft_radix4_q15(&S, testInput_radix4_q15_50hz);" \% j% T, n. J4 f# z* w8 a
  37. /* 计算模值 */. m" M$ \5 y8 i& T9 ]
  38. arm_cmplx_mag_q15(testInput_radix4_q15_50hz, testOutputQ15, fftSize);! [4 S% n% T" w
  39. /* 串口打印求解的模值 */
      J' X) _1 [: y9 l
  40. for(i=0; i<1024; i++)
    5 U; @$ i$ K$ ?8 B' a7 N5 j$ }2 L
  41. {
    2 \( g3 [, z' @9 W) e/ b
  42. printf("%d\r\n", testOutputQ15[i]);
    - q, X. F9 v* S$ k% M5 ~" n' O
  43. }4 v2 C$ h& P3 U
  44. }
复制代码
运行如上函数可以通过串口打印出原始信号和计算的模值,下面我们就通过Matlab计算的模值跟arm_cfft_radix4_q15计算的模值做对比。
    对比前需要先将串口打印出的数据加载到Matlab中,原始信号起名signal,函数arm_cmplx_mag_q15计算的模值起名sampledata,加载方法在前面的教程中已经讲解过,这里不做赘述了。Matlab中运行的代码如下:
Fs = 1024;                     % 采样率
N  = 1024;                     % 采样点数
n  = 0:N-1;                     % 采样序列
f = n * Fs / N;                %真实的频率

& D0 E; e1 a' [; @- f  J! d) {3 R3 C, U+ T
y = fft(signal, N);           %对原始信号做FFT变换

" F9 c3 {; C  {4 S
* P+ R5 q9 T4 Y  o' k
subplot(2,1,2);
plot(f, abs(y));           %绘制幅频相应曲线
title('Matlab计算结果');
xlabel('频率');
ylabel('幅度');
8 `0 i1 @0 Y- m' K- L+ S
& w$ `% V) b. x0 f! N9 C, I
subplot(2,1,1);
plot(f,  sampledata);      %绘制幅频相应曲线
title('复数FFT计算结果');
xlabel('频率');
ylabel('幅度');

, Q$ b& s" X) N4 B: X4 `+ @9 N) t4 a+ f* z6 N' Q( Y( S  u
Matlab运行结果如下:
30.9.png
从上面的对比结果中可以看出,Matlab和函数arm_cfft_radix4_q15计算的频率点基本是一致的,而幅值大小不一样是因为调用函数arm_cmplx_mag_q15和arm_cfft_radix4_q15对数据结果做了移位处理。

1 }& u' V) W. l( y  T- F7 u
baiyongbin2009 回答时间:2015-4-15 10:54:02
30.4 总结
    本章节内容比较多,有兴趣的可以深入了解源码的实现。
" Z; J' v& ~; v; n, `
wamcncn 回答时间:2015-4-15 12:32:03
标记下学习
拼命三郎 回答时间:2015-4-15 13:22:16
ddddd.png
拼命三郎 回答时间:2015-4-15 13:22:32
stm.jpg
拼命三郎 回答时间:2015-4-15 13:22:51
stm (1).jpg
木木鱼 回答时间:2015-4-15 23:39:53
mark一下!

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版