STM32-三重ADC同步采样-FFT测信号相位差

STM32---adc-fft-信号相位差

目录

STM32---adc-fft-信号相位差

前言:

基础知识:

正文:

时钟配置:

基础配置:

串口配置:

TIM定时器:

ADC配置:

 DMA配置:

代码示例:

结语:


前言:

        在信号处理领域,测量两个信号之间的相位差在许多应用中至关重要,如电力系统的同步、振动分析、以及通信系统中的相位调制。STM32系列微控制器,特别是STM32F407,具有强大的ADC模块,可以实现多路同步采样。通过使用三重ADC同步采样,并结合快速傅里叶变换(FFT),我们可以精确地测量信号之间的相位差。本次将分享如何使用STM32,利用三重ADC同步采样模式,配合FFT测信号相位差。本教程使用stm32f4,基于cubemx、keil5开发。(相位差这一概念,存在于频率相同,相位不同的信号中)


基础知识:

有关FFT的使用方法,大家可以先看看这篇博客

——《stm32-HAL库-fft-测频率幅度

        STM32系列微控制器(如STM32F407)提供了多个ADC模块,允许用户配置它们以同步方式采样多个通道。STM32的多ADC模块可以通过一个公共触发源(例如一个定时器)来实现同步启动,从而保证多ADC通道的采样在同一时间点进行。

正文:

关于时钟,串口,定时器这些比较常规的配置,我就不多做解释了,在下面这篇文章中我有过详细的介绍,如果需要的话,大家可以先看看这篇文章了解一下。下面ADC的配置比较重要!!!

——《stm32-HAL 电赛信号教程

时钟配置:

基础配置:

串口配置:

TIM定时器:

这里我开的是2M采样率。128M/64=2M

ADC配置:

 DMA配置:

到此,配置完成。

代码示例:

这里就以ADC1和ADC2两个ADC采集为例,获得信号的相位差。但实际这个程序也采集了ADC3的数据,处理方法跟这两一样的哈。

(1)宏定义

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#define  FFT_LENGTH		   1024
#define  ADC1_DMA_Size	   3072
#define  ADC2_DMA_Size	   1024

(2)定义数组变量

int   freq1=0;   		//获得ADC1采集信号的频率
int   freq2=0;   		//获得ADC2采集信号的频率
float phase_1=0; 		//获得ADC1采集信号的相位
float phase_2=0; 		//获得ADC2采集信号的相位
float phase_differ=0;	//相位差
 int SAM_FRE=2000000;                  //采样率-与cubemx配置的数据一致
 float    fft_inputbuf1 [FFT_LENGTH*2];	
 float    fft_inputbuf2 [FFT_LENGTH*2];	
 float    fft_outputbuf1[FFT_LENGTH];
 float    fft_outputbuf2[FFT_LENGTH];
 uint32_t ADC1_ConvertedValue[1024][3];//存放各ADC采集到的数据

(3)数据处理

打开ADC-DMA开启数据采集和传输。

  HAL_ADC_Start(&hadc1);
  HAL_ADC_Start(&hadc2);
  HAL_ADC_Start(&hadc3);
  
  HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t*)ADC1_ConvertedValue,3072);
  HAL_TIM_Base_Start(&htim8);                        
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {	

 主函数main中

  /* USER CODE BEGIN WHILE */
  while (1)
  {	 
	  if(DMA_FLAG==1)
	  {
	     DMA_FLAG=0;
		  
		 HAL_TIM_Base_Stop(&htim8); //关闭ADC-tim,防止数据处理时数据变化
		 HAL_ADCEx_MultiModeStop_DMA(&hadc1);
		 HAL_ADC_Stop(&hadc1);
		 HAL_ADC_Stop(&hadc2);
		 HAL_ADC_Stop(&hadc3);
		  
		  for(int i=0;i<1024;i++)
		  {
			  //printf("%d,%d,%d\r\n",ADC1_ConvertedValue[i][0],ADC1_ConvertedValue[i][1],ADC1_ConvertedValue[i][2]);
			  fft_inputbuf1[i*2]= (ADC1_ConvertedValue[i][0])*3.3/4095;//将ADC1采集的数据放到FFT待处理数组中
			  fft_inputbuf1[2*i+1]=0;
			  fft_inputbuf2[i*2]= (ADC1_ConvertedValue[i][1])*3.3/4095;//将ADC2采集的数据放到FFT待处理数组中
			  fft_inputbuf2[2*i+1]=0;			  
		  }
	  
		  	  arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf1,0,1); //ADC1采集的数据FFT运算
			  arm_cmplx_mag_f32(fft_inputbuf1, fft_outputbuf1, FFT_LENGTH); 
			  
			  arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf2,0,1); //ADC2采集的数据FFT运算
			  arm_cmplx_mag_f32(fft_inputbuf2, fft_outputbuf2, FFT_LENGTH);
		  
		  freq1= findmax((float*)fft_outputbuf1,1024,2);   //获得ADC1采集信号的频率
		  freq2= findmax((float*)fft_outputbuf2,1024,2);   //获得ADC2采集信号的频率
		  		  
		  phase_1=Phase_atan(fft_inputbuf1,freq1);         //获得ADC1采集信号的相位
		  phase_2=Phase_atan(fft_inputbuf2,freq2);         //获得ADC2采集信号的相位
		  phase_differ=phase_1-phase_2;					   //计算相位差
		  
		  if(phase_differ>180)                             //相位调整
		  {
			  phase_differ=phase_differ-360;
		  }	
          if(phase_differ<-180)
		  {
			  phase_differ=phase_differ+360;
		  }			  
		  printf("phase_differ = %f\r\n",phase_differ);
		  
		  HAL_Delay(1000);

		    HAL_ADC_Start(&hadc1);
			HAL_ADC_Start(&hadc2);
		    HAL_ADC_Start(&hadc3);
		   
			HAL_ADCEx_MultiModeStart_DMA(&hadc1,(uint32_t*)ADC1_ConvertedValue,3072);
		    HAL_TIM_Base_Start(&htim8);
			
	  }		  	  			  	  	  	  

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */	  
  }

