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

基于STM32的FFT频谱分析+波形识别

[复制链接]
STMCU小助手 发布时间:2023-2-5 17:05
一.硬件部分
3 |! d3 w0 i$ t信号发生器,正点原子精英板,3.5’TFTLCD,两根杜邦线(接PC1和GND)
: u1 a" }& v" p; v8 c- t' f3 i) O8 d: \5 C- n) I2 e, }
二.基本思路
0 w4 u4 G9 z! Y6 ?& L, R1.使用ADC采集音频信号
: L/ {7 i6 z) m) q1 [; Y1 C0 }2.使用官方提供的FFT函数(1024点)对采集到的信号进行处理
$ n1 a$ r8 b; }4 {" v$ P* }' A% l3.量化、频谱图显示
9 S+ I: Y/ z- l& ]! G0 Q. c, W( y采样频率:Fs = 2400Hz(触摸版本可以根据实际情况调整)
' {* m5 H0 M2 K" X$ k, I. z! g样本数量:NPT = 1024
6 f7 F! @; T  Z+ v8 ~# E3 z% H  I6 B' q2 I
三.程序编写3 e$ N) E, n, j0 C% {. C/ e
1.ADC采样# z3 K3 C$ c+ Y$ [2 h4 D
这次用到的是ADC的DMA传输,这可以减少对程序的占用。这里着重关注这几行代码:6 z; d1 x+ B2 A. ]) J" u
  1. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1 工作在独立模式$ h. I, V6 h; p% K4 Y! f* c3 b# u
  2. ADC_InitStructure.ADC_ScanConvMode =DISABLE; //模数转换工作在非扫描模式, G" {/ {6 Z$ p, B- a
  3. ADC_InitStructure.ADC_ContinuousConvMode =DISABLE; //模数转换工作在不连续转换模式
    $ \! w% k% s4 R0 H$ B: i
  4. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //Timer1触发转换开启(定时器T1的CC1通道,控制采样频率)
