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

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

[复制链接]
STMCU小助手 发布时间:2023-2-5 17:05
一.硬件部分: O7 z0 J6 o# @% y9 O. V% `) m
信号发生器,正点原子精英板,3.5’TFTLCD,两根杜邦线(接PC1和GND)5 G) K8 `- m# A( t% f# m$ M" d' Z

6 f4 Z0 B" o# `; F6 v# V* `% h; v二.基本思路9 t. b1 ^2 q6 n* n
1.使用ADC采集音频信号
- l/ ]# ?8 B+ y7 Q) P8 q6 d2.使用官方提供的FFT函数(1024点)对采集到的信号进行处理- w4 L/ x8 l& O) g4 e
3.量化、频谱图显示7 y1 F: f. g4 Z7 O6 A) X) S
采样频率:Fs = 2400Hz(触摸版本可以根据实际情况调整)
% S- ~- w7 k  s- @( e样本数量:NPT = 1024' e* L& V/ ~, t& R7 c( q* M& I

* h/ r! z" B/ j# }: B0 O( a( w三.程序编写
: b- q% l) V0 D7 [$ C% q; E/ V1.ADC采样& n! J, j3 `% F2 [
这次用到的是ADC的DMA传输,这可以减少对程序的占用。这里着重关注这几行代码:8 v- O% e) V0 Y. m5 L, _% _: `
  1. ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC1 工作在独立模式7 y8 v8 P- g: A* T8 F& y# ^1 k
  2. ADC_InitStructure.ADC_ScanConvMode =DISABLE; //模数转换工作在非扫描模式
    4 C& v* r/ Q5 `$ D/ Z+ O# Q/ S
  3. ADC_InitStructure.ADC_ContinuousConvMode =DISABLE; //模数转换工作在不连续转换模式
    1 @4 ?$ e: l0 K
  4. ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; //Timer1触发转换开启(定时器T1的CC1通道,控制采样频率)
复制代码

+ z) j3 g* k# K$ I假设有ADC1有四个通道需要转换(本例中只有一个通道):
" R' H4 P8 Z$ s1 l: W  S# w% f1 [8 o8 n3 o# M& P
PUM1575JSKKW8W6VPL0@{ZF.png   D" H% I8 |2 b* ?) R$ M

' g" u; g8 u( E; A% u/ t' w, y我的代码就是按这个写的,不这样写应该也可以,不过我认为还是要多注意细节,不然根本不知道问题出在哪儿。
& y3 k; b! P+ o- N6 j  n
  1. DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&ADC1->DR; //DMA 外设 ADC 基地址, [& ^( V9 I1 p% i
  2. DMA_InitStructure.DMA_MemoryBaseAddr = (u32)&ADC_Value; //DMA 内存基地址
复制代码

: o9 ~: [# t# J) @3 @DMA传输注意这两句,存放地址。
+ c1 y3 l8 q. E( d6 u9 r$ ?3 M) ?# P) s3 O: K
2.FFT频谱分析0 E# j7 b. H; U
先下载一个FFT官方库,添加这几个文件9 y8 C9 U! n6 g% y$ x- l. }% S& I

( @$ }1 i7 O: p3 Y
20190710113714855.png
3 V+ m% V0 k* H# f/ ]0 d
4 e) D0 |) D* @* S5 o2 e
主要就是这几个函数
; Y5 g# {; j! q0 l
  1. for(i=0;i<NPT;i++)
    : u1 v$ U! ~$ W# k
  2.   {1 J# }* D' q1 j( B7 ?. Q' I! X
  3.     lBufInArray[i]=ADC_Value[i]<<16;  V7 S& o8 P, @' s
  4.   }8 ]; x6 G( A7 Z+ Q0 O4 U) P
  5.   cr4_fft_1024_stm32(lBufOutArray, lBufInArray, NPT);    4 l0 l( @' [& K; b* Z! c: Y; U9 H$ G
  6.   GetPowerMag();
    * c$ k8 j/ h" N$ R0 b
  7. /******************************************************************
    + _, v( i) {: \! W7 x
  8. 函数名称:GetPowerMag()
    # @8 \4 m2 y' u/ x1 r. q4 ~
  9. 函数功能:计算各次谐波幅值  [short 的范围,是-32767 到 32767 。也就是 -(2^15 - 1)到(2^15 - 1)。]5 a' o  P1 f5 m, \9 Z# O7 n( O! l
  10. 参数说明:
    ; e( V& b+ P) R4 z. I% S/ N
  11. 备  注:先将lBufOutArray分解成实部(X)和虚部(Y),然后计算幅值(sqrt(X*X+Y*Y)3 A) |$ O2 |7 J& z, I& e/ Y
  12. *******************************************************************/$ s6 j9 e/ L* ~5 q
  13. void GetPowerMag(void)
    ' h* m8 {1 E( A) ?! I) L  A
  14. {% \3 `- ?) I+ j$ u; L9 _' l3 y; c  j
  15.     signed short lX,lY;                                                  //算频率的话Fn=i*Fs/NPT  //由于此处i是从0开始的,所以不需要再减13 M0 B  ^# v: t% m) e0 V
  16.     float X,Y,Mag;                                                      
    5 O. B6 f' F& L$ F' v& [- T+ V
  17.     unsigned short i;
    : |5 f) Q/ B) F) X) s! H
  18.     for(i=0; i<NPT/2; i++)                                                  //经过FFT后,每个频率点处的真实幅值  A0=lBufOutArray[0]/NPT
    2 S8 z7 n  l: }3 L: Z$ A) V, G
  19.     {                                                                       //                                 Ai=lBufOutArray[i]*2/NPT
    . G: T! U" y* N1 r" @& p
  20.         lX  = (lBufOutArray[i] << 16) >> 16;  //lX  = lBufOutArray[i];
    % N8 l8 M. D7 @" d; Y; i8 W+ r5 u
  21.         lY  = (lBufOutArray[i] >> 16);
    6 _& W4 k- n2 ^) m
  22.                                     4 }9 _7 i$ w  o, ~
  23.         X = NPT * ((float)lX) / 32768;//除以32768再乘65536是为了符合浮点数计算规律,不管他
    3 F+ U6 K0 v6 a
  24.         Y = NPT * ((float)lY) / 32768;
    ; {( u% T5 ]% D! q# u
  25.         Mag = sqrt(X * X + Y * Y) / NPT;3 T. Z% a1 L/ p, K' v4 Z
  26.         if(i == 0)# p; W# d3 T2 h, p3 Z- I. e4 }
  27.             lBufMagArray[i] = (unsigned long)(Mag * 32768);   //0Hz是直流分量,直流分量不需要乘以2' e% y9 z1 p7 L
  28.         else
    / G% Y  k( j# g
  29.             lBufMagArray[i] = (unsigned long)(Mag * 65536);
    , \$ {  D5 G" C; P
  30.     }8 ~# L6 Y8 R( K1 C
  31. }
复制代码
1 j/ [2 z9 b" i. l9 c
需要说明的是:按照FFT官方库的说明,lBufOutArray和lBufInArray都必须是32位的数据类型,其中高16位存储实部,低16位存储虚部。因为信号发生器输出的不可能是虚数,所以对于lBufInArray来说,低16位存储的虚部总是为0。上面的代码,主要就是计算各次谐波的幅值,就是把虚部和实部取出来平方开根号。
8 \9 h9 i( B( _' x
  1. void lcd_show_fft(unsigned int *p)3 p; Y8 s6 P, ?6 n
  2. {
    7 K6 j  _- ?, L8 E
  3.    unsigned int *pp = p+1;             //p+1相当于我直接把0HZ部分滤掉了/ ~! }5 d6 H6 P9 \
  4.    unsigned int i = 0;8 g3 G4 v+ A# E# S0 h; k1 d
  5.    for(i = 0;i<480;i++)5 L/ L' z& Y" k4 X- j. U$ a
  6.    {
    / b3 Y, Y. m) C' c  w9 n
  7.       LCD_Fill(0,        i, *pp*0.11, (i+1), WHITE);     //有效部分白色       / ]4 A7 Q! {& L6 B7 C3 H
  8.       LCD_Fill(*pp*0.11, i, 270,       (i+1), BLACK);   //其他就黑色# q6 }% X# O% \. a8 x/ c
  9.       pp++;, P% X! `: a; q# R/ i7 E0 W* ^
  10.    }. C. E$ Y9 h! t. j
  11. }
复制代码
: D# T6 i7 M" b& b5 K9 f7 X# D
显示的函数也没什么可讲的,注意不要用画直线的函数,刷屏很慢。
; E) F# y) X# C! v7 \. u3 o) W/ [/ r9 {
7 P- v% X; c7 V# A: A
3.波形识别
" E- I& Y4 @+ s, p先说说我的思路,这个思路肯定不是最好的,我们先来看看各个波形的特点:
1 z9 n& o, b+ H; S' z
* Y8 M# V$ r3 v% B; y
20190710143438516.png
8 w# m) B1 l+ |: e" l' l) p* T, Q! e6 v/ Z
20190710143521584.png
8 g# g* _3 k8 z/ J. w! z4 B- _- N: F- ^+ ^3 f
正弦波:只有基波分量,基本无谐波分量,没什么好说的。; C+ k2 U' F$ W3 w' J7 ~" ]- j
方波:除了基波,还有3,5,7次谐波分量,且3次谐波分量为基波分量的1/3.0 U5 n0 _3 ^1 a8 D8 p' S$ r
三角波:除了基波,还有3,5,7次谐波分量,但3次谐波分量为基波分量的1/9.
1 K" ~0 p6 k2 m7 Z锯齿波:除了基波,还有2,3,4次谐波分量.
6 o  Q6 f, W: [7 x0 w4 I, l+ L) x
1 M/ q. E3 v- g
  1. /***********************************************
    5 i# @, {' h: \! W* n
  2. 找最大值,次大值……对应的频率,分析波形! K0 ]. i1 `% l  F, z
  3. *************************************************/
    ' k( s' S6 C8 U  A
  4. void select_max(float *f,float *a)$ _( {' o2 u+ Y7 J+ O' I
  5. {
    - y, F8 B) j  j# |5 s3 F
  6.    int i,j;8 R( O8 H5 w; X2 ~$ @) j1 n; b
  7.    float k,k1,m;
    ; o- }  m8 X& }) n% U4 i
  8.     float aMax =0.0,aSecondMax = 0.0,aThirdMax = 0.0,aFourthMax=0.0;
    8 S/ f3 r- J$ x0 R  S/ Q
  9.     float fMax =0.0,fSecondMax = 0.0,fThirdMax = 0.0,fFourthMax=0.0;
    9 B$ v7 A; C$ T  n
  10.    int nMax=0,nSecondMax=0,nThirdMax=0,nFourthMax=0;+ k* X4 t; L8 F) l4 F. S
  11.    for ( i = 1; i < NPT/2; i++)//i必须是1,是0的话,会把直流分量加进去!!!!  \9 g/ l# {) ]0 n
  12.     {# ]/ P% y) B7 o) \1 M- @  m7 E
  13.         if (a[i]>aMax)) }/ n# h% W% W, Y7 e0 f1 i
  14.         {4 t6 v; @5 ?% ^1 v
  15.             aMax = a[i];
      j5 M! E; u5 M
  16.        nMax=i;% q4 O3 y( v/ d! F9 M8 P4 f4 i5 s
  17.        fMax=f[nMax];. J, i  D; O3 I" W# o3 ?! ^
  18.         }. g- o/ v% q& J, Z' d2 `$ _
  19.     }6 y/ z8 i4 U8 O
  20.   for ( i=1; i < NPT/2; i++)
    1 `2 |9 |9 ?$ w6 e5 T+ u
  21.     {
    + P4 m  v0 ~6 Z3 K* a
  22.     if (nMax == i)
    " S' e+ `( w/ L- o% t! l. A1 W
  23.     {
      V+ x$ |0 u% D9 s  K1 l
  24.       continue;//跳过原来最大值的下标,直接开始i+1的循环8 M7 o) C3 ]) N) b* I9 `9 N$ p( ^
  25.     }9 s/ F6 p: f  o7 T* K4 i6 g
  26.         if (a[i]>aSecondMax&&a[i]>a[i+1]&&a[i]>a[i-1])* t% p9 W. e, c& x
  27.         {
      t7 L; x* X) W6 V
  28.             aSecondMax = a[i];
    ! x- @8 p& x2 W9 }
  29.        nSecondMax=i;
    9 ?4 z' P  [; M. p* d  G; \' _; `
  30.        fSecondMax=f[nSecondMax];
    0 i0 n3 Y1 _5 q" K2 j6 T; x) q% }& \
  31.         }  V4 U+ Y& e" L$ q" W3 y
  32.     }& k$ Z/ f$ X$ G
  33.   for ( i=1; i < NPT/2; i++)
      h$ `. z6 U8 {- U/ F# `: t
  34.     {  N; i  [( _. C" O- U: f; Z
  35.     if (nMax == i||nSecondMax==i)
    / s7 D: E* Q& W. W, \4 j0 G4 Y9 Y
  36.     {/ X7 X% Q: }6 ^5 r
  37.       continue;//跳过原来最大值的下标,直接开始i+1的循环
    3 N7 P8 `; m! n7 ]* n2 k2 u" q
  38.     }
    % T1 x& B* N7 |) q1 D8 c$ U; Y
  39.         if (a[i]>aThirdMax&&a[i]>a[i+1]&&a[i]>a[i-1])
    ) k+ _$ a# N4 T9 A
  40.         {8 J* |% i& n4 Q3 E8 M
  41.             aThirdMax = a[i]; % G# f; X( D1 B+ [) I
  42.        nThirdMax=i;' T! G3 ~9 s4 q
  43.        fThirdMax=f[nThirdMax];  ^2 P! x( s: e/ R
  44.         }
    6 g0 q  ^! x# j
  45.     }2 G- W  f6 w2 K/ D9 S  V' l
  46.   for ( i=1; i < NPT/2; i++)
    # w0 `  [/ [5 R
  47.     {
    , e, S* t+ M/ O
  48.     if (nMax == i||nSecondMax==i||nThirdMax==i)5 h# \8 u; ~; `1 h% A0 i
  49.     {
    6 u: O7 `. d9 d* |7 b
  50.       continue;//跳过原来最大值的下标,直接开始i+1的循环* ^( A. e" o4 }" A" D
  51.     }+ b% Z6 r9 \# E* g
  52.         if (a[i]>aFourthMax&&a[i]>a[i+1]&&a[i]>a[i-1]): ]& `) z# w) A! b& B+ P) c. l4 B
  53.         {
    . E9 i( D7 D, l% S8 Y0 n
  54.             aFourthMax = a[i];
    : w# Z; M0 V. d6 ~0 u
  55.        nFourthMax=i;
    : M* f, y" B' m9 A: b9 s; X5 E
  56.        fFourthMax=f[nFourthMax];% H' s  u1 e# f5 O  J0 e
  57.         }* J" V6 Z, y( ]. d
  58.     }
    5 w$ P! G0 E% s# I+ {3 F9 {. e
  59.   k=fabs(2*fMax-fSecondMax);
    9 e. B# k" y1 ]8 b; i
  60.   k1=fabs(3*fMax-fSecondMax);
    & V9 L. s1 S7 E3 X9 `4 [8 ]& e, j* b
  61.   m=fabs((float)(aMax-3.0*aSecondMax)); # I: A7 O2 g* h: a) }
  62.   if(k<=5)
    + b0 d, z, t% b! H5 P) t3 [. N2 H6 N
  63.      LCD_ShowString(275,230,12*4,12,12,"JvChi  ");2 G$ _/ a- s0 E' M/ R' w
  64.   else if(k1<=5&&m<0.4)
    9 ~* R: t+ G8 B3 q! t
  65.      LCD_ShowString(275,230,12*4,12,12,"Fang   ");
    & j, G: z5 d* ^
  66.   else if(k1<=5&&m>=0.4)% ^3 @  E3 Z. H: G2 ?, @& f
  67.      LCD_ShowString(275,230,12*4,12,12,"SanJiao");
    1 ^% @5 V% f) B6 N, t
  68.   else LCD_ShowString(275,230,12*4,12,12,"Sin    ");" T- p+ N/ W7 k) f" y
  69. }! {+ j$ M# A) S+ x/ C0 r/ \
复制代码
, ~: v! c/ D* p& z8 o
, n7 X7 I. _7 I( M
这里我们要做的,就是把各次谐波的频率和对应的的幅值提取出来,我采用了两种方法,并把它们结合起来使用。首先是求幅值的最大值、次大值和次次大值,网上也有人写过,我直接拿来用感觉并不好用,就自己写了一个。求出来之后还要和两侧的值比较,看他是否为极大值,是的话才保留。最后,按前面的思路进行比较,比较时的参数是我自己选的(适用于50Hz~200Hz,其他范围的可能也可以)。
1 u* k% P1 m" c. `! }# e  S
5 f+ G1 Z# F  ~$ ?7 _' x. M4.触屏调整采样频率
0 x+ h) V- f6 d# l9 \  P; N" W我最初定的采样频率是2400Hz,因为要求的最大可以分析1kHz的谐波,采样频率要大于2倍的信号频率。通过计算,频率分辨率只有2.34Hz,这样导致有些频率和幅值测得不准确。补零也不能提高频率分辨率,就考虑实时的改变采样频率。
' s+ e  n4 s8 y$ \; F) f9 {
  1. if(tp_dev.sta&TP_PRES_DOWN)   //触摸屏被按下
    9 w7 [: X/ F( |6 F; o' ?) @
  2. {
    0 Q3 A: W' [0 w
  3.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>360&&tp_dev.y[0]<400)
      a* a  Q' `9 {: ?
  4.   {
    $ _: k6 @* E2 }: K
  5.     LCD_Fill(270,360,320,400,BLACK);* u$ d. Y- j3 e! Q# W$ T: b
  6.     delay_ms(200);
    9 Z! R9 q8 E- R8 d! h! O
  7.     LCD_Fill(270,360,320,400,YELLOW);
    ! |5 x1 V* s  L, u" o# Y+ m& f) ^' [
  8.     ji_shu=ji_shu-200;
    ; ]  e) ^2 Q+ {9 t3 X7 I( g
  9.     TIM1_Int_Init(ji_shu-1,fen_pin-1);//只有放这里才能保证只运行一次,不然可能会卡死
    " t/ \8 N; b; W. Y# I; y
  10.     POINT_COLOR=BLACK; //画笔颜色1 u! ~% O- m- e: _0 |& G0 T. P
  11.        BACK_COLOR=YELLOW;  //背景色   t- e; x, S# P
  12.        LCD_ShowString(284,365,16*2,16,24,"up");
    . s6 f) m. m1 m6 \7 W6 H8 a
  13.     //i++;3 \7 V4 r. G5 ~. `" G: O- r
  14.   }
    * }! E7 O) V) s9 e) u7 ?
  15.   if(tp_dev.x[0]>270&&tp_dev.x[0]<320&&tp_dev.y[0]>440&&tp_dev.y[0]<480): @2 o8 r! O" ^' M
  16.   {
    7 N3 A3 h9 s* t# A
  17.     LCD_Fill(270,440,320,480,BLACK);/ N2 i3 F0 T8 I( z, O( m2 m, B
  18.     delay_ms(200);
    3 w( S# ]8 u) N# y) X3 g( p
  19.     LCD_Fill(270,440,320,480,YELLOW);! o9 m; `, N$ d& c: h
  20.     ji_shu=ji_shu+200;5 t4 d, e8 E  W/ S
  21.     TIM1_Int_Init(ji_shu-1,fen_pin-1);
    / t9 p: U( z$ W0 y% l, }, g
  22.     POINT_COLOR=BLACK; //画笔颜色
    % R0 f9 o5 g- Z; a
  23.        BACK_COLOR=YELLOW;  //背景色
    7 B# J+ J# J: j0 f8 b1 O
  24.        LCD_ShowString(272,442,16*4,16,24,"down");! \" l0 z# T# ]2 v9 y3 ^8 \
  25.     //i--;
    ! e3 h3 @- G0 S& m4 W  a
  26.   }6 ], G. G8 k8 }
  27.     //TIM1_Int_Init(ji_shu,fen_pin);//取消注释,触屏后会卡死
    3 K& e/ L$ d8 g' Z+ N! P7 j
  28.   Fs=2400000/ji_shu;
复制代码
$ A6 v5 p( F7 F2 a
也没什么可讲的,这个大家自己也能写。把采样频率变小,频率分辨率也会减小,达到目的。( j' r2 p, s) O1 C& P
# y  [4 _2 |% T$ Y$ \
0 c' B* y3 j% k
四、效果图
; O" o0 N4 @  K1.锯齿波Fs=2400Hz
9 \  g- A& y; g6 y' K) P+ J( V$ `' P/ B! m& \
20190710151930624.jpg # F, f. l2 K0 i" P; {" F8 g$ n
; x5 o5 o2 X" _5 @5 Q
20190710150734618.jpg
) y* U% d% w) ?' x2 S6 p

* y6 c* T9 j/ Q9 o6 j1 k, q2.方波Fs=2400Hz和Fs=1200Hz的对比
1 F; R& j* _/ n8 m* I8 Y8 Z: h( T* d
20190710152056388.jpg
# h; j; N( [, M
$ G2 p( x+ l0 W9 d6 o# E
20190710151817322.jpg
. G% \4 `4 m3 {  P' I, T) Y4 r3 M! q& a$ N9 B; J3 g
20190710152152859.jpg 0 r+ t; I( i) a: [

; c4 R$ @3 C/ h2 a————————————————, G/ Z: ^3 |5 z( n: S
版权声明:_鑫鑫鑫_9 i3 d- D! [! }  v% z
8 {  a! S  z! f5 D
$ y5 O: C9 G0 M4 A# q1 A
收藏 评论0 发布时间:2023-2-5 17:05

举报

0个回答

所属标签

相似分享

官网相关资源

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