基于STM32F4的FFT+测频率幅值相位差,波形显示,示波器,时域频域分析相关工程

分享前的总结

一入电赛深似海,此话不假,个人感觉很累,但是收获确实多。本人去年参加了国赛,电赛提前半个月就开始着手准备了,只记得那时候不是调试就是在调试的路上,也因此留下了宝贵的我姑且称之为“经验”,作为一名小白,借此机会跟各位老白和小白分享一下。我训练较多的是信号类的题目,做到最后我发现无非就是测频,测幅值,用一下FFT,显示,玩一下LCD屏,分析一下时域和频域,其实原理上都挺简单的,再加一些难度,也就无非是提高一下测量频率的上限和精度,比如能测一个上千KHz的信号,或者是能产生一个上千KHz的信号,像这种情况就要用到FPGA了,不过这里主要就常规而言,关于FPGA的测频方法,我会另外抽时间专门写一篇文章出来分享给大家。无论哪种情况,本人期间也摸索了很长时间,为此专门写下一篇文章。有任何问题q2036795517 本文章目的在于为广大电赛小白和电赛老白提供资源和经验上的帮助,这些都是很方便的。

资源列表

1.ADC规则通道多通道DMA转换(基于STM32F1),可参考思路
2.基于STM32F4的FFT测信号频率并判断波形种类(采样率可调)
3.基于STM32f407的示波器(LCD屏用的FFT并显示时域和频域两种波形)
4.(基于STM32F4的FFT)幅值,频率相位差

主要代码

2.基于STM32F4的FFT测信号频率并判断波形种类(采样率可调)

//ADC通道1引脚为PA5,通道2为PA6,只对通道一的数据进行FFT,通道二的数据用来取平均,测量直流。
//PA0为输入捕获引脚,用来测量外部信号频率,采样率可由此接口触发决定,比例关系自行确定,注意:没有触发信号的话,无法进行采样
//PA9,PA10作为UART的通信接口
#define FFT_LENGTH		4096		//FFT长度,默认是1024点FFT

double freq_zeroValue=0; //直流量
float fft_inputbuf[FFT_LENGTH*2];	//FFT输入数组
float fft_outputbuf[FFT_LENGTH];	//FFT输出数组
float relbuf[FFT_LENGTH/64];   //实部信号数组
float freq=1000;   // 定义输入信号的频率
__IO uint32_t tc=1000; //周期,单位为us
float duty=50;     //占空比
float thd1,thd2,thd3,thd4,thd5=0;
// float M1=1.5; //定义放大倍数
// float M0=0.5;  //直流放大倍数
 float THD;    //判断波形频率分量比值
 float freqlow,freqhigh,VPP;  //。双音多频低频分量,高频分量,VPP峰峰值
__IO uint16_t ADC_DMA_ConvertedValue[8192]; //4096*2
__IO uint8_t TransferComplete=0;   //转换完成标志
float ARRP;     //计算得出的ARR
 __IO uint32_t ARRR;   //取整后得ARR
static __IO uint8_t D0,D1,freq0_sign=0,sign=0;    //D0,D1检测放大位数,直流标志,开启标记
extern TIM_HandleTypeDef TIM5_Handler;      //定时器5句柄
extern __IO uint8_t  TIM5CH1_CAPTURE_STA;	//输入捕获状态		    				
extern __IO uint32_t	TIM5CH1_CAPTURE_VAL1;	//输入捕获值1(TIM2/TIM5是32位)
extern __IO uint32_t	TIM5CH1_CAPTURE_VAL2; 	//输入捕获值2(TIM2/TIM5是32位)

__IO uint16_t   index_fir,index_sec,index_thi,index_for,index_fif,index_max,index_sec; //一到五次次谐波下标