复制代码
) S9 y- L  \0 W% Z; c2 L/ \1 M( T
假设有ADC1有四个通道需要转换(本例中只有一个通道):, |, [' G) Q$ D, v+ o

3 E* f& _4 U* _2 J) |6 [
PUM1575JSKKW8W6VPL0@{ZF.png ) x. ]6 Y# U1 T0 d' @

! u$ o! ~# ^1 l( v) _& X9 [$ Z+ V我的代码就是按这个写的,不这样写应该也可以,不过我认为还是要多注意细节,不然根本不知道问题出在哪儿。
! Y% Z2 E3 ^& [9 [+ F* ~
  1. DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA 外设 ADC 基地址& A7 t6 s7 {( U- |4 {; |0 U1 x# `
  2. DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value; //DMA 内存基地址
复制代码

) b- v' `+ m0 K  o! @DMA传输注意这两句,存放地址。, X* f) y" [8 K' a" z

) ^& e' c" R0 C  T3 B1 Q2.FFT频谱分析: J( {. |/ }$ E) S9 m6 m0 p
先下载一个FFT官方库,添加这几个文件
3 p- Z8 ~7 l( H9 N* H
4 f2 m) x, p) I" @. @- O8 c
20190710113714855.png
7 A6 P0 k* d& e% i
; F, y5 l. I- B4 {9 O& r$ u
主要就是这几个函数
, }3 f0 A9 r% R- s2 B  c4 B0 B
  1. for(i=0;i<NPT;i++)# Z# ]3 x. N2 @
  2.   {9 E8 ]9 e/ B# ?4 K5 }
  3.     lBufInArray[i]=ADC_Value[i]<<16;
    ! J3 s" H4 J8 C; o' b+ B
  4.   }
    " c) K4 R" W2 M) d
  5.   cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);   
    . `1 b; a# U# r( P2 t9 X) X
  6.   GetPowerMag();% e$ i+ @6 h2 \* t, T. j: l4 z
  7. /******************************************************************
    . c- x$ w& G; q0 x3 `3 x. F7 j3 K
  8. 函数名称:GetPowerMag()% ~- g8 V& F0 a! U1 b, ?& w, |
  9. 函数功能:计算各次谐波幅值  [short 的范围,是-32767 到 32767 。也就是 -(2^15 - 1)到(2^15 - 1)。]: K# ~9 D) t( P6 X+ A" n1 o1 W
  10. 参数说明:+ C) @) Y" y  F. B% h! |5 c3 D
  11. 备  注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)
      P! U6 e/ H' z  j3 @
  12. *******************************************************************/
    ) g7 G2 p5 C( R3 W  j7 f) a
  13. void GetPowerMag(void)! d# Z, z) Z  G! u! D
  14. {
    / \8 V) p2 v2 `$ [
  15.     signed short lX,lY;                                                  //算频率的话Fn=i*Fs/NPT  //由于此处i是从0开始的,所以不需要再减1
    9 B  N; N' O! |( M7 P9 @* \: `/ \
  16.     float X,Y,Mag;                                                      # S: |9 e- Y: F2 A$ P
  17.     unsigned short i;
    - a( f. I) ]" d5 ^6 l% L8 g6 Y
  18.     for(i=0; i<NPT/2; i++)                                                  //经过FFT后,每个频率点处的真实幅值  A0=lBufOutArray[0]/NPT
    4 v# P! Y( v) p/ e  v) I, d
  19.     {                                                                       //                                 Ai=lBufOutArray[i]*2/NPT
    0 d9 s: u3 O1 _+ [. G
  20.         lX  = (lBufOutArray[i] << 16) >> 16;  //lX  = lBufOutArray[i];
    # V) x# x" j9 Z( q& W- R
  21.         lY  = (lBufOutArray[i] >> 16);
    ) [6 A3 Z( o+ C, e
  22.                                     0 E% u9 D1 l1 G. T$ U# X; Y) D) R
  23.         X = NPT * ((float)lX) / 32768;//除以32768再乘65536是为了符合浮点数计算规律,不管他) z& b0 f4 |3 k1 m' S
  24.         Y = NPT * ((float)lY) / 32768;& }6 V' |$ j$ J& r% @2 y$ o% ~- g
  25.         Mag = sqrt(X * X + Y * Y) / NPT;
    5 ?; ?' W( ^. G
  26.         if(i == 0)
    2 Q0 v0 l6 r- w- m5 p9 K3 {7 D" v
  27.             lBufMagArray[i] = (unsigned long)(Mag * 32768);   //0Hz是直流分量,直流分量不需要乘以2
    # D7 N- T4 T* z, z3 ?
  28.         else
    5 L% W1 T( U* w+ l0 [; C# f
  29.             lBufMagArray[i] = (unsigned long)(Mag * 65536);
    + Z$ v1 g1 d0 M8 G
  30.     }: s. L3 g( o' `0 N  E
  31. }
复制代码

% z/ m' l5 z5 A% Y; G需要说明的是:按照FFT官方库的说明,lBufOutArray和lBufInArray都必须是32位的数据类型,其中高16位存储实部,低16位存储虚部。因为信号发生器输出的不可能是虚数,所以对于lBufInArray来说,低16位存储的虚部总是为0。上面的代码,主要就是计算各次谐波的幅值,就是把虚部和实部取出来平方开根号。$ Z& Q9 C. B9 D8 q& D
  1. void lcd_show_fft(unsigned int *p)- p2 Y. o5 E2 `9 C4 s3 q2 g
  2. {
    4 {% x: d) F) X& |; O2 v
  3.    unsigned int *pp = p+1;             //p+1相当于我直接把0HZ部分滤掉了7 j7 h4 i+ E/ z6 x
  4.    unsigned int i = 0;
    6 V/ n5 c' m( {. c. @' @
  5.    for(i = 0;i<480;i++)
    8 h. c3 k9 ?. u
  6.    {; m$ W7 ?8 I2 y5 W; d* `
  7.       LCD_Fill(0,        i, *pp*0.11, (i+1), WHITE);     //有效部分白色       ; G! e6 J, k- |! ?/ x7 L
  8.       LCD_Fill(*pp*0.11, i, 270,       (i+1), BLACK);   //其他就黑色
    # ~- K: A. J( c/ m* m- c- m2 k' i
  9.       pp++;
    . l+ t9 y& A& u. ]. g! q
  10.    }9 J5 v7 g0 h  L2 X* }2 z/ o
  11. }
