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

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

[复制链接]
STMCU小助手 发布时间:2023-2-5 17:05
一.硬件部分! y$ j+ s+ d! r# l8 e, ~
信号发生器,正点原子精英板,3.5’TFTLCD,两根杜邦线(接PC1和GND)
' s/ |; ?% y/ }( j4 }% w3 e* }1 `* t! {0 s) z
二.基本思路0 b' X# v* Y6 L+ b
1.使用ADC采集音频信号! N+ }+ P; A0 f% Q+ L! U( i
2.使用官方提供的FFT函数(1024点)对采集到的信号进行处理
  t$ L" _6 {0 t+ \! Y2 D3.量化、频谱图显示2 m$ Q# P7 v5 T, X8 _& G2 R
采样频率:Fs = 2400Hz(触摸版本可以根据实际情况调整)
3 x5 _: b1 z7 b9 X' v8 ]4 ^  G- K样本数量:NPT = 10240 H3 N! e; p9 D

9 ?' e1 i& h7 L9 v% {! Z三.程序编写* j' e8 I2 X, }2 j4 ~" G0 f( ^
1.ADC采样% U- N2 O* S( Z; v& e6 A
这次用到的是ADC的DMA传输,这可以减少对程序的占用。这里着重关注这几行代码:+ `6 Z) \2 I, A/ [1 Z! ]; `, k
  1. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1 工作在独立模式& L' L& K, q& z. k$ c6 ?; M1 w
  2. ADC_InitStructure.ADC_ScanConvMode =DISABLE; //模数转换工作在非扫描模式
    * x/ j, {5 H$ B4 l" j9 j
  3. ADC_InitStructure.ADC_ContinuousConvMode =DISABLE; //模数转换工作在不连续转换模式
    * x- U; C- k& a0 I
  4. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //Timer1触发转换开启(定时器T1的CC1通道,控制采样频率)
复制代码
! Z3 E. h4 b- ]* A; [" J7 y8 Q
假设有ADC1有四个通道需要转换(本例中只有一个通道):# H5 T" H' C" J. x9 |+ F
: _8 G1 E, z- r) h4 i( P
PUM1575JSKKW8W6VPL0@{ZF.png 1 |, t' p! o4 o0 b$ E6 |$ d

' a, m& H" G+ g6 Z" w5 d我的代码就是按这个写的,不这样写应该也可以,不过我认为还是要多注意细节,不然根本不知道问题出在哪儿。
( i" G/ ^3 [. w3 K
  1. DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA 外设 ADC 基地址# q; X/ k' [/ _0 V
  2. DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value; //DMA 内存基地址
复制代码
& r# Q, L+ w7 ^5 t* t- V
DMA传输注意这两句,存放地址。
) p3 w" q& `0 O% i" S+ F& q
- }: U" l+ Z4 H, U0 V2.FFT频谱分析
; F6 h2 x2 G% Q, E8 r& W  s6 l先下载一个FFT官方库,添加这几个文件3 v, ^6 I: L" N8 d3 a/ n+ D5 ]

8 o" z6 w9 P! c- }. |2 K+ q; v1 |
20190710113714855.png
+ X. z$ c* P9 G( S' T/ k3 U
+ Z" E. b1 ]2 i3 y5 s- }( v
主要就是这几个函数) L* g5 n5 j1 E; y0 t! K6 o
  1. for(i=0;i<NPT;i++)
    9 Y. K9 D& N; @3 v
  2.   {
    , f4 ]6 @0 C  r4 H+ V9 h$ w& r
  3.     lBufInArray[i]=ADC_Value[i]<<16;( i; f( k/ i+ y+ n
  4.   }
      Y8 F. H) c/ [1 {
  5.   cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);   
    # l' c& ?$ B2 J
  6.   GetPowerMag();2 D( S7 y' j- c1 ?2 S8 G( }" W
  7. /******************************************************************
    $ g5 E2 p) S$ E, @2 [3 R$ c
  8. 函数名称:GetPowerMag()
    # Y, g: k5 a0 Q# N5 _3 J; R
  9. 函数功能:计算各次谐波幅值  [short 的范围,是-32767 到 32767 。也就是 -(2^15 - 1)到(2^15 - 1)。]
    2 }8 q5 A; _. `+ w6 T/ V7 K% }  b
  10. 参数说明:/ b0 A5 `; d" }+ O' K- u
  11. 备  注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)7 w" o- K* Q+ y' h' K9 l6 G, `: E. V
  12. *******************************************************************/
    8 d% K" L2 X4 W* w5 Q% A" a! Q5 _' i
  13. void GetPowerMag(void)
    2 v1 k# \9 o1 B2 E% N
  14. {, R# P( |% P9 l0 i" o
  15.     signed short lX,lY;                                                  //算频率的话Fn=i*Fs/NPT  //由于此处i是从0开始的,所以不需要再减1" v) @/ X: s& _, o$ y* x0 ?* x5 t
  16.     float X,Y,Mag;                                                      . P: N$ `% o' ~8 e8 Z: l1 u% C
  17.     unsigned short i;
    4 q  g5 t( o8 \$ j  u4 L0 j
  18.     for(i=0; i<NPT/2; i++)                                                  //经过FFT后,每个频率点处的真实幅值  A0=lBufOutArray[0]/NPT( B7 R% f! `9 P
  19.     {                                                                       //                                 Ai=lBufOutArray[i]*2/NPT
    . O$ j( p& j. ]+ x- O, X* e
  20.         lX  = (lBufOutArray[i] << 16) >> 16;  //lX  = lBufOutArray[i];3 u. o7 v% _" p' `0 ^9 p
  21.         lY  = (lBufOutArray[i] >> 16);7 F8 q- x6 ^# A' M
  22.                                     : t8 H' L9 x7 `/ [* J- ~
  23.         X = NPT * ((float)lX) / 32768;//除以32768再乘65536是为了符合浮点数计算规律,不管他0 b( f/ t* L" W0 g% u. p
  24.         Y = NPT * ((float)lY) / 32768;. _  |9 T- {& M6 D8 |
  25.         Mag = sqrt(X * X + Y * Y) / NPT;
    8 p! Y( I8 y% Y9 y, L" Z# A
  26.         if(i == 0)
    7 I* E7 @- L# Z
  27.             lBufMagArray[i] = (unsigned long)(Mag * 32768);   //0Hz是直流分量,直流分量不需要乘以2
    $ L. g2 s  f& [1 m) t$ n- T
  28.         else# c; d. a- B7 e5 U! y. C5 Z! h' \
  29.             lBufMagArray[i] = (unsigned long)(Mag * 65536);
    % ~6 N9 z7 Q$ I( C6 `2 a# Z9 s
  30.     }: Z1 O  ~: I1 Y$ F. x# b6 ~
  31. }
复制代码
# T& [; N; _$ P5 J+ p& n+ I
需要说明的是:按照FFT官方库的说明,lBufOutArray和lBufInArray都必须是32位的数据类型,其中高16位存储实部,低16位存储虚部。因为信号发生器输出的不可能是虚数,所以对于lBufInArray来说,低16位存储的虚部总是为0。上面的代码,主要就是计算各次谐波的幅值,就是把虚部和实部取出来平方开根号。( {+ `! j+ c7 @& R/ b- f( F6 b" L
  1. void lcd_show_fft(unsigned int *p)
    % p6 d- ]7 t; K5 {/ V  e5 k
  2. {. k! ~% \! w8 z. J9 U, w
  3.    unsigned int *pp = p+1;             //p+1相当于我直接把0HZ部分滤掉了
    5 |7 r3 I- R5 G! d; b
  4.    unsigned int i = 0;
    9 {# V! ^9 W# o0 H5 H+ D
  5.    for(i = 0;i<480;i++)5 z" _$ G" O3 r8 Y! m
  6.    {9 g$ M; _1 F, G# V8 z6 Z$ {
  7.       LCD_Fill(0,        i, *pp*0.11, (i+1), WHITE);     //有效部分白色      
    ) @8 w/ d7 \7 L" v1 g8 |' \
  8.       LCD_Fill(*pp*0.11, i, 270,       (i+1), BLACK);   //其他就黑色. y6 I$ H( @0 W; P. N
  9.       pp++;
    ) k! O' Q( P" G0 [% z6 O: T
  10.    }; r, k8 B. F0 ?2 Q: j+ O& O
  11. }
复制代码
) m, v" K5 h! j7 j+ J6 ^
显示的函数也没什么可讲的,注意不要用画直线的函数,刷屏很慢。
, ~8 p2 g: K# }5 o8 [! j5 p
* Q9 W1 H$ ~* ~

, [6 `/ {. C; Q& j( d% n3.波形识别
& w) g; d1 u5 w0 n$ f先说说我的思路,这个思路肯定不是最好的,我们先来看看各个波形的特点:$ k+ r& W0 ]: @1 G# E, P+ b+ |
2 H( a( F' y+ y. l2 [  e2 A
20190710143438516.png
/ \6 F/ ?5 D6 Y' Z6 j2 U: ]- `# K- i; ~0 v3 ?" ?% U
20190710143521584.png 6 H: L8 Z; ?& K& F" h! B9 ?5 U
; U* m$ q2 F% M' U
正弦波:只有基波分量,基本无谐波分量,没什么好说的。
; _. ?$ o  {% z! c方波:除了基波,还有3,5,7次谐波分量,且3次谐波分量为基波分量的1/3.' D1 d" k3 [$ e$ S/ e
三角波:除了基波,还有3,5,7次谐波分量,但3次谐波分量为基波分量的1/9.
( q! O, _8 c$ j: |# X锯齿波:除了基波,还有2,3,4次谐波分量.

8 x$ Y; }; b4 O1 D- S! {

" Z( g& |/ L+ X$ n! b
  1. /***********************************************
    " l: X- C, W7 _; n7 T8 N- Y6 s  o
  2. 找最大值,次大值……对应的频率,分析波形% d# O  w7 o. g
  3. *************************************************/
    $ d7 o" G% H& r: ?8 ?
  4. void select_max(float *f,float *a)" s! L5 A: K; _! \
  5. {
    6 U% C1 k7 Y: z: ?" E9 d
  6.    int i,j;1 {& l' y3 ~  ?+ D) x* ~
  7.    float k,k1,m;
    9 v( L, }0 u7 Z: b; V& e, Q
  8.     float aMax =0.0,aSecondMax = 0.0,aThirdMax = 0.0,aFourthMax=0.0;
    ; [/ n$ ]* m, u* r1 {$ c$ X
  9.     float fMax =0.0,fSecondMax = 0.0,fThirdMax = 0.0,fFourthMax=0.0;0 d- q% i* R- y) O2 _
  10.    int nMax=0,nSecondMax=0,nThirdMax=0,nFourthMax=0;
    3 n6 A) o& P5 |( b
  11.    for ( i = 1; i < NPT/2; i++)//i必须是1,是0的话,会把直流分量加进去!!!!
    ( }! {6 h) g9 H% @
  12.     {
    ! V& _& b$ Z$ \6 H7 Y, w
  13.         if (a[i]>aMax)
    3 w) O6 t( ~' |4 U7 X) o- m
  14.         {
    $ n) N  z+ D+ M5 Y0 Q
  15.             aMax = a[i];
    5 }- s  }8 k1 U5 w% [
  16.        nMax=i;' }5 w) b6 o8 U5 h- f
  17.        fMax=f[nMax];, s; G2 U6 \1 G" v" ^# w" q; I& }* S
  18.         }
    % E0 s) q: s2 j4 U6 k2 ~8 m( P/ I
  19.     }
    1 E8 z0 i0 n9 L% F
  20.   for ( i=1; i < NPT/2; i++)
    2 u. u6 j) o9 P" N, e
  21.     {$ v6 ^. U* X- {) ?6 H
  22.     if (nMax == i)
    0 z9 S' [# J* S5 S: }
  23.     {
    + I! Q" u1 y2 @2 r  t. R% X( q- `
  24.       continue;//跳过原来最大值的下标,直接开始i+1的循环+ Q8 n4 {5 B" A/ t# ~" Q* x3 w
  25.     }$ _, s  k1 R$ C% d6 o2 N
  26.         if (a[i]>aSecondMax&&a[i]>a[i+1]&&a[i]>a[i-1])
      U3 s0 N& c+ b3 A
  27.         {3 p0 s/ p6 f; m. y& Y  t* r
  28.             aSecondMax = a[i];
    1 X# r5 H6 C) U6 |% n, n
  29.        nSecondMax=i;
    7 J8 u+ B& e% ^& h+ `
  30.        fSecondMax=f[nSecondMax];4 _8 \6 T& D3 g9 v
  31.         }- D" B; m+ p  p
  32.     }+ v! K9 r- Y! `7 [4 d. N
  33.   for ( i=1; i < NPT/2; i++)
    3 q- H& n0 b6 u5 K$ v; |9 h7 `8 Y9 h
  34.     {- w8 i5 {4 v& y, P# X3 s& L6 W
  35.     if (nMax == i||nSecondMax==i)
    ) u7 k7 V! Y4 L# I  b8 _) f5 @
  36.     {/ ^/ D. W9 r9 H% C. k* d- w
  37.       continue;//跳过原来最大值的下标,直接开始i+1的循环. Q( V* \" ^' M, c- {
  38.     }
    ( [% @2 m) s! S& d
  39.         if (a[i]>aThirdMax&&a[i]>a[i+1]&&a[i]>a[i-1])- i- k) v0 J( N
  40.         {
    9 H6 ]3 a5 r$ S5 x
  41.             aThirdMax = a[i];
    ( f- ?! G0 H$ a/ ~; i1 z0 _
  42.        nThirdMax=i;
    8 u6 N+ w: ^: E- l' i
  43.        fThirdMax=f[nThirdMax];
    ) t6 \6 U! e6 A0 K- D6 h
  44.         }* [" p: e6 {& L. @8 I% c
  45.     }3 ?) M8 G# y$ J
  46.   for ( i=1; i < NPT/2; i++)7 z  h+ ~9 C2 S/ L
  47.     {8 v: j$ ?# M2 e
  48.     if (nMax == i||nSecondMax==i||nThirdMax==i)
    " s) F3 F4 y2 b" U' A
  49.     {( K3 [& ]* q. @
  50.       continue;//跳过原来最大值的下标,直接开始i+1的循环
    # l& y+ ?) M  T% I- i/ @
  51.     }
    1 j, W1 b, N( m1 n
  52.         if (a[i]>aFourthMax&&a[i]>a[i+1]&&a[i]>a[i-1])
    + m' E- G2 C4 ^: X4 y9 V1 W
  53.         {, [' S- A$ Q! U  @2 U
  54.             aFourthMax = a[i];   |9 n) F6 X7 a- x. m- o+ @9 {& ^0 }  t
  55.        nFourthMax=i;' f" k5 F8 f) p
  56.        fFourthMax=f[nFourthMax];4 P; K! ?: {8 t1 C2 O
  57.         }. I1 ~, g* K0 ?! B7 ?% x6 K
  58.     }
    9 P- C- ]. R# M# Y3 M$ {
  59.   k=fabs(2*fMax-fSecondMax);
    / c, B" T9 Q( g1 l
  60.   k1=fabs(3*fMax-fSecondMax);
    6 p# x; I, {. h7 R# o. A, ]
  61.   m=fabs((float)(aMax-3.0*aSecondMax)); 8 P: h9 c& v# y' F% j
  62.   if(k<=5), _# a  N$ x4 p* K  P  P
  63.      LCD_ShowString(275,230,12*4,12,12,"JvChi  ");- r% u4 K; y1 o2 R3 y3 l: E
  64.   else if(k1<=5&&m<0.4)
    . o( r6 D$ Z  k4 W
  65.      LCD_ShowString(275,230,12*4,12,12,"Fang   ");
    * i, A* n, V' ?0 ]- P" w
  66.   else if(k1<=5&&m>=0.4)1 ^- P1 o1 Z' x2 H
  67.      LCD_ShowString(275,230,12*4,12,12,"SanJiao");4 ?5 W' `* _2 t7 W( {$ U+ C
  68.   else LCD_ShowString(275,230,12*4,12,12,"Sin    ");) m, L2 ]: N9 \& n
  69. }
    7 E+ G0 a- o- c3 ^
复制代码
' f5 S; `: E: `( L- @
; X& l! l# p; B$ r+ n& Y8 t% r
这里我们要做的,就是把各次谐波的频率和对应的的幅值提取出来,我采用了两种方法,并把它们结合起来使用。首先是求幅值的最大值、次大值和次次大值,网上也有人写过,我直接拿来用感觉并不好用,就自己写了一个。求出来之后还要和两侧的值比较,看他是否为极大值,是的话才保留。最后,按前面的思路进行比较,比较时的参数是我自己选的(适用于50Hz~200Hz,其他范围的可能也可以)。
. a* U2 S! e( H
& }: d5 h, o2 M. l0 D% m. }4.触屏调整采样频率" \+ @8 s8 ]; R. Q7 j4 z
我最初定的采样频率是2400Hz,因为要求的最大可以分析1kHz的谐波,采样频率要大于2倍的信号频率。通过计算,频率分辨率只有2.34Hz,这样导致有些频率和幅值测得不准确。补零也不能提高频率分辨率,就考虑实时的改变采样频率。
! \: y$ ]( `. t# Z0 Q
  1. if(tp_dev.sta&TP_PRES_DOWN)   //触摸屏被按下( u  b* Q' r+ Q& w. ~
  2. { 1 d' C" l9 x% e% B! H2 z% x# _
  3.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>360&&tp_dev.y[0]<400)
    ) Q; G9 \4 E8 m$ ^) z: m
  4.   {
      w1 M* s8 e2 K) c0 _
  5.     LCD_Fill(270,360,320,400,BLACK);
    4 W- J0 `+ @4 Y( c9 D- d
  6.     delay_ms(200);1 X9 @: t% U$ \4 a  Q0 j8 {6 S
  7.     LCD_Fill(270,360,320,400,YELLOW);" v# v& ^, F6 P
  8.     ji_shu=ji_shu-200;
    + c; P! P1 v! }% T
  9.     TIM1_Int_Init(ji_shu-1,fen_pin-1);//只有放这里才能保证只运行一次,不然可能会卡死( q8 u5 Y+ I' o* G
  10.     POINT_COLOR=BLACK; //画笔颜色% q/ I4 c! E7 \
  11.        BACK_COLOR=YELLOW;  //背景色 $ V5 e9 u4 v. c
  12.        LCD_ShowString(284,365,16*2,16,24,"up");
    # l0 ]8 B' p6 L5 x. V) w  Z. D+ w
  13.     //i++;/ H% s; c0 ?4 U4 j: }
  14.   }- P3 R- a  e8 h6 G; q
  15.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>440&&tp_dev.y[0]<480)
    / X7 e0 K) n- Z0 J4 f
  16.   {
    1 @4 C1 T$ H7 B- c& [- v
  17.     LCD_Fill(270,440,320,480,BLACK);. V0 T1 s- a( I4 i  L2 w
  18.     delay_ms(200);+ J1 j$ s' `7 \
  19.     LCD_Fill(270,440,320,480,YELLOW);# [. l8 B3 K* ~; v/ `3 `8 D
  20.     ji_shu=ji_shu+200;
    ! [: _- J- x0 h
  21.     TIM1_Int_Init(ji_shu-1,fen_pin-1);
    ( N' O- d) C$ ~
  22.     POINT_COLOR=BLACK; //画笔颜色! [9 Z8 k2 B4 K6 t
  23.        BACK_COLOR=YELLOW;  //背景色 # w: c9 O$ T8 F; @: {9 Z
  24.        LCD_ShowString(272,442,16*4,16,24,"down");
    - U+ ^' n1 x) T5 ^. E" E; c  Y( t
  25.     //i--;
    / O$ Q$ z; {) Z# @/ [8 O
  26.   }: V: u7 ?/ }% P/ r
  27.     //TIM1_Int_Init(ji_shu,fen_pin);//取消注释,触屏后会卡死
    & z' L1 j# V' r$ `/ Z
  28.   Fs=2400000/ji_shu;
复制代码

/ R' C/ U* P7 }# \, u: x. I) g也没什么可讲的,这个大家自己也能写。把采样频率变小,频率分辨率也会减小,达到目的。
2 \! E+ j: Y) s& f2 _
! h1 ^+ X7 g2 Z: k
# p$ a! x5 s; @$ v* C4 ~1 u0 @
四、效果图/ h) Z5 ~! D& h7 X9 H+ I, m
1.锯齿波Fs=2400Hz4 ?+ m1 w" k1 l" `8 v2 A% Q3 |
# P  }4 J' G. e% u
20190710151930624.jpg . }8 _/ w) f4 K7 |

( f5 }/ S/ e$ H7 _
20190710150734618.jpg & v4 a3 K2 u" ]1 [& E5 g
0 A: F: ^$ I: {" g7 V  {
2.方波Fs=2400Hz和Fs=1200Hz的对比9 U9 O- ?6 x* t" g' }

9 _: N9 x8 u1 [9 b/ S
20190710152056388.jpg ( V& J6 q7 Y6 V; G! C' N; B* w
( r( s% G8 c  z8 ]: w2 b% N
20190710151817322.jpg : M: s. m5 E% \4 c% h" i% V( T

2 b2 p1 v  O+ B) Q4 L/ ~- _
20190710152152859.jpg * b5 m" B! i& }4 D
" V, u. Q6 |4 p5 N
————————————————
# n$ A" N( v( F2 e版权声明:_鑫鑫鑫_. U( u  C) [+ e* ~. L

: M# S: y4 [1 C$ f# m' c  P9 i4 i
0 r: j4 T- I' y! }+ R! X& ?
赞 收藏 评论0 发布时间:2023-2-5 17:05

举报

0个回答

所属标签

相似技术帖

官网相关资源

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