int findmax(float*array,__IO uint16_t len,__IO uint16_t s)  //s表示从第几个数开始找起
 {
	 int i;
	 int j=s;
	 for(i=s;i<len;i++)
	 { 
		 if(array[i]>array[j])
		 {
		 j=i;
		 }
	 }
	 return j;
 }
 int findmin(float*array,__IO uint16_t len,__IO uint16_t s)  //s表示从第几个数开始找起
 {
	 int i;
	 int j=s;
	 for(i=s;i<len;i++)
	 {
		 if(array[i]<array[j])
		 {
		 j=i;
		 }
	 }
	 return j;
 }
	
 void MAXandSEC(float *array,__IO uint16_t len)  //找出最大值和次大值得下标
{   
    int i;

    if(array[1]>array[2])
    {
        index_max=1;
        index_sec=2;
    }
    else
    {
        index_max=2;
        index_sec=1;
    }                                  

    for(i=3;i<len;++i)
    {
        if(array[i]>array[index_max])          
        {
            index_sec=index_max;
            index_max=i;
        }
        else
            if(array[i]>array[index_sec])       
                index_sec=i;
    }
}
 int main(void)
{   float MAX_VALUE,MIN_VALUE;

   arm_cfft_radix4_instance_f32 scfft;  //FFT会用到的东西

	u16 i; 

	  HAL_Init();                     //初始化HAL库   
    Stm32_Clock_Init(360,25,2,8);   //设置时钟,180Mhz
    delay_init(180);                //初始化延时函数
    uart_init(115200);              //初始化USART
	  LED_Init();                     //初始化LED 
	
  TIM5_CH1_Cap_Init(0XFFFFFFFF,90-1);   //以1MHZ的频率计数 (测试后视情况可提升至2MHZ来减小测高频的误差)

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_ADC1_Init();
  MX_TIM3_Init();

  /* USER CODE BEGIN 2 */
   HAL_ADC_Start_DMA(&hadc1,(uint32_t*)&ADC_DMA_ConvertedValue,8192);
  /* USER CODE END 2 */

arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数
  

    while(1)
	{	        //检测放大电路倍数
//						D0=HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_9);
//	
//			     	if(D0==1){M1=1.5;}
//						else M1=30;
						
//     printf("dianya5=%f\rdianya4=%f\r",(float)temp_v[0]*(3.3/4096),(float)temp_v[1]*(3.3/4096));

   if(USART_RX_STA&0x8000)
		{					
//			while(__HAL_UART_GET_FLAG(&UART1_Handler,UART_FLAG_TC)!=SET);		//等待发送结束
//			printf("\r\n\r\n");//插入换行

					if(USART_RX_BUF[0]==0x70)            //开始测试
			{  
	            delay_ms(500);						
					           sign=1;		
			}
				
			USART_RX_STA=0;      //复位接受完成标志,开启UART接收
		}


     					 if(TIM5CH1_CAPTURE_STA==8&&sign==1)        //成功获取了想要的频率数据
						{     
							   
					        HAL_TIM_Base_Stop(&TIM5_Handler);      
							//计算周期、频率
							tc=TIM5CH1_CAPTURE_VAL1-TIM5CH1_CAPTURE_VAL2; 

							printf("t21.txt=\"%dus\"\xff\xff\xff",tc);  //发送到串口屏t22控件的文本里进行显示
							freq=(float)1000000/(float)tc;   //单位hz
						  	printf("t9.txt=\"%0.1fHz\"\xff\xff\xff",freq);
							delay_ms(10);  							
						  ARRP=1000/(9.92481203f*freq/1000+1.50375939f)+0.5f; //这个式子就是用来确定自动重装载计数器的值的,四舍五入
							/* Set the Auto-reload value */
						 ARRR=(uint32_t)ARRP;
							if(ARRR<2){ARRR=2;}
						 	TIM3->ARR = ARRR-1;  //设置计数周期?
							//4k最低时钟触发频率,此时对于交流信号采样率为2k 
	      HAL_TIM_IC_Stop_IT(&TIM5_Handler,TIM_CHANNEL_1);   //停止TIM5的捕获通道1,捕获中断
			      	__HAL_TIM_SET_COUNTER(&TIM5_Handler,0);
								TIM5CH1_CAPTURE_STA=0;          //开启下一次捕获标志

				HAL_TIM_Base_Start(&htim3);        //开启TIM3触发AD采集
	
		
						} 
						

		if(TransferComplete==1&& freq0_sign==0)	//采集完,测频完,不是直流
		{    
			//  	printf("t13.txt=\"%dHz\"\xff\xff\xff",ARRR);
			for(i=0;i<FFT_LENGTH;i++)//生成信号序列
			{   
				 fft_inputbuf[2*i]=ADC_DMA_ConvertedValue[2*i];	//生成输入信号实部
		
				fft_inputbuf[2*i+1]=0;//虚部全部为0 
			}
			    for(i=0;i<1024;i++)   //画曲线
				{ uint16_t pot;
					
				pot=(uint16_t)((ADC_DMA_ConvertedValue[2*i]+2*ADC_DMA_ConvertedValue[2*i+1]-4344.24f)*120.0f/4468.38f+90+0.5f);
						printf("add 13,0,%d\xff\xff\xff",pot); 
				}  
					for(i=0;i<32;i++)   //计算直流值
				{
					freq_zeroValue+=ADC_DMA_ConvertedValue[2*i+1]; 
				}
				
    				
     
				for(i=0;i<FFT_LENGTH/64;i++)//生成信号序列
			{    
		     relbuf[i]=fft_inputbuf[2*i];	  //实部信号采样数组
			}				 
			  	MAX_VALUE=relbuf[findmax(relbuf,FFT_LENGTH/64,1)];
						MIN_VALUE=relbuf[findmin(relbuf,FFT_LENGTH/64,1)];
			   	VPP=(MAX_VALUE-MIN_VALUE)*3280.f/4096.0f;
			//		VPP=(VPP-1461)*2.0299f; //建立的函数关系如此
			
							printf("t7.txt=\"%0.1fmV\"\xff\xff\xff",VPP);
						
			        freq_zeroValue=freq_zeroValue/4096/32*3200.0f;  //实际值
			       	printf("t23.txt=\"%0.1fmV\"\xff\xff\xff",freq_zeroValue);
			
			
			arm_cfft_radix4_f32(&scfft,fft_inputbuf);	//FFT计算(基4)
			arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//把运算结果复数求模得幅值 
	    delay_ms(500);

				index_fir=findmax(fft_outputbuf,2048,5);
				index_sec=findmax(fft_outputbuf,2048,index_fir*1.2);
				
			thd1=fft_outputbuf[index_fir];
			thd2=fft_outputbuf[index_sec];
		  THD=thd2/thd1;
	    THD=THD*100;   
			if(0<THD&&THD<=8){printf("t5.txt=\"正弦波\"\xff\xff\xff");}
				if(8<THD&&THD<=20){printf("t5.txt=\"三角波\"\xff\xff\xff");}
					if(20<THD&&THD<=30){printf("t5.txt=\"脉冲波\"\xff\xff\xff");}
						if(30<THD&&THD<=36){printf("t5.txt=\"方波\"\xff\xff\xff");}
						if(36<THD&&THD<=40){printf("t5.txt=\"锯齿波\"\xff\xff\xff");}
							if(40<THD&&THD<=45){printf("t5.txt=\"脉冲波\"\xff\xff\xff");}
										if(45<THD&&THD<=70){printf("t5.txt=\"锯齿波\"\xff\xff\xff");}
										if(70<THD&&THD<=90){printf("t5.txt=\"脉冲波\"\xff\xff\xff");}
																
										
										if(90<THD&&THD<=100){
																		
																		printf("t5.txt=\"DTMF\"\xff\xff\xff");
															

																	freqlow=(float)(index_fir/4096)*1000/ARRR*1000;
																	freqhigh=(float)(index_sec/4096)*1000/ARRR*1000;
						
						
							
					  	printf("t15.txt=\"%0.2fHz\"\xff\xff\xff",freqlow);	
							printf("t17.txt=\"%0.3f\"\xff\xff\xff",fft_outputbuf[index_fir]/2048*3220.0f/4096);
					  	printf("t11.txt=\"%0.2fHz\"\xff\xff\xff",freqhigh);
							printf("t13.txt=\"%0.3f\"\xff\xff\xff",fft_outputbuf[index_sec]/2048/2048*3220.0f/4096);
							
					
						
				if(100<freqlow&&freqlow<=730&&940<freqhigh&&freqhigh<=1250) {	printf("t19.txt=\"1\"\xff\xff\xff");}
							if(100<freqlow&&freqlow<=730&&1250<freqhigh&&freqhigh<=1400) {	printf("t19.txt=\"2\"\xff\xff\xff");}
											if(100<freqlow&&freqlow<=730&&1400<freqhigh&&freqhigh<=1500) {	printf("t19.txt=\"3\"\xff\xff\xff");}
															if(100<freqlow&&freqlow<=730&&1500<freqhigh&&freqhigh<=2000) {	printf("t19.txt=\"A\"\xff\xff\xff");}
				if(730<freqlow&&freqlow<=810&&940<freqhigh&&freqhigh<=1250) {	printf("t19.txt=\"4\"\xff\xff\xff");}
	       if(730<freqlow&&freqlow<=810&&1250<freqhigh&&freqhigh<=1400) {	printf("t19.txt=\"5\"\xff\xff\xff");}
         	if(730<freqlow&&freqlow<=810&&1400<freqhigh&&freqhigh<=1500) {	printf("t19.txt=\"6\"\xff\xff\xff");}
          	if(730<freqlow&&freqlow<=810&&1500<freqhigh&&freqhigh<=2000) {	printf("t19.txt=\"B\"\xff\xff\xff");}	
 	if(810<freqlow&&freqlow<=895&&940<freqhigh&&freqhigh<=1250) {	printf("t19.txt=\"7\"\xff\xff\xff");}
	if(810<freqlow&&freqlow<=895&&1250<freqhigh&&freqhigh<=1400) {	printf("t19.txt=\"8\"\xff\xff\xff");}	
	if(810<freqlow&&freqlow<=895&&1400<freqhigh&&freqhigh<=1500) {	printf("t19.txt=\"9\"\xff\xff\xff");}	
	if(810<freqlow&&freqlow<=895&&1500<freqhigh&&freqhigh<=2000) {	printf("t19.txt=\"C\"\xff\xff\xff");}	
	   if(895<freqlow&&freqlow<=1200&&940<freqhigh&&freqhigh<=1250) {	printf("t19.txt=\"*\"\xff\xff\xff");}
		   if(895<freqlow&&freqlow<=1200&&1250<freqhigh&&freqhigh<=1400) {	printf("t19.txt=\"0\"\xff\xff\xff");}
			   if(895<freqlow&&freqlow<=1200&&1400<freqhigh&&freqhigh<=1500) {	printf("t19.txt=\"#\"\xff\xff\xff");}
				   if(895<freqlow&&freqlow<=1200&&1500<freqhigh&&freqhigh<=2000) {	printf("t19.txt=\"D\"\xff\xff\xff");}
					
																}
		
		

			delay_ms(20); 
		TransferComplete=0;
		HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_1);   //开启TIM5的捕获通道1,并且开启捕获中断 
		HAL_TIM_Base_Start(&htim3);  //再次开启时钟,开启采样
	  
		}
		

	
	delay_ms(50);
	LED0=!LED0;
	}
}


void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)  //DMA转换完数据中断回调函数
	
{    delay_ms(200);
	LED1=!LED1;
	 TransferComplete=1;
	 HAL_TIM_Base_Stop(&htim3);  //关闭TIM3,采集停止
}

3.基于STM32f407的示波器(LCD屏用的FFT并显示时域和频域两种波形)

/********************************************************************************************************
*	                           ADC1 的数据缓存,大小均为10240
*********************************************************************************************************/
//uint16_t ADC1ConvertedValue[FFT_LENGTH];
	FLAG_T       *g_Flag;      /* 汇总各种标志的结构体指针变量 */
	DSO_T 	     *g_DSO1;	   /* 汇总示波器通道1的结构体指针变量 */
	TRIVOLTAGE_T *g_TrigVol;   /* 汇总各种触发值的结构体指针变量 */

	DSO_T DS01;

	float Freq_sin = 0;

//EMWIN5.22  2D绘图实验
//STM32F4工程----库函数版本
//淘宝店铺:http://mcudev.taobao.com	

//START任务
//设置任务的优先级
#define START_TASK_PRIO				0
//任务堆栈大小 
#define START_STK_SIZE			  128
//任务堆栈
OS_STK	START_TASK_STK[START_STK_SIZE];
//start_task任务
void start_task(void *pdata);

//TOUCH任务
//设置任务优先级
#define TOUCH_TASK_PRIO				2
//任务堆栈大小
#define TOUCH_STK_SIZE				128
//任务堆栈
OS_STK TOUCH_TASK_STK[TOUCH_STK_SIZE];
//touch任务
void touch_task(void *pdata);

//LED0任务
//设置任务优先级
#define LED0_TASK_PRIO 				3
//任务堆栈大小
#define LED0_STK_SIZE				64
//任务堆栈
OS_STK LED0_TASK_STK[LED0_STK_SIZE];
//led0任务
void led0_task(void *pdata);


//EMWINDEMO任务
//设置任务优先级
#define EMWINDEMO_TASK_PRIO		5
//任务堆栈大小
#define EMWINDEMO_STK_SIZE		1024
//任务堆栈
OS_STK EMWINDEMO_TASK_STK[EMWINDEMO_STK_SIZE];
//emwindemo_task任务
void emwin_maintask(void *pdata);

//dsp 任务
//设置任务优先级
#define DSP_TASK_PRIO		4
//任务堆栈大小
#define DSP_STK_SIZE		2048
//任务堆栈
OS_STK DSP_TASK_STK[DSP_STK_SIZE];
//emwindemo_task任务
void dsp_task(void *pdata);