复制代码
* ~2 N; d( I$ a2 {3 v1 X
显示的函数也没什么可讲的,注意不要用画直线的函数,刷屏很慢。
, z$ \5 r& h! V2 o9 g( V  `# o: B- e& O4 f

/ Y% \; G7 r; ^, G3.波形识别0 R2 _9 k7 [/ C6 U1 U9 V% p& ^) E
先说说我的思路,这个思路肯定不是最好的,我们先来看看各个波形的特点:) M6 _* ~+ O6 m" [
5 b- s  y" F9 Y4 f. r9 Z
20190710143438516.png
+ J- z4 J- d# J6 n% e
5 y' a) f' _. a2 k" f; C- i
20190710143521584.png
* e/ [! Y% \! _' \) I
3 J! a  V. I% E' d# R正弦波:只有基波分量,基本无谐波分量,没什么好说的。
$ Z2 U: Q- [3 y5 ]7 ^1 W, a. E% Y方波:除了基波,还有3,5,7次谐波分量,且3次谐波分量为基波分量的1/3.
" p- v# r- ]( e& N3 }三角波:除了基波,还有3,5,7次谐波分量,但3次谐波分量为基波分量的1/9.7 {# M) j3 j# r# c, S% p/ [
锯齿波:除了基波,还有2,3,4次谐波分量.
0 O1 @* }+ h2 D7 V0 e4 \

* f" `3 p( c1 v/ [0 Q
  1. /***********************************************8 {- v* @& |. W  K& y# \
  2. 找最大值,次大值……对应的频率,分析波形
    2 c% w1 p$ l" P/ A0 `' n4 B
  3. *************************************************/
    # W6 U: }2 u; K" x
  4. void select_max(float *f,float *a)
    2 B' s5 ^# w7 q
  5. {: i! r& T2 `6 K) T' l- g
  6.    int i,j;
    * [* L$ A2 {" }) b: ^, h+ t
  7.    float k,k1,m;
    ( }9 H. T- m4 @5 k8 j
  8.     float aMax =0.0,aSecondMax = 0.0,aThirdMax = 0.0,aFourthMax=0.0;: q4 _; x! l2 x/ b& r
  9.     float fMax =0.0,fSecondMax = 0.0,fThirdMax = 0.0,fFourthMax=0.0;5 w+ T+ R1 N/ K5 d9 y: ]
  10.    int nMax=0,nSecondMax=0,nThirdMax=0,nFourthMax=0;
    2 I. I" j$ `; a0 m2 r
  11.    for ( i = 1; i < NPT/2; i++)//i必须是1,是0的话,会把直流分量加进去!!!!# X3 n3 I+ D8 s# a6 _6 w" _# F
  12.     {- x' N$ r4 [6 F6 Q
  13.         if (a[i]>aMax)
    ( g% @5 I, T+ S
  14.         {  A, x; u1 v8 Q) b* b" `
  15.             aMax = a[i];
    6 d% H! Z" b3 p; E6 g, O7 x+ S
  16.        nMax=i;8 C: y9 e/ i$ M2 l  h( \# f0 e
  17.        fMax=f[nMax];
    7 P+ R- t6 n" N% O5 y8 r% F
  18.         }" h9 l# @# Q3 v
  19.     }
    ; M# p5 g1 {9 D3 i
  20.   for ( i=1; i < NPT/2; i++)
    % x- m6 X. f  d/ k' x
  21.     {
    $ A( r4 c% O  r: S
  22.     if (nMax == i)" M2 y- Q. E( S  r% p
  23.     {
    3 \; t" U! V" m  V
  24.       continue;//跳过原来最大值的下标,直接开始i+1的循环
    2 O3 s& k+ s, {1 v( x0 P- J# l. y6 x
  25.     }
    - F/ l: d* N' U7 f8 j. o
  26.         if (a[i]>aSecondMax&&a[i]>a[i+1]&&a[i]>a[i-1])- j$ s* V- B/ |; X! j$ Y" H
  27.         {
    ' r4 w, Z) b, c8 t9 ~7 S# A0 `
  28.             aSecondMax = a[i]; ) |2 s  }; [  [
  29.        nSecondMax=i;( W% V+ ^: P  G6 V$ Z8 ?& a7 A
  30.        fSecondMax=f[nSecondMax];) k. _6 Q0 H3 u+ r3 u
  31.         }
    - s% }, _+ N" u, W. v
  32.     }
    1 e& R+ i' c- {# a3 E* q* u% j
  33.   for ( i=1; i < NPT/2; i++)
    4 t7 n7 A2 V0 ~( f, b
  34.     {. {; b0 X7 x+ B2 K1 @% M
  35.     if (nMax == i||nSecondMax==i)
    " M0 E* R2 E1 G* {
  36.     {8 h! d0 `% z+ g! J$ i2 {
  37.       continue;//跳过原来最大值的下标,直接开始i+1的循环
    9 n$ ]# u) Q1 q5 S1 b
  38.     }" W; N; n% j( G) c/ H
  39.         if (a[i]>aThirdMax&&a[i]>a[i+1]&&a[i]>a[i-1])
    8 X( ~7 }" N- m" @
  40.         {
    ) W6 }) R9 w1 J: a; J& A; N' m, y1 K
  41.             aThirdMax = a[i];
    ' e5 j* s# ^! D. _* ?+ M1 M
  42.        nThirdMax=i;: ~- a  R" W& y
  43.        fThirdMax=f[nThirdMax];2 ?0 K: M% _: F- `3 w% ^
  44.         }4 E8 i7 H8 C& g& l
  45.     }  R% i) ?4 |! g; p, k7 c" g
  46.   for ( i=1; i < NPT/2; i++)
    + v+ F; _$ u) y3 _( l  K5 M# E
  47.     {1 K$ Y1 f: g: O% `" U" a5 a5 e
  48.     if (nMax == i||nSecondMax==i||nThirdMax==i)7 f/ H1 \& g0 j" b1 `# ]
  49.     {
    # d! O3 V* S: q7 H( g
  50.       continue;//跳过原来最大值的下标,直接开始i+1的循环
    ( W/ W" {9 ?5 i! l  ^, Q( J
  51.     }
    9 _2 d6 k, m+ ~" a5 x4 {- Y
  52.         if (a[i]>aFourthMax&&a[i]>a[i+1]&&a[i]>a[i-1])
    - p: \% F6 {0 ~$ x8 P3 z1 k
  53.         {
    9 B% j: J3 u, \3 N4 z' h# W, ?! Q. O
  54.             aFourthMax = a[i];
    ( Z9 I) W- C1 s, m% t8 l
  55.        nFourthMax=i;
    % c+ A' r4 P, p4 J: R) _( h* w
  56.        fFourthMax=f[nFourthMax];
    ; \" \+ D' @5 y, b1 o
  57.         }
    6 S& c6 I, v( A- m7 Z, U) d$ ?0 }8 f  _
  58.     }0 C' u: b  |: K7 A. S/ o
  59.   k=fabs(2*fMax-fSecondMax);* C; u/ m; |6 ^
  60.   k1=fabs(3*fMax-fSecondMax);
    9 V7 m3 X" [  R3 T2 l& [
  61.   m=fabs((float)(aMax-3.0*aSecondMax));   x% G/ w% ~/ F( a6 g& K3 m
  62.   if(k<=5)
    1 C: i$ u: \' E8 Q2 O8 w
  63.      LCD_ShowString(275,230,12*4,12,12,"JvChi  ");
    ) Z. G$ ^" m$ @1 z
  64.   else if(k1<=5&&m<0.4) 5 ]9 Z1 h) |$ o9 Y# `
  65.      LCD_ShowString(275,230,12*4,12,12,"Fang   ");- w& V% N8 V2 z4 t' z
  66.   else if(k1<=5&&m>=0.4). Q2 Q; m  A& t! D# W
  67.      LCD_ShowString(275,230,12*4,12,12,"SanJiao");
    ) m4 c0 \3 L- ]5 p+ I2 m" [, \# @! G
  68.   else LCD_ShowString(275,230,12*4,12,12,"Sin    ");. f- Z9 Z5 I) A0 N
  69. }
    8 p  D5 _# F7 e6 O0 V
复制代码

9 f7 @& j0 c. E9 [7 s% e9 z: d/ `: E1 {
这里我们要做的,就是把各次谐波的频率和对应的的幅值提取出来,我采用了两种方法,并把它们结合起来使用。首先是求幅值的最大值、次大值和次次大值,网上也有人写过,我直接拿来用感觉并不好用,就自己写了一个。求出来之后还要和两侧的值比较,看他是否为极大值,是的话才保留。最后,按前面的思路进行比较,比较时的参数是我自己选的(适用于50Hz~200Hz,其他范围的可能也可以)。
& v+ x! ?, V* M, M" X- W
$ L8 `5 \, o5 P3 I5 M3 {# k4.触屏调整采样频率
6 e( w% {! N+ j/ m我最初定的采样频率是2400Hz,因为要求的最大可以分析1kHz的谐波,采样频率要大于2倍的信号频率。通过计算,频率分辨率只有2.34Hz,这样导致有些频率和幅值测得不准确。补零也不能提高频率分辨率,就考虑实时的改变采样频率。- x( p0 g3 W6 H( v4 @* t" E. z4 i
  1. if(tp_dev.sta&TP_PRES_DOWN)   //触摸屏被按下, M, E4 r( n1 I  N: i, f5 @
  2. {
    4 b9 B3 n, v+ G6 p- E$ d
  3.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>360&&tp_dev.y[0]<400)  M2 B4 o% T5 j/ J# _) @
  4.   {
    # e' D  F* ]: m) ]* [+ N* L. v. {
  5.     LCD_Fill(270,360,320,400,BLACK);: B5 \: D, F2 f  b! |2 s
  6.     delay_ms(200);. O5 L( i; E* N+ R' a
  7.     LCD_Fill(270,360,320,400,YELLOW);  l# _0 _. G" m2 D8 ~, K
  8.     ji_shu=ji_shu-200;
    7 W( i$ v, y. P0 t1 z
  9.     TIM1_Int_Init(ji_shu-1,fen_pin-1);//只有放这里才能保证只运行一次,不然可能会卡死9 e7 ^* b+ @0 Q$ I
  10.     POINT_COLOR=BLACK; //画笔颜色$ n8 i# D" u0 j+ I( y
  11.        BACK_COLOR=YELLOW;  //背景色
    ! A0 e6 N) @8 T0 O( i
  12.        LCD_ShowString(284,365,16*2,16,24,"up");
    ' ^8 _  F0 s4 x% B: H' M
  13.     //i++;% _8 Q; F* @8 b
  14.   }
    , m6 O# _' F9 ]
  15.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>440&&tp_dev.y[0]<480)
    - ^% i" M  V) G9 N/ J; M) I- L
  16.   {
    1 ?8 {  L% F, c2 T
  17.     LCD_Fill(270,440,320,480,BLACK);
    % T) i- }: D0 T
  18.     delay_ms(200);* X# I' I$ C& R5 r6 i
  19.     LCD_Fill(270,440,320,480,YELLOW);
    $ Z5 q, [) r, g
  20.     ji_shu=ji_shu+200;
    ! |/ n. Z/ T& K' ]5 u% c
  21.     TIM1_Int_Init(ji_shu-1,fen_pin-1);
    ' @  r4 B8 l7 y, M9 I
  22.     POINT_COLOR=BLACK; //画笔颜色
    : T  f. `* A8 o  E3 \6 Q9 R$ Y& d
  23.        BACK_COLOR=YELLOW;  //背景色
    2 S2 E+ x) m4 n8 c4 ], h
  24.        LCD_ShowString(272,442,16*4,16,24,"down");" g4 r  }1 g3 c3 `6 p& Q% z
  25.     //i--;( g  L. J* y8 a6 z
  26.   }
    7 d* m' M3 B- d
  27.     //TIM1_Int_Init(ji_shu,fen_pin);//取消注释,触屏后会卡死  D8 h% Z* k5 m* V6 V3 ~. F
  28.   Fs=2400000/ji_shu;
复制代码
. o+ U  F* G( i6 q2 \) v% U* q
也没什么可讲的,这个大家自己也能写。把采样频率变小,频率分辨率也会减小,达到目的。
' R" d& H- |. }! r
& Y) E  R9 c( a% D) Q/ S) n8 @
/ v, t2 f# V3 J) @. M* }
四、效果图
4 A3 I. V/ ~4 a( ^1.锯齿波Fs=2400Hz
: `6 x- ]( y# w7 X; t" m" Z
  D8 v$ i) w3 @% L# l/ c% O5 T
20190710151930624.jpg
0 F! x- x, o  }, N: W2 l9 h7 A! B! E! c# ]5 h' S1 K
20190710150734618.jpg
. p. e4 ?$ V% t7 T+ n
0 T& w+ |  t! i4 ]6 r5 T4 q3 B
2.方波Fs=2400Hz和Fs=1200Hz的对比+ S* r6 g  `3 Z; m. U) t8 T: W# B' t

& w* D6 y' b/ ^+ V$ G# f. a
20190710152056388.jpg 7 K5 s7 q: w0 x
& j5 H% ?/ L5 Y" h8 [# K
20190710151817322.jpg
$ p' a( |' _( u% t# N
9 H8 o, Y6 S% T
20190710152152859.jpg 7 i8 S" I! R0 d7 }

4 \. K! j7 P7 t! k, P9 O( M————————————————
5 _3 T' a6 D8 P" m! Y* J版权声明:_鑫鑫鑫_
2 [- A! W* _0 A
7 p/ r! }. S  `: V- }, c2 r

$ \( H0 C: G5 k1 i* Q. H. A
收藏 评论0 发布时间:2023-2-5 17:05

举报

0个回答

所属标签

相似技术帖

官网相关资源

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