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

这篇具有很好参考价值的文章主要介绍了基于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我是只发布高质量文章的李白有点儿黑。文章来源地址https://www.toymoban.com/news/detail-780503.html

到了这里,关于基于STM32F4的FFT+测频率幅值相位差,波形显示,示波器,时域频域分析相关工程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32测相位差(根据时间差)

             两路方波输入到stm32的两路定时器通道,通过检测高电平到来的时间差从而算出相位差, 公式  相位差=360*频率*(时间差)         如果要测正弦波,可以通过电压比较电路转为方波          定时器初始化及定时器中断代码: 在主程序中求其中一路信号的

    2024年02月16日
    浏览(30)
  • [GD32F4]基于GD32固件库移植cherryusb[STM32F4]

    [GD32F4]基于GD32固件库移植cherryusb[STM32F4] 使用开发板是淘宝买的不知名开发板,没什么好说的,具体的型号是GD32F450VET6。 使用的cherryusb版本是0.9.0版本。 使用的GD32官方固件库版本是:GD32F4xx_Firmware_Library_V3.0.4 cherryusb最牛的地方在于抛弃掉所有的依赖,只需要知道芯片的usb中断

    2024年02月06日
    浏览(49)
  • (DDS)正弦波形发生器——幅值、频率、相位可调(一)

    设计一个幅值、频率、相位均可调的正弦波发生器。 频率每次增加1kHz。 相位每次增加 2*PI/256 幅值每次增加两倍 DDS的核心原理。 分别使用两种方式完成频率可调(a、b),并且进行对比(c),最后对b进行优化(d)。 完成赋值、频率、相位可调的正弦波形发生器。(见文章

    2023年04月08日
    浏览(36)
  • 基于STM32F4的心电监护仪

    从题目中可以看出该课题来源于 2020年省电赛A题的无线运动传感器节点的设计 ,该作品得过湖北省电赛二等奖,同时也是我本科毕业设计,这里我把自己做的关于心电部分的工作进行一次总结,也对我的大学四年进行一次总结。 处理器板子的选择 本研究的处理器模块选择

    2024年02月03日
    浏览(40)
  • 基于STM32F4开发的智能台灯

    写这篇博客的目的有2个,首先是记录一下学习STM32大半年来的第一个自己动手开发的项目,整理一下开发过程和思路;其次也是希望可以和更多的同行交流开发经验,有什么问题可以多多讨论,集思广益,共同进步~ 开发的智能台灯功能有2个: 1.手动模式:可通过按键调节

    2024年02月05日
    浏览(41)
  • 基于STM32F4的多摩川协议通讯

    1、介绍        之前项目刚好有用到禹衡家的17位绝对值编码器,趁着周末有时间整理一下开发思路,同时也分享出来给有需要的人做做参考。        说回编码器,我们都知道在伺服控制中,为了获取更高的位置精度,完成更精细的绝对定位,通常会采用绝对式光电编码

    2024年02月11日
    浏览(44)
  • STM32F4 基于USART串口的蓝牙通信

    目录 一、硬件资源 连接方案 其他配置 二、实验原理 基本定义 USART介绍 USART工作原理 数据发送 数据接收 蓝牙HM-10配置 三、代码部分 usart.c usart.h Serial.c Serial.h main.c 结语 STM32F401,OLED,蓝牙hm10 连接方案 设备1的TX与设备2的RX连接,这样设备1发送的数据可以被设备2接收到。

    2024年01月17日
    浏览(46)
  • 【stm32开发笔记】基于HAL库的STM32F4添加DSP库

    本文分两种方法添加DSP库:1.CubeMX直接配置ioc添加; 2.KEIL内添加; 简述:补齐全部lib库-添加DSP包-使能DSP勾选-添加头文件及魔术棒配置-测试 1.补齐lib库。( 如果使用直接默认添加的库,是不支持FPU的,所以需要补齐后找到所需的lib文件进行替换,在MX的工程管理栏,选择复制所

    2024年02月16日
    浏览(54)
  • 基于STM32F4的CANOpen移植教程(超级详细)

    本专题相关教程: 基于STM32F4的CANOpen移植教程 基于STM32F4的CANopen快速SDO通信 linux下CANopen for python的使用 基于Linux C的CANopen移植 CANopen补充–时间计算出错 CANopen补充–主站检测节点是否在线 为了在STM32F4上能够运行CANopen(CanFestival),跟着网上的教程操作,发现总是不够详细。

    2024年02月02日
    浏览(43)
  • 基于stm32f407的示波器+FFT频谱分析

    1 设计思路 2 DMA传输ADC采样值 使用DMA直接将ADC-DR中的数据传输到ADC数据缓存区,节省cpu资源,高速AD采集,代码如下: 3 ADC定时器触发(可修改ADC采样率) 为了实现ADC采样率可调,我将AD的出发方式设置为定时器触发,使用TIM3来触发adc采集,首先初始化定时器,先预设几种初

    2024年02月05日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包