int main(void)
{
	delay_init(168);       	//延时初始化
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 	//中断分组配置
	uart_init(115200);    	//串口波特率设置
	TFTLCD_Init();			//初始化LCD
	TP_Init();				//初始化触摸屏
	W25QXX_Init();			//初始化W25Q16
	LED_Init();   			//LED初始化
	
	FSMC_SRAM_Init(); 		//SRAM初始化
	
	mem_init(SRAMIN); 		//内部RAM初始化
	mem_init(SRAMEX); 		//外部RAM初始化
	mem_init(SRAMCCM);		//CCM初始化
	
	OSInit();  //开始UCOS
	OSTaskCreate(		start_task,  																	//start_task任务
									(void*)0,    																	//参数
									(OS_STK*)&START_TASK_STK[START_STK_SIZE-1], 	//任务堆栈栈顶
									START_TASK_PRIO);  														//任务优先级
	OSStart();  //开启UCOS
}

//START任务
void start_task(void *pdata)
{
	OS_CPU_SR cpu_sr;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC,ENABLE);	//开启CRC时钟				//在所有窗口上使用存储设备
	
	OSStatInit(); //初始化统计任务
	OS_ENTER_CRITICAL();  //进入临界区,关闭中断
	
	OSTaskCreate(emwin_maintask,(void*)0,(OS_STK*)&EMWINDEMO_TASK_STK[EMWINDEMO_STK_SIZE-1],EMWINDEMO_TASK_PRIO);//2D绘图任务				
	OSTaskCreate(touch_task,(void*)0,(OS_STK*)&TOUCH_TASK_STK[TOUCH_STK_SIZE-1],TOUCH_TASK_PRIO); //触摸屏任务
	OSTaskCreate(led0_task,(void*)0,(OS_STK*)&LED0_TASK_STK[LED0_STK_SIZE-1],LED0_TASK_PRIO); //LED0任务
	OSTaskCreate(dsp_task,(void*)0,(OS_STK*)&DSP_TASK_STK[DSP_STK_SIZE-1],DSP_TASK_PRIO); 		//DSP 任务
	
	OSTaskSuspend(OS_PRIO_SELF); //挂起start任务
	OS_EXIT_CRITICAL();  //退出临界区,开中断
}

//2D绘图任务
void emwin_maintask(void *pdata)
{
	(void)pdata;		/* 避免编译器告警 */

		while(1)
		{
			MainTask();
		}
}

