FFT_LCD频谱显示(STM32F103RCT6与DSP库实现)


在网上搜了很多频谱显示的例程,但以本人的水平写出来的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文件的可以告诉我,我也可以写一写^_^

  闲着也是闲着嘛,能帮一个是一个。

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM