本文詳細的講解了FFT的實現,簡直是手把手了。我也是慢慢學過來的,知道網上的教程對於初學者不是很友好,所以決定自己寫一份博客來記錄下來我的經驗 ...
最近學校電賽院隊招新,出的招新題就是低頻示波器的。之前一直沒有弄懂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採樣率應遵循奈奎斯特採樣定理!採樣率不是越高越好!