//DSP波形处理任务
void dsp_task(void *pdata)
{
		Adc_Init((u32)ADC_INPUT,adcDorpLen,TimeBaseId);         //初始化ADC
    while(1)
    {
			DSO1_WaveTrig(1);
			OSTimeDlyHMSM(0,0,0,1);//延时500ms
    }
}

//触摸屏任务
void touch_task(void *pdata)
{
	KEY_Init();
	while(1)
	{
		if(KEY0 == 0)
		{
			TimeBaseId++;
			if(TimeBaseId>20)TimeBaseId=20;
			while(KEY0 == 0);
			Adc_Init((u32)ADC_INPUT,adcDorpLen,TimeBaseId);
		}
		if(KEY1 == 0)
		{
			TimeBaseId--;
			if(TimeBaseId<1)TimeBaseId=0;
			while(KEY1 == 0);
			Adc_Init((u32)ADC_INPUT,adcDorpLen,TimeBaseId);
		}
		OSTimeDlyHMSM(0,0,0,5);//延时5ms
	}
}

//LED0任务
void led0_task(void *pdata)
{
	while(1)
	{
		LED0 = !LED0;
		OSTimeDlyHMSM(0,0,0,500);//延时500ms
	}
}


4.(基于STM32F4的FFT)幅值,频率相位差

#include "tim_adc_dma_fft.h"
#include "usart.h"
#include "arm_math.h"  
#include "oled.h"  
#include "delay.h" 
#define sampledot  4096
#define FFT_LENGTH		4096		//1024点FFT
#define fft_arr 10                 
#define fft_psc 84                   
const u32  fft_sample_freq=84000000/(fft_arr*fft_psc);  //fft采样频率 为信号的3到6倍   幅值最准确   
float fft_inputbuf[FFT_LENGTH*2];	//FFT输入数组
float fft_outputbuf[FFT_LENGTH];	//FFT输出数组
arm_cfft_radix4_instance_f32 scfft;  
u32 sampledata[sampledot]={0};//高16位保存adc2 pa5, 低16位保存adc1 pa6
float angel=0;
void Tim3_Init(u16 arr,u16 psc)
{
	TIM_TimeBaseInitTypeDef   TIM_TimeBaseInitstruct;          
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);          
	TIM_TimeBaseInitstruct.TIM_Period=arr;   
    TIM_TimeBaseInitstruct.TIM_Prescaler=psc;
	TIM_TimeBaseInitstruct.TIM_CounterMode=TIM_CounterMode_Up;
	TIM_TimeBaseInitstruct.TIM_ClockDivision=TIM_CKD_DIV1;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitstruct);
	//TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE);     
	TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);	
	TIM_Cmd(TIM3,DISABLE);
}

void Adc_Init()
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	ADC_InitTypeDef       ADC_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
  	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC2, ENABLE);	
	 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_5;  //adc 1和2 的通道
	 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	 GPIO_Init(GPIOA, &GPIO_InitStructure);
	 
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,ENABLE);	
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1,DISABLE);
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC2,ENABLE);	
	RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC2,DISABLE); //重置
	ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_InjecSimult;
    ADC_CommonInitStructure.ADC_TwoSamplingDelay =    ADC_TwoSamplingDelay_5Cycles;
    ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
    ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2; 
    ADC_CommonInit(&ADC_CommonInitStructure);
    
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;	
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;  
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	
    ADC_InitStructure.ADC_NbrOfConversion =1;  //通道数
	ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_T3_TRGO;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_Init(ADC2, &ADC_InitStructure);
    ADC_RegularChannelConfig(ADC2, ADC_Channel_5, 1, ADC_SampleTime_3Cycles);
	ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_3Cycles);
		
   ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE); //多路转化完后触发dma 
	ADC_DMACmd(ADC1, ENABLE); 
	ADC_Cmd(ADC1, ENABLE);
    ADC_Cmd(ADC2, ENABLE);
}



void Dma_ADC_Init()
{
	
	
	DMA_InitTypeDef  DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
	DMA_DeInit(DMA2_Stream0);
	DMA_InitStructure.DMA_BufferSize= sampledot;
	DMA_InitStructure.DMA_Channel=DMA_Channel_0; 
	DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralToMemory;	
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable;         
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;	
	DMA_InitStructure.DMA_Memory0BaseAddr= (uint32_t)&sampledata ;//要存入的值	DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_MemoryDataSize= DMA_MemoryDataSize_Word;
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;		
	DMA_InitStructure.DMA_Mode=DMA_Mode_Circular;
	DMA_InitStructure.DMA_PeripheralBaseAddr=(uint32_t)0x40012308; //adc地址
	DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single;
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Word;
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority=DMA_Priority_High;
	
  DMA_Init(DMA2_Stream0, &DMA_InitStructure);
  DMA_ITConfig(DMA2_Stream0, DMA_IT_TC, ENABLE);
  DMA_Cmd(DMA2_Stream0, ENABLE);
	 
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;  //DMA2_Stream0中断
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;  //抢占优先级1
  NVIC_InitStructure.NVIC_IRQChannelSubPriority =0;        //子优先级1
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;            //IRQ通道使能
  NVIC_Init(&NVIC_InitStructure);
}


float freamp[50];//获取各次谐波频率和幅?
void DMA2_Stream0_IRQHandler(void)  
{
	u32 idex;	//用于将采集到的数据赋值给fft_inputbuf[2*idex]的计数	
    float zhiliu2,HZ2,amp2,phase2,zhiliu1,HZ1,amp1,phase1;
	u8 temp[40];
	int i;
	u16   freamplen; // freamp长度的一半
	if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0))  //判断DMA传输完成中断  
    {
		
		TIM_Cmd(TIM3,DISABLE);//失能时钟,进行计算			
		//adc2 pa5
		for(idex=0;idex<sampledot;idex++) //高16位fft,adc2 fft1 //sampledot==4096
		{			
			fft_inputbuf[2*idex]=(u16)(sampledata[idex]>>16)*(3.3/4096);    //生成输入信号实部
			fft_inputbuf[2*idex+1]=0;//虚部全部为0
		}
		arm_cfft_radix4_f32(&scfft,fft_inputbuf);  //fft运算
		arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//把运算结果复数求模得幅值	
		freamplen=fft_getpeak(fft_inputbuf,fft_outputbuf+1,freamp,FFT_LENGTH/2,10,5,0.2);//寻找基波和谐波		
		zhiliu2=fft_outputbuf[0]/FFT_LENGTH;//直流 
		HZ2=freamp[0];//频率
		amp2=freamp[1];//幅度
		phase2=freamp[2];//相位
		freamp[0]=0;freamp[1]=0;freamp[2]=0;
		//adc1 pa6
		for(idex=0;idex<sampledot;idex++) //低16位fft ,adc1 fft2
		{
			 fft_inputbuf[2*idex]=(u16)(sampledata[idex])*(3.3/4096);    //生成输入信号实部
			fft_inputbuf[2*idex+1]=0;//虚部全部为0	
			
		}	
		arm_cfft_radix4_f32(&scfft,fft_inputbuf);  //fft运算
		arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH);	//把运算结果复数求模得幅值
