stm32F103RCT6使用FFT運算分析波形詳解(非常新手)

砕月之殤發表於2022-04-28

最近學校電賽院隊招新,出的招新題就是低頻示波器的。之前一直沒有弄懂FFT,藉著這次機會實現了一下。

  • FFT原理詳解

  FFT,就是快速傅立葉變換,這個操作能夠將時域訊號轉化成頻域訊號,然後對訊號進行分析

  這樣說可能有點抽象。講細點就是指能夠直觀的看出來目標訊號的頻率是多少。x軸座標本來是表示時間,FFT之後變成了表示頻率,就是這個意思

  對於訊號處理,FFT之後的結果,波峰一般會出現在我們希望測得訊號的頻率附近(十分相近)

  • 官方檔案解釋

stm32官方給了幾個用於處理FFT的檔案,如圖所示:

 

 

 其中有兩個彙編檔案兩個標頭檔案:彙編檔案是定義了FFT的計算函式,我們直接呼叫即可

cr4_fft_1024_stm32.s是包含了計算1024個點的FFT的函式的彙編檔案,另一個彙編檔案同理

stm32_dsp.h裡面有關於FFT處理函式的宣告,我們包含了這個標頭檔案之後直接呼叫函式即可

  • 演算法解釋
 1 //進行FFT運算等操作
 2 void FFT_Wave(void)
 3 {
 4   u16 i;
 5   float mid_value;
 6   while(!ADC_flag)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              
 7   {
 8     LED1 = !LED1;
 9     delay_ms(100);
10   }
11   ADC_flag = 0;
12   
13   //獲取最大值最小值
14   adc_value_max = adc_value_min = ADC_buff[1];
15   for(i = 0;i < NPT;i++)
16   {
17     //尋找最大值最小值
18     if(ADC_buff[i] >= adc_value_max)
19     {
20       adc_value_max = ADC_buff[i];
21     }
22     if(ADC_buff[i] <= adc_value_min)
23     {
24       adc_value_min = ADC_buff[i];
25     }
26     //先清空陣列
27     fftin[i] = 0;
28     //移位,讓後面16位為虛部
29     fftin[i] = ((s16)ADC_buff[i] << 16);
30   }
31   cr4_fft_1024_stm32(fftout,fftin,1024);//FFT
32   GetPowerMag();
33   //計算電壓值
34   Vpp_true = (adc_value_max - adc_value_min) * 3.3 / 4096.0;//獲得Vpp值
35   mid_value = (adc_value_max + adc_value_min) / 2;
36   for(int i = 0;i < NPT;i++)
37   {
38     if(ADC_buff[i] > mid_value)
39     {
40       rect_duty++;
41     }
42   }
43   rect_duty = rect_duty / 1024 * 100;
44 }

這是FFT的主體函式

第一步我們先要等待ADC採集完成,將資料存入陣列當中準備進行處理

 第二步是在取樣值當中尋找最大值和最小值(遍歷陣列即可)

第三步是對陣列進行移位處理(前面的是實部,後面的是虛部,由於我們採集到的電壓都是實數,所以虛部都置0)

第四步是使用ST官方提供的函式進行FFT運算,得到運算之後的陣列

第五步是根據頻譜查詢我們訊號所對應的頻率,也就是對頻譜圖當中所有的頻率進行幅值的比較,找出幅值最大時所對應的頻率,即為我們所需要測量的頻率,其他的都可以看作噪聲

在我們找到該頻率之後,不能立刻輸出,要與ADC的取樣率相乘再除以1024,之後才能得到我們想要的訊號頻率

GetPowerMag函式定義如下:

 1 void GetPowerMag(void)
 2 {
 3   s16 lX,lY;
 4   u32 i;
 5   float maxmag;
 6   for(i = 0;i < NPT / 2;i++)
 7   {
 8     lX = (fftout[i] << 16) >> 16;
 9     lY = (fftout[i] >> 16);
10     float X = 1024 * ((float)lX) / 32768;
11     float Y = 1024 * ((float)lY) / 32768;
12     float mag = sqrt(X * X + Y * Y) / 1024;
13     FFT_Mag[i] = (u32)(mag * 65536);
14   }
15   FFT_Mag[0] >>= 1;//頻譜圖第一個是直流分量,無需乘2
16   for(int i = 0;i < NPT / 2;i++)
17   {
18     if((maxmag < FFT_Mag[i]) && (i != 0))
19     {
20       maxmag = FFT_Mag[i];
21       temp = i;
22     }
23   }
24   F_hz = temp * sampling_rate / 1024.0;
25 }

至此,我們就得到了我們所需訊號的頻率

鑑於本小白能力有限,如果有紕漏或改進之處,歡迎指正

特別提醒:ADC取樣率應遵循奈奎斯特取樣定理!取樣率不是越高越好!

相關文章