信号频率计算函数

int findmax(float*arry,uint16_t len,uint16_t s) //s表示从第几个数开始找
 {
   int i;
   int j=s;
   for(i=s;i<len/2;i++)
   {
	  if(arry[i]>arry[j])
	  {
	    j=i;
	  }		  
   }
   return j;
 }

相位计算函数:FFT计算后的fft_inputbuf数组中,虚部除实部就是相位。

//相位计算函数
float Phase_atan(float32_t *inputSignal,uint32_t index)
{
        return   atan(inputSignal[2*index+1] / inputSignal[2*index]) / PI * 180;
}

OKOK,代码编写完成,大家可以打ADC1与ADC2采集的数据使用vofa+打印出来,用函数信号发生器产生两个频率相同,相位差为0的信号,可以发现两图案是重叠的,证明确实是同步采样。

结语:

        测量相位的内容较多且较为复杂,可以有点难理解,希望我的分享对大家能有帮助。如果需要完整代码,可以私信我或者留言,有疑问或建议欢迎留言!(这些代码都是在实验室验证过的哟)

STM32F4上进行FFT频率、幅值和相位差的步骤如下: 1. 采集信号:使用ADC模块采集需要进行FFT分析的信号,并将采样数据存储到数组中。 2. 傅里叶变换:使用FFT库对采集到的数据进行快速傅里叶变换(FFT),得到频域数据。 3. 计算频率:利用FFT得到的频域数据计算出对应的频率。 4. 计算幅值:计算得到的频域数据可以通过计算其绝对值来得到其对应的幅值。 5. 计算相位差:利用FFT得到的频域数据可以计算出各个频率对应的相位,从而计算出两个信号之间的相位差。 以下是一个简单的示例代码,可供参考: ```c #include <stdio.h> #include "stm32f4xx.h" #include "arm_math.h" #define N 256 // 采样点数 #define Fs 8000 // 采样频率 #define PI 3.1415926 float32_t input[N], output[N]; uint32_t max_index; // FFT变换 void FFT(float32_t* input, float32_t* output) { arm_cfft_radix4_instance_f32 S; arm_cfft_radix4_init_f32(&S, N, 0, 1); arm_cfft_radix4_f32(&S, input); arm_cmplx_mag_f32(input, output, N); // 计算幅值 arm_max_f32(output, N, &max_index); // 找到最大值的下标 } int main() { // 初始化ADC模块,配置GPIO口为模拟输入 ADC_InitTypeDef ADC_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_InitStructure); ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfConversion = 1; ADC_Init(ADC1, &ADC_InitStructure); // 采集信号并进行FFT变换 int i; for (i = 0; i < N; i++) { ADC_SoftwareStartConv(ADC1); while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET); input[i] = ADC_GetConversionValue(ADC1); } FFT(input, output); // 计算频率和相位差 float32_t freq = max_index * Fs / N; float32_t phase_diff = atan2f(input[max_index*2+1], input[max_index*2]); printf("Frequency = %f Hz\n", freq); printf("Amplitude = %f\n", output[max_index]); printf("Phase difference = %f degrees\n", phase_diff * 180 / PI); while (1); return 0; } ``` 此代码仅作为示例,具体实现还需要根据具体需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值