宝安自适应网站建设,asp网站建设,试析媒体网站品牌建设,互联网营销师是什么在研究声音、电力或任何形式的波形时#xff0c;我们常常需要穿过表面看本质。FFT#xff08;快速傅里叶变换#xff09;就是这样一种强大的工具#xff0c;它能够揭示隐藏在复杂信号背后的频率成分。本文将带你走进FFT的世界#xff0c;了解它是如何将时域信号转化为频域…在研究声音、电力或任何形式的波形时我们常常需要穿过表面看本质。FFT快速傅里叶变换就是这样一种强大的工具它能够揭示隐藏在复杂信号背后的频率成分。本文将带你走进FFT的世界了解它是如何将时域信号转化为频域信号如何使用STM32F407微控制器和FFT来分析正弦信号的幅值、频率和相位差。
一、FFT介绍
FFT快速傅里叶变换是一种将信号从时域随时间变化的信号转换为频域不同频率成分的信号的算法。做一个比喻信号食材FFT刀频域切好的食材。时域信号想象你拿到了一块复杂的食材比如一只未处理的鱼里面有骨头、肉、皮等。这就是时域信号它包含所有部分但你还不能清楚地看到每个成分。FFT这就像一把精准的刀它能快速地把鱼切成不同的部分肉、骨头、皮等。FFT就像这把刀把复杂的信号拆分为不同的频率成分让我们能够看到信号中隐藏的细节。频域信号处理后的结果就像分好类的食材——你清楚地看到了鱼的骨头、肉和皮知道每个部分的大小幅值和具体成分频率。这样你就可以根据需要进一步处理这些“分好类的食材”。FFT像一把高效的刀能快速、精准地将复杂的信号“解剖”成简单、清晰的频率部分。其优势在于
1.实现频域分析FFT将时域信号转换为频域使我们能够识别信号中的频率成分。对于复杂信号时域分析可能难以识别其频率特征而频域分析如滤波、频谱分析、数据压缩则能直观地展现信号的频率内容。
2.提高效率直接计算离散傅里叶变换DFT运算复杂度较高而FFT通过优化算法显著提升计算效率适用于单片机中实时数据处理。
FFT广泛应用于音频处理、通信系统、图像处理、医学成像、地震数据分析等领域。在电力信号处理中FFT可帮助清晰识别信号中的基波和谐波成分协助检测电网中的频率偏移和谐波干扰问题。
二、时域分析 vs 频域分析
时域时域是我们通常看到的信号比如正弦波随时间波动。用电压表测量交流电压时指针上下波动就是时域信号的表现。时域分析只能告诉我们信号的瞬时变化无法揭示信号的频率组成。 频域频域是一种数学上的表示方式用于分析信号的频率成分。在频域中任何复杂的时域信号都可以分解为正弦波的叠加因为正弦波是频域中唯一存在的基本波形。 时域分析这是你在电压表或示波器上看到的信号波形它显示信号如何随时间变化。时域分析的主要缺点是无法直接告诉你信号中包含哪些频率成分。
频域分析通过FFT我们可以将这些时域信号分解为不同的频率成分就像把一首歌分解成各个音符。可以帮助我们找到信号中的主要频率如50Hz的基波和其他高频谐波。
三、FFT的实部和虚部
正弦信号可以用以下数学表达式表示 x(t)A⋅sin(ωtθ) 其中
A 是振幅表示信号的最大值。ω 是角频率单位为弧度/秒ω2πf其中 f 为频率。θ是相位角表示信号的初始偏移。 x(t)A⋅sin(ωtθ) 本身是一个实数函数因为它在任何时间点 t 的值都是实数。这个表达式没有直接包含虚数部分所以可以说它的“虚部”是0。
FFT 变换后得到的是复数Rej⋅Im 包括实部和虚部
实部 (Re)与余弦分量相关决定信号的振幅。虚部 (Im)与正弦分量相关影响信号的相位。
计算幅值和相位 幅值表示信号在某频率下的强度通过以下公式计算幅值 sqrt(Re^2 Im^2) 相位表示信号相对于参考信号的偏移通过以下公式计算 相位 atan2(Im, Re)
通过 FFT 的实部和虚部我们可以准确地获取信号的幅值和相位从而深入理解信号的频率特性包括基波和谐波的影响。 基波信号的主要频率成分。例如电网的基波通常是50Hz或60Hz它代表了信号的基本频率。 谐波基波频率的整数倍例如基波的2倍、3倍频率等。谐波会影响电力设备的正常运行可能导致设备过热或损坏。 四、如何用STM32进行FFT计算
让我们一步步看看整个计算流程。
1. 信号采集 首先我们需要使用STM32的ADC模块来采集模拟信号比如三相交流电。ADC将模拟信号如电压或电流转换为数字信号供后续处理。
采样数量FFT的计算通常需要2的整数次幂的采样点数如1024、2048。采样点数越多频率分辨率越高。 采样频率采样频率必须至少是信号频率的两倍奈奎斯特定理。例如分析50Hz的信号时采样频率应至少为100Hz但通常使用更高的采样频率比如10kHz以保证计算精度。下面的代码是1000Hz的信号的选用的 采样频率是100000Hz。
2. FFT变换 采集到的时域数据通过FFT算法进行处理转换为频域信息。ARM-DSP库中有现成的FFT函数可以简化计算过程。
3. 运算结果幅值、频率和相位差
幅值信号的振幅大小表示每个频率成分的强度。基波的幅值代表主要的电压或电流值。 频率FFT能帮助我们识别信号中的不同频率成分如电网中的50Hz基波及其他谐波。
相位差对于三相信号FFT可以帮助我们分析不同相之间的相位差揭示它们之间的时间延迟。
4. 处理流程概述
整个处理流程如下
信号采集定时器触发ADC采样交流电信号。采样与ADC转换STM32的ADC将模拟信号转为数字信号。DMA传输使用DMA自动传输采样数据到内存。FFT计算利用FFT将时域数据转换为频域数据。结果提取从FFT结果中提取幅值、频率和相位差信息。
五、程序实现
硬件正点原子探索者 V3 STM32F407 开发板下面是核心代码代码中使用了ARM提供的数学库arm_math.h来实现FFT算法以及STM32的标准库库来配置定时器、ADC和DMA。通过这些配置系统能够高效地采集和处理模拟信号分析其频谱特性。完整代码请在资源下载。
#include tim_adc_dma_fft.h
#include usart.h
#include arm_math.h
#include delay.h /*通过ADC采集模拟信号然后使用DMA将采集的数据传输到内存中接着通过FFT算法分析信号的频谱最后通过串口输出信号的基波和谐波的频率、幅值和相位差等信息*/
#define sampledot 4096
#define FFT_LENGTH 4096 //4096点FFT
#define fft_arr 10 // 用于计算FFT采样频率的系数
#define fft_psc 84 // 用于计算FFT采样频率的系数 const u32 fft_sample_freq84000000/(fft_arr*fft_psc); // 计算FFT采样频率float fft_inputbuf[FFT_LENGTH*2]; // FFT输入数组用于存放复数
float fft_outputbuf[FFT_LENGTH]; // FFT输出数组存放幅值
arm_cfft_radix4_instance_f32 scfft; // FFT实例结构体u32 sampledata[sampledot]{0};//用于存放ADC采样数据的数组,高16位保存adc2 pa5 低16位保存adc1 pa6float phase_difference0; // 用于存放相位差的变量
float freamp[50];//用于存放各次谐波频率和幅值的数组// 定时器3初始化函数
void Tim3_Init(u16 arr,u16 psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitstruct; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); TIM_TimeBaseInitstruct.TIM_Periodarr; TIM_TimeBaseInitstruct.TIM_Prescalerpsc;TIM_TimeBaseInitstruct.TIM_CounterModeTIM_CounterMode_Up;TIM_TimeBaseInitstruct.TIM_ClockDivisionTIM_CKD_DIV1;TIM_TimeBaseInit(TIM3,TIM_TimeBaseInitstruct);//TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update); TIM_Cmd(TIM3,DISABLE);
}
// ADC初始化函数
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_ExternalTrigConvADC_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); //多路转化完后触发dmaADC_DMACmd(ADC1, ENABLE); ADC_Cmd(ADC1, ENABLE);ADC_Cmd(ADC2, ENABLE);
}// DMA初始化函数用于ADC数据的采集
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_ChannelDMA_Channel_0; DMA_InitStructure.DMA_DIRDMA_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_MemoryBurstDMA_MemoryBurst_Single;DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Word;DMA_InitStructure.DMA_MemoryIncDMA_MemoryInc_Enable; DMA_InitStructure.DMA_ModeDMA_Mode_Circular;DMA_InitStructure.DMA_PeripheralBaseAddr(uint32_t)0x40012308; //adc地址DMA_InitStructure.DMA_PeripheralBurstDMA_PeripheralBurst_Single;DMA_InitStructure.DMA_PeripheralDataSizeDMA_PeripheralDataSize_Word;DMA_InitStructure.DMA_PeripheralIncDMA_PeripheralInc_Disable;DMA_InitStructure.DMA_PriorityDMA_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_IRQChannelPreemptionPriority0; //抢占优先级1NVIC_InitStructure.NVIC_IRQChannelSubPriority 0; //子优先级1NVIC_InitStructure.NVIC_IRQChannelCmd ENABLE; //IRQ通道使能NVIC_Init(NVIC_InitStructure);
}// 数据初始化函数用于初始化ADC、DMA、串口和定时器
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 4096Tim3_Init(fft_arr-1,fft_psc-1);
}// DMA中断服务函数用于处理ADC数据采集完成后的操作
void DMA2_Stream0_IRQHandler(void)
{u32 idex; //用于将采集到的数据赋值给fft_inputbuf[2*idex]的计数 float bias_voltage2,HZ2,amplitude2,phase2,bias_voltage1,HZ1,amplitude1,phase1;u8 temp[40];int i;u16 freamplen; // freamp长度的一半if(DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0)) //判断DMA传输完成中断 {TIM_Cmd(TIM3,DISABLE);//关闭时钟进行计算 //adc2 pa5for(idex0;idexsampledot;idex) //高16位fftadc2 fft1 //sampledot4096{ fft_inputbuf[2*idex](u16)(sampledata[idex]16)*(3.3/4096); //生成输入信号实部fft_inputbuf[2*idex1]0;//虚部全部为0}arm_cfft_radix4_f32(scfft,fft_inputbuf); //fft运算arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把运算结果复数求模得幅值 freamplenfft_getpeak(fft_inputbuf,fft_outputbuf1,freamp,FFT_LENGTH/2,10,5,0.2);//寻找基波和谐波 bias_voltage2fft_outputbuf[0]/FFT_LENGTH;//直流 HZ2freamp[0];//频率amplitude2freamp[1];//幅度phase2freamp[2];//相位freamp[0]0;freamp[1]0;freamp[2]0;//adc1 pa6for(idex0;idexsampledot;idex) //低16位fft adc1 fft2{fft_inputbuf[2*idex](u16)(sampledata[idex])*(3.3/4096); //生成输入信号实部fft_inputbuf[2*idex1]0;//虚部全部为0 } arm_cfft_radix4_f32(scfft,fft_inputbuf); //fft运算arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把运算结果复数求模得幅值freamplenfft_getpeak(fft_inputbuf,fft_outputbuf1,freamp,FFT_LENGTH/2,10,5,0.2); //寻找基波和谐波 bias_voltage1fft_outputbuf[0]/FFT_LENGTH;//偏置电压 HZ1freamp[0];//频率amplitude1freamp[1];//幅度phase1freamp[2];//相位freamp[0]0;freamp[1]0;freamp[2]0;phase_differencephase2-phase1;if(phase_difference180) phase_differencephase_difference-180;if(phase_difference-180) phase_differencephase_difference180;printf(\r\n); //fft采样频率printf(fft_sample_freq:%d\r\n,fft_sample_freq); //fft采样频率 printf(bias_voltage1:%.2f\r\n,bias_voltage1); //偏置电压 printf(bias_voltage2:%.2f\r\n,bias_voltage2); //偏置电压printf(HZ1:%.2f\r\n,HZ1); //频率printf(HZ2:%.2f\r\n,HZ2);//频率printf(amplitude1:%.2f\r\n,amplitude1); //幅值 printf(amplitude2:%.2f\r\n,amplitude2);//幅值 printf(phase_difference:%.2f\r\n,phase_difference);//相位差 DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);}
}// 获取FFT峰值
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 outlen0;for(i0;iinlen-x;ix){arm_max_f32(inputi,x,datas,idex); if( (input[iidex]input[iidex1])(input[iidex]input[iidex-1])( (2*datas)/FFT_LENGTH )y) {sum0; for(i2iidex-N;i2iidexN;i2) {suminput[i2]; } if(1.5*sum/(2*N)datas) { output[3*outlen2] atan2(inputx[2*(iidex1)1],inputx[2*(iidex1)])*180/3.1415926f; //计算相位 output[3*outlen1] 1.0*(2*datas)/FFT_LENGTH; //计算幅度output[3*outlen] 1.0*fft_sample_freq*(iidex1)/FFT_LENGTH;//计算频率outlen; } else continue; }else continue;}return outlen;} 定时器初始化 (Tim3_Init): 配置定时器3用于控制ADC的采样频率。 ADC初始化 (Adc_Init): 配置两个ADCADC1和ADC2用于采集模拟信号。ADC1和ADC2分别连接到不同的通道采集不同的模拟信号。 DMA初始化 (Dma_ADC_Init): 配置DMA用于将ADC采集的数据直接传输到内存中减少CPU的负担。 数据初始化 (Data_Init): 调用上述初始化函数完成系统的基本配置。 FFT峰值获取函数 (fft_getpeak): 该函数用于在FFT结果中寻找峰值这些峰值代表了信号中的基波主要频率成分。下面是核心中的核心。 arm_cfft_radix4_f32(scfft,fft_inputbuf); //fft运算arm_cmplx_mag_f32(fft_inputbuf,fft_outputbuf,FFT_LENGTH); //把运算结果复数求模得幅值freamplenfft_getpeak(fft_inputbuf,fft_outputbuf1,freamp,FFT_LENGTH/2,10,5,0.2); //寻找基波和谐波 DMA中断服务函数 (DMA2_Stream0_IRQHandler): 当DMA传输完成时该函数会被调用。它负责执行FFT算法计算信号的偏置电压、频率、幅度、相位的相关信息并用串口1打印。
六、现象
1.信号发生器输入信号源。 信号1:1000Hz的信号的幅度1V偏置电压0.5V相位0°
信号2:1000Hz的信号的幅度1.2V偏置电压0.6V相位50° 2.串口收到测量结果与输入信号源的实际接近。 七、总结
FFT快速傅里叶变换是一种强大的技术它允许我们将信号从时域转换到频域从而深入分析其频率成分。这种转换揭示了信号隐藏的频率特性为我们提供了一个全新的视角来观察和理解信号的行为。
通过上述介绍我们探讨了如何使用STM32微控制器执行FFT计算以提取信号的幅值、频率和相位。 希望这些内容能够为大家提供有价值的参考和指导。在实际应用中理解和运用FFT的原理和技巧将有助于我们更有效地处理和分析各种复杂的信号。