//		for(i=0;i<FFT_LENGTH;i++)
//{
//printf("fft_outputbuf[%d]:%f\r\n",i,fft_outputbuf[i]);
//}
		freamplen=fft_getpeak(fft_inputbuf,fft_outputbuf+1,freamp,FFT_LENGTH/2,10,5,0.2); //寻找基波和谐波	
		zhiliu1=fft_outputbuf[0]/FFT_LENGTH;//直流      
		HZ1=freamp[0];//频率
		amp1=freamp[1];//幅度
		phase1=freamp[2];//相位
		freamp[0]=0;freamp[1]=0;freamp[2]=0;	
		printf("zhiliu1:%.2f\r\n",zhiliu2);
		printf("HZ1:%.2f\r\n",HZ2);
		printf("amp1:%.2f\r\n",amp2);	
		OLED_ShowString(5,5,"saHZ:",12);      
		sprintf(temp,"%d",fft_sample_freq);    //fft采样频率   
		OLED_ShowString(30,5,temp,12);	
		OLED_ShowString(5,20,"zl:",12);
		sprintf(temp," %.2f",zhiliu1); //直流分量
		OLED_ShowString(20,20,temp,12);
		OLED_ShowString(5,35,"HZ:",12);   //频率
		sprintf(temp,"%.2f   ",HZ1);
		OLED_ShowString(30,35,temp,12);
		OLED_ShowString(5,50,"amp:",12);  //幅值  
		sprintf(temp,"%.2f",amp1); 
		OLED_ShowString(30,50,temp,12);	
		
	angel=phase2-phase1;
	if(angel>180) angel=angel-180;
	if(angel<-180) angel=angel+180;
		OLED_ShowString(60,20,"ag:",12);
		sprintf(temp,"%.2f    ",angel);
		OLED_ShowString(75,20,temp,12);
		OLED_Refresh_Gram();
 // printf("取样频率%d\r\n",fft_sample_freq);
		//printf("直流分量%f\r\n",fft_outputbuf[0]/FFT_LENGTH);
		for(idex=0;idex<freamplen;idex++)
			{
				//printf("HZ:%.2f   amplitude:%.2f 相位:%.2f \r\n",freamp[3*idex],freamp[3*idex+1],freamp[3*idex+2]);//没有求解出来
				//printf("HZ:%.2f   amplitude:%.2f \r\n",freamp[3*idex],freamp[3*idex+1]);
			}	
		//TIM_Cmd(TIM3,ENABLE);//进行下一次读取           
		DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);
		
	}	
}

void Data_Init()
{
	u32 idex;
	float temp;	
	Adc_Init();
	Dma_ADC_Init();
	uart_init(115200);
	arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数     //FFT_LENGTH 4096
	Tim3_Init(fft_arr-1,fft_psc-1);
}

//获取峰值
int fft_getpeak(float *inputx,float *input,float *output,u16 inlen,u8 x,u8 N,float y) //  intlen 输入数组长度,x寻找长度
{                                                                           
	int i,i2;
	u32 idex;  //不同于上一个函数中的,因为他们在不同的函数中被定义
	float datas;
	float sum;
	int outlen=0;
	for(i=0;i<inlen-x;i+=x)
	{
		arm_max_f32(input+i,x,&datas,&idex);   
		if( (input[i+idex]>=input[i+idex+1])&&(input[i+idex]>=input[i+idex-1])&&( (2*datas)/FFT_LENGTH )>y)   
		   {
			   sum=0;   
			   for(i2=i+idex-N;i2<i+idex+N;i2++)   
			   {
				   sum+=input[i2];          
			   }        
			   if(1.5*sum/(2*N)<datas)       
			   {                                                                                             
				     output[3*outlen+2] = atan2(inputx[2*(i+idex+1)+1],inputx[2*(i+idex+1)])*180/3.1415926f;				   
				     output[3*outlen+1] = 1.0*(2*datas)/FFT_LENGTH;   //计算幅度
					 output[3*outlen] = 1.0*fft_sample_freq*(i+idex+1)/FFT_LENGTH;//计算频率
					 outlen++;				   
			   }                                                                                               
               else continue;			   
		   }
			
		else continue;
		
	}
	return outlen;	
}

心得体会

过程真的比结果重要,放下功利心,你收获的将是满满的知识和不断开阔的知识面。而不是一张毫无价值的荣誉证书。岁月催人老,愿各位仁兄珍惜时间,及时行乐。早日晋升为老白。理论指导实践,这句话一点都不假。学好专业课和基础课真的超级超级重要,我这边比赛结束,就准备俯下身子,把我以前的的专业课和基础课再好好的过一遍。发现最近几年的电赛对大学生的理论功底要求越来越高,比如这一次我们不太会用的最小二乘法,以及电路精度的问题,想要解决这些比较棘手的问题,理论功底一定要过关.

最后

列表里的资源都是调试过的,没有问题,STM32F4系列的板子都可以拿来用,有任何问题的可以告诉我企鹅q2036795517我是只发布高质量文章的李白有点儿黑。

物联沃分享整理
物联沃-IOTWORD物联网 » 基于STM32F4的FFT+测频率幅值相位差,波形显示,示波器,时域频域分析相关工程

发表评论