在網上搜了很多頻譜顯示的例程,但以本人的水平寫出來的fft算法優化過差,只好借用官方dsp庫的fft函數(確實好用==)
代碼在 Jung1e0523/FFT_STM32 at stm32_fft_dsp_ver1.0 (github.com)
以下為MDK5 main.c 簡單思路
1 #include "led.h"
2 #include <stdio.h> 3 #include "delay.h" 4 #include "sys.h" 5 #include "usart.h" 6 #include "lcd.h" 7 #include "adc.h" 8 #include "stm32f10x_gpio.h" 9 #include "stm32f10x_rcc.h" 10 #include "math.h" 11 #include "fft.h" 12 #include "stm32_dsp.h" //代碼有很多冗雜,但是我實在懶得刪了 13 14 #define PI 3.14 15 16 u32 adc_1[NPT+1]; //adc比濾波結果output多一位,主要是為了填滿 output 1-255位,NPT=256,在其他頭文件中定義了 18 u32 adc_2[NPT+1]; //三通道adc 19 u32 adc_3[NPT+1]; 20 u32 output_1[NPT]; 21 u32 output_2[NPT]; 22 u32 output_3[NPT]; 23 u32 fre_data_1[NPT];//fre_data是dsp庫中fft函數輸出存放的數組 24 u32 fre_data_2[NPT]; 25 u32 fre_data_3[NPT]; 26 u32 temp_1[NPT]; //LCD畫圖的時候用來比大小的兩個臨時數組 27 u32 temp_2[NPT]; 28 29 int main(void) 30 { 31 int i=0 , j=0 , t=0 ; 32 u32 fft_max = 0; 33 float kernel[3] = {0.274069,0.451863,0.274069}; 34 //直接輸入一維3x1高斯模板 35 36 delay_init(); //延時函數初始化 37 uart_init(9600); //串口初始化為9600,學藝不精,我也不知道為啥要用串口,反正大家都用了 38 LED_Init(); //初始化與LED連接的硬件接口,本來還有一個LED閃爍的,我看着煩就刪了,這個很簡單,喜歡看霓虹燈的可以加上 39 LCD_Init(); //LCD初始化 40 adc_Init(); //ADC初始化 41 LCD_Clear(BLACK); //清屏,設置背景為黑色(默認是白色) 42 LCD_Display_Dir(1); //橫屏 43 POINT_COLOR=GREEN; //設置字體為黃色(更改為綠色) 44 BACK_COLOR=BLACK; //設置背景顏色為黑色(正點原子的庫不寫這個字體背景居然還是白的,整體背景卻是黑的,真的弱智) 45 46 47 48 while(1) 49 { 50 for(i=0;i<NPT+1;i++) 51 { 52 adc_1[i] = Get_Adc1(ADC_Channel_0); 53 adc_2[i] = Get_Adc2(ADC_Channel_1); 54 adc_3[i] = Get_Adc3(ADC_Channel_2);//adc輸入 55 } 56 //adc配置詳見adc.c 因為就是配置那些參數,不明白百度能搜到(我就是百度的) 57 for(i=1;i<NPT;i++) 58 { 59 output_1[i] = (float)adc_1[i-1]* kernel[0] + (float)adc_1[i]* kernel[1] + (float)adc_1[i+1]* kernel[2]; 60 output_2[i] = (float)adc_2[i-1]* kernel[0] + (float)adc_2[i]* kernel[1] + (float)adc_2[i+1]* kernel[2]; 61 output_3[i] = (float)adc_3[i-1]* kernel[0] + (float)adc_3[i]* kernel[1] + (float)adc_3[i+1]* kernel[2];//高斯濾波 62 }//高斯濾波實際處理循環,寫的有點蠢,實際使用還可以優化 63 64 for(i=0;i<NPT;i++) 65 { 66 adc_1[i] = adc_1[i+1]; 67 adc_2[i] = adc_2[i+1]; 68 adc_3[i] = adc_3[i+1]; 69 } 70 71 for(i=0;i<NPT-1;i++) 72 { 73 output_1[i] = output_1[i+1]; 74 output_2[i] = output_2[i+1]; 75 output_3[i] = output_3[i+1]; 76 } 77 //兩個移位操作,把數組左移一位 78 adc_1[256] = Get_Adc1(ADC_Channel_0); 79 adc_2[256] = Get_Adc2(ADC_Channel_1); 80 adc_3[256] = Get_Adc3(ADC_Channel_2); 81 //adc輸入第256位(數組這里多設了一位,最后一位的話會報warning,本人有強迫症) 82 83 output_1[255] = (float)adc_1[254]* kernel[0] + (float)adc_1[255]* kernel[1] + (float)adc_1[256]* kernel[2]; 84 output_2[255] = (float)adc_2[254]* kernel[0] + (float)adc_2[255]* kernel[1] + (float)adc_2[256]* kernel[2]; 85 output_3[255] = (float)adc_3[254]* kernel[0] + (float)adc_3[255]* kernel[1] + (float)adc_3[256]* kernel[2];//高斯濾波,簡單的模板x數組對應數值 86 87 // for(i=0;i<NPT;i++) 88 // { 89 // LCD_Fast_DrawPoint(i,80-output_1[i]*60/4096,YELLOW); 90 // LCD_Fast_DrawPoint(i,150-output_2[i]*60/4096,YELLOW); 91 // LCD_Fast_DrawPoint(i,220-output_3[i]*60/4096,YELLOW); 92 // } 93 //這里注釋掉的部分是用來測試adc和濾波部分是否正常運行的,不想要可以刪掉; 94 LCD_ShowxNum(256,20,output_1[255],4,16,0);//顯示通道1濾波后adc值(就看一下adc正常不,一般是0-4096之間的數,會不斷變動); 95 96 cr4_fft_256_stm32(fre_data_1 , (u32*)output_1 ,NPT); 97 cr4_fft_256_stm32(fre_data_2 , (u32*)output_2 ,NPT); 98 cr4_fft_256_stm32(fre_data_3 , (u32*)output_3 ,NPT); 99 //這里是dsp庫的fft函數,程序使用的是256位,也可以用64位和1024位的,看開發板速度和需求而定; 100 for(i=0;i<NPT;i++) 101 { 102 lBufOutArray[i] = ( fre_data_1[i]+fre_data_2[i]+fre_data_3[i] )<<16; 103 } 104 //三個通道的fft處理結果相加,為什么能相加可以去學習傅里葉變換相關知識; 105 GetPowerMag();//這是百度到的一個算實部和虛部模值的函數,而且里面用到了右移16位,所以上面計算輸入數組的時候需要左移16位(數組每一位都是32位); 106 107 fft_max=lBufMagArray[0]; 108 109 for(i=1;i<NPT;i++) 110 { 111 if (lBufMagArray[i]>lBufMagArray[i-1]) 112 fft_max = lBufMagArray[i]; 113 else i = i; 114 } 115 //這里是顯示每次fft得到最大值的部分,不想要可以刪掉; 116 LCD_ShowxNum(256,40,fft_max,4,16,0);//顯示FFT后諧波幅值 117 118 119 for(i=0;i<NPT;i++) 120 { 121 temp_2[i] = (u32)lBufMagArray[i]*100/500; 122 if (temp_2[i] > 200) 123 temp_2[i] = 200; 124 } 125 126 for(i=1;i<40;i++) 127 { 128 if (temp_2[i]>temp_1[i]) 129 { 130 for(j=temp_1[i];j<temp_2[i];j++) 131 LCD_Fast_DrawPoint(6*i,220-j,GREEN); 132 } 133 else if (temp_2[i] < temp_1[i]) 134 { 135 for(j=temp_2[i];j<temp_1[i];j++) 136 LCD_Fast_DrawPoint(6*i,220-j,BLACK); 137 } 138 else j = j; 139 } 140 //畫圖部分,下面詳細說一下思路; 141 for(i=0;i<256;i++) 142 temp_1[i] = temp_2[i]; 143 144 t++;//計循環次數的,通過這個大概估算幀率(主要是懶得寫幀率顯示的了,甲方又沒有這個要求) 145 146 LCD_ShowxNum(256,0,t,4,16,0);//顯示循環次數 147 148 delay_ms(75);//程序跑起來幀率有點高,LCD屏幕太拉了,看的人眼花,故手動延遲降低幀率; 149 } 150 }
下面說一下畫圖的思路:
1 for(i=0;i<NPT;i++) 2 { 3 temp_2[i] = (u32)lBufMagArray[i]*100/500; 4 if (temp_2[i] > 200) 5 temp_2[i] = 200; 6 } 7 8 for(i=1;i<40;i++) 9 { 10 if (temp_2[i]>temp_1[i]) 11 { 12 for(j=temp_1[i];j<temp_2[i];j++) 13 LCD_Fast_DrawPoint(6*i,220-j,GREEN); 14 } 15 else if (temp_2[i] < temp_1[i]) 16 { 17 for(j=temp_2[i];j<temp_1[i];j++) 18 LCD_Fast_DrawPoint(6*i,220-j,BLACK); 19 } 20 else j = j; 21 } 22 23 for(i=0;i<256;i++) 24 temp_1[i] = temp_2[i];
我嘗試了不少畫圖方式,散點圖在LCD上顯示太淡了,離遠了看不清,直接用化線的方式LCD頂不住,會導致幀率特別低,做不到實時處理;
所以這里先把需要顯示的數組輸入temp_2,大於200的直接不要了(這里每個循環temp_2都輸入了新一輪數據,而temp_1使用的是上一次循環的舊數據);
第二個for循環是比較temp_1和temp_2相同位每個數值大小,如果temp_2的大,那么就向上畫綠色的點,如果temp_2小,就向下畫黑色的點(也就是綠色條會下降),二者相等的話不做任何操作(j=j);
最后一個for循環就是把本輪循環的temp_2輸入temp_1中,以待與下一輪循環的新一組數據比較。
ps:最后說句心里話,就百度出來類似例程的,真的沒一個說明變量代表的是什么意義的,看的人十分痛苦==(雖然程序本身並不難)。
不會教可以不教。
另外如果這篇博客有人看的話還希望我寫本工程其他.c文件的可以告訴我,我也可以寫一寫^_^
閑着也是閑着嘛,能幫一個是一個。