基于STM32 HAL库的FFT计算与数学运算:幅值、频率、均方根、平均值、最大值、最小值、峰峰值与标准差

一、用STM32进行FFT计算与数学运算的过程

1. 信号采集
首先,我们需要使用STM32的ADC模块来采集模拟信号,比如三相交流电。ADC将模拟信号(如电压或电流)转换为数字信号,供后续处理。

采样数量:FFT的计算通常需要2的整数次幂的采样点数(如1024、2048)。采样点数越多,频率分辨率越高。
  
采样频率:采样频率必须至少是信号频率的两倍(奈奎斯特定理)。例如,分析50Hz的信号时,采样频率应至少为100Hz,但通常使用更高的采样频率,下的例子采集50hz的交流信号,采用的是2500hz,以保证计算精度。

2. FFT变换
采集到的时域数据通过FFT算法进行处理,转换为频域信息。ARM-DSP库中有现成的FFT函数,可以简化计算过程。关于FFT可以查看上一篇关于FFT的介绍:STM32上实现FFT算法精准测量正弦波信号的幅值、频率和相位差(标准库)​​​​​​​

3. 运算结果:幅值、频率和均方根、平均值、最大值、最小值、峰峰值与标准差

幅值:信号的振幅大小,表示每个频率成分的强度。基波的幅值代表主要的电压或电流值。
频率:FFT能帮助我们识别信号中的不同频率成分,如电网中的50Hz基波及其他谐波。

均方根(RMS)运算公式:RMS = sqrt( (1/N) * Σ(x_i^2) )
其中,N 是数据点的数量,x_i 是每个数据点的值,Σ 表示求和。
 
平均值运算公式:平均值 = (1/N) * Σ(x_i)
其中,N 是数据点的数量,x_i 是每个数据点的值。
 
最大值运算公式:最大值 = max(x_i)
其中,x_i 是数据点集合中的每个值,max 表示取最大值操作。
 
最小值运算公式:
最小值 = min(x_i)
其中,x_i 是数据点集合中的每个值,min 表示取最小值操作。
 
峰峰值运算公式:
峰峰值 = 最大值 - 最小值
其中,最大值和最小值分别是从数据点集合中计算得出的。
 
标准差运算公式:
标准差 = sqrt( (1/N) * Σ((x_i - 平均值)^2) )
其中,N 是数据点的数量,x_i 是每个数据点的值,平均值是之前计算得出的数据点的平均值。

4. 处理流程概述

整个处理流程如下:

  • 信号采集:定时器触发ADC采样交流电信号。
  • 采样与ADC转换:STM32的ADC将模拟信号转为数字信号。
  • DMA传输:使用DMA自动传输采样数据到内存。
  • FFT计算:利用FFT将时域数据转换为频域数据。
  • 结果提取:从FFT结果中提取幅值、频率等信息。

二、采样频率设定-定时器3(TIM3)配置

1.时钟源:定时器3的时钟源被设置为84MHz。这意味着定时器的时钟频率是84,000,000 Hz。

2.预分频器(Prescaler):预分频器的值被设置为3(通过4-1得到,因为预分频器的值通常是通过设置其寄存器的一个字段来实现的,该字段的值是所需分频数减1)。预分频器的作用是将定时器的时钟频率降低到一个更低的频率,以便能够更容易地实现所需的计数周期。因此,经过预分频后的时钟频率为84,000,000 / 4 = 21,000,000 Hz。

3.计数器周期(AutoReload Register):AutoReload Register的值被设置为8399(通过8400-1得到)。这个寄存器定义了定时器计数到多少时会产生一个更新事件(或称为溢出事件),并重新从0开始计数。因此,定时器的计数周期是8400个时钟周期。

4.采样频率:

采样率(Fs)是指每秒对信号进行采样的次数。采样率是2500Hz,意味着每秒采样2500个点。

信号频率(F)是指信号的周期性变化的速率。测量信号频率是50Hz,意味着信号每秒变化50个周期。

为了计算FFT的采样频率,使用了两个宏定义:fft_arrfft_psc

  • fft_arr:这个宏被定义为8400,它实际上代表了AutoReload Register的值加1(即计数周期的实际值)。这个值用于计算定时器溢出一次所需的时间。

  • fft_psc:这个宏被定义为4,它代表了预分频器的值加1(即预分频的实际倍数)。以下公式来计算FFT的采样频率:


const uint32_t fft_sample_freq = 84000000 / (fft_arr * fft_psc);
将fft_arr和fft_psc的值代入公式中,得到:

fft_sample_freq = 84000000 / (8400 * 4) = 84000000 / 33600 = 2500 Hz

FFT的采样频率是2500 Hz。这意味着定时器每0.0004秒(即400微秒)溢出一次,从而为FFT提供一个新的采样点。

一个信号周期内的采样点数(N)可以通过以下公式计算:

N = Fs / F

将已知的采样率(Fs = 2500Hz)和信号频率(F = 50Hz)代入公式中,我们可以得到:

N = 2500Hz / 50Hz = 50

因此,在2500Hz的采样率下,50Hz信号一个周期内将有50个采样点。

三、ADC配置

ADC1配置,common settings :independent,当ADC配置为“independent”模式时,它意味着该ADC模块是独立工作的,不与其他ADC模块共享任何资源或配置。
​​​​​​​external trigger conversion source :timer3 tirgger out event。Timer3是一个定时器模块,它可以生成定时中断或触发输出事件。当Timer3达到某个预设的条件(如计数到某个值)时,它会触发一个事件,这个事件被用作ADC1的转换触发源。

中断DMA

四、程序实现

硬件:正点原子探索者 V3 STM32F407 开发板,下面是核心代码,代码中使用了ARM提供的数学库(arm_math.h)来实现FFT算法,以及STM32的标准库库来配置定时器、ADC和DMA。通过这些配置,系统能够高效地采集和处理模拟信号,分析其频谱特性。以下为核心代码,完整代码请在资源下载。

/*
*********************************************************************************************************
*	函 数 名: WaveProcess
*	功能说明: //WaveProcess 功能说明: 波形通道均方根,平均值,最大值,最小值,峰峰值,频率,标准差,幅值,相位的计算
*	形    参: 无。
*	返 回 值: 无
*********************************************************************************************************
*/
void WaveProcess(void)
{
	uint16_t    i; 
  data.WaveMin = 4095;
	uint16_t idex=0;	//用于将采集到的数据赋值给fft_inputbuf[2*idex]的计数	
  float tempdata=0;
	uint8_t temp[40];	
	uint16_t   freamplen; // freamp长度的一半

	
	/* 自动触发模式才计算FFT */
	arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT相关参数     //FFT_LENGTH 4096

	//adc1 pa5
	for(idex=0;idex<FFT_LENGTH;idex++) //adc1 fft1 //FFT_LENGTH==4096
	{			
		fft_inputbuf[2*idex]= (uint16_t)g_adc_dma_buf[idex]*(3.3/4096);      //生成输入信号实部
		fft_inputbuf[2*idex+1]=0;//虚部全部为0
	}
		arm_cfft_radix4_f32(&scfft,fft_inputbuf);	//FFT计算(基4)
		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); //寻找基波和谐波	

	
	data.WaveOffset=fft_outputbuf[0]/FFT_LENGTH;//偏置电压      
	data.WaveFreq=freamp[0];//频率
	data.WaveAmplitude=freamp[1]+data.WaveOffset;//幅度
	data.WavePhase=freamp[2];//相位
	freamp[0]=0;freamp[1]=0;freamp[2]=0;
			
	/* 求4096个数值的最大值和最小值 */
	for (i = 0; i < FFT_LENGTH; i++) 
	{
		data.WaveMean += g_adc_dma_buf[i];
	
		data.WaveRMS  += g_adc_dma_buf[i]*g_adc_dma_buf[i];
		
		if(g_adc_dma_buf[i] < data.WaveMin)
		{
			data.WaveMin = g_adc_dma_buf[i];
		}
		
		if(g_adc_dma_buf[i] > data.WaveMax)
		{
			data.WaveMax = g_adc_dma_buf[i];
		}
	}	
	/* 求RMS 均方根(RMS,Root Mean Square)*/
	data.WaveRMS = sqrt(data.WaveRMS/FFT_LENGTH)* 3.3f / 4095;
	//data.WaveMean = data.WaveMean/FFT_LENGTH;
		/* 求平均值 */
	data.WaveMean = data.WaveMean/FFT_LENGTH* 3.3f / 4095;
		/* 求标准差 */
	for (i = 0; i < FFT_LENGTH; i++) 
	{
		tempdata =g_adc_dma_buf[i];
		data.WaveStd  += pow((tempdata* 3.3f / 4095-data.WaveMean),2);	
	}	
	data.WaveStd = data.WaveStd/FFT_LENGTH;
	data.WaveStd =sqrt(data.WaveStd);

	
	/* 求最大值 */
	data.WaveMax =  data.WaveMax * 3.3f / 4095;
	
	/* 求最小值 */
	data.WaveMin = data.WaveMin *3.3f / 4095;
	
	/* 求峰峰值 */
	data.WavePkPk = data.WaveMax - data.WaveMin;		
	
	printf("fft_sample_freq:%d\r\n",fft_sample_freq);    //fft采样频率 	
	printf("WaveRMS:%.3f\r\n", data.WaveRMS); //数据打印,查看RMS结果
	printf("WaveMean:%.3f\r\n",data.WaveMean);//数据打印,查看平均值
	printf("WaveStd:%.3f\r\n",data.WaveStd);//数据打印,查看标准差
	printf("WaveMax:%.3f\r\n", data.WaveMax);//数据打印,查看最大值
	printf("WaveMin:%.3f\r\n", data.WaveMin);//数据打印,查看最小值 
	printf("WavePkPk:%.3f\r\n", data.WavePkPk);//数据打印,查看峰峰值
	printf("WaveOffset:%.3f\r\n",data.WaveOffset); //偏置电压 
	printf("WaveFreq:%.3f\r\n",data.WaveFreq);   //频率	
	printf("WaveAmplitude:%.3f\r\n",data.WaveAmplitude); //幅值    

}

// 获取FFT峰值
int fft_getpeak(float *inputx,float *input,float *output,uint16_t inlen,uint8_t x,uint8_t N,float y) //  intlen 输入数组长度,x寻找长度
{                                                                           
	int i,i2;
	uint32_t 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;
}
  1. 定时器初始化 (Tim3_Init): 配置定时器3,用于控制ADC的采样频率。

  2. ADC初始化 (Adc_Init): 配置两个ADC(ADC1和ADC2),用于采集模拟信号。ADC1和ADC2分别连接到不同的通道,采集不同的模拟信号。

  3. DMA初始化 (Dma_ADC_Init): 配置DMA,用于将ADC采集的数据直接传输到内存中,减少CPU的负担。

  4. 数据初始化 (Data_Init): 调用上述初始化函数,完成系统的基本配置。

  5. FFT峰值获取函数 (fft_getpeak): 该函数用于在FFT结果中寻找峰值,这些峰值代表了信号中的基波(主要频率成分)。下面是核心中的核心。

    		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); //寻找基波和谐波
  6. DMA中断服务函数 (DMA2_Stream0_IRQHandler): 当DMA传输完成时,该函数会被调用。它负责执行FFT算法,计算信号的偏置电压、幅值、频率、均方根、平均值、最大值、最小值、峰峰值与标准差的相关信息并用串口1打印。 

五、现象

按下复位键,串口输出采集的4096个数据点,通过excel图表显示。的确一个周期内将有50个采样点。

1.信号发生器输入信号源,信号1:50Hz的信号的,幅度1V,偏置电压0.5V,相位0°;

2.串口收到测量结果与输入信号源的实际接近。

3.信号2:50Hz的信号的,幅度2V,偏置电压1V,相位0°;

六、总结 

通过上述介绍,我们探讨了如何使用STM32微控制器执行FFT计算,以提取信号的幅值、频率、均方根、平均值、最大值、最小值、峰峰值与标准差,一些示波器测量的参数。


希望这些内容能够为大家提供有价值的参考和指导。在实际应用中,理解和运用FFT的原理和技巧,将有助于我们更有效地处理和分析各种复杂的信号。

在使用STM32进行FFT测量频率时,可以通过以下步骤进行操作。首先,使用STM32F4系列单片机和陶晶驰3.5寸T0系列串口屏。通过触摸屏上的按键开启测量,并显示信号的峰峰值频率和波形。为了确定采样率,需要测量频率变化的信号,并确定时钟触发频率。可以使用ADC双通道测量两路信号,并使用DMA传输至一个数组内存中。然后,可以显示波形、计算峰峰值,并对数据进行FFT,以分析频谱并确定波形的名称,如正弦波、三角波、方波、脉冲波、锯齿波等。\[2\] 在进行频率测量时,需要注意一些问题。首先,使用单片机自带的ADC进行采样时,可能会遇到信号度太小或太大的问题。可以通过使用自动增益控制电路来解决这个问题。其次,对于频率变化范围较大的信号,固定的采样频率可能导致波形失真和频谱混叠。为了解决这个问题,可以先确定信号的频率范围,并使用MCU的输入捕获功能来测量频率。对于规则信号,如正弦波、方波、三角波等,可以精确测量其频率。对于不规则信号,如DTMF信号,可以大致获得其频率。通过这样的方法,在有限的采样点数下可以获得较好的频率分辨率。\[3\] 因此,使用STM32进行FFT测量频率时,可以通过合适的硬件配置和信号处理方法来获得准确的频率测量结果。 #### 引用[.reference_title] - *1* [stm32f1单片机上用FFT测量信号频率(高精度、过程详细)](https://blog.csdn.net/weixin_43368814/article/details/103552114)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [STM32F4时钟触发ADC双通道采样DMA传输进行FFT+测频率+采样频率可变+显示波形(详细解读)](https://blog.csdn.net/qq_45620831/article/details/110819495)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值