stm32-HAL库-fft-测频率幅度_stm32 fft
stm32-fft教程
目录
stm32-fft教程
前言:
FFT介绍:
正文:
基础知识:
DSP库调用:
代码示例:
结语:
前言:
学习单片机,就避不开stm32,学习stm32,就避不开ADC采样,学习ADC采样,就避不开fft-快速傅里叶变换。本文将给大家分享fft的使用,本教程使用stm32f407VET6,基于cubemx与keil5软件开发,将逐步带领大家实现fft测量采样信号的频率和幅度。(由于fft测量相位差需要用到ADC同步采样,所以将在下篇博客与大家分享哈)
FFT介绍:
stm32---fft 究其根本其实就是三个函数的使用,下面也就是教大家如何去用。
arm_cfft_radix2_instance_f32 scfft;arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf,0,1);arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH);
快速傅里叶变换(FFT)是一种有效计算离散傅里叶变换(DFT)的算法,它将信号从时域转换到频域。通过频域分析,可以更容易地理解和处理信号的频率成分。在嵌入式系统中,特别是在像STM32F407这样的微控制器上,FFT常用于音频处理、振动分析和通信等地方。
STM32F407是一款基于ARM Cortex-M4内核的微控制器,具有DSP(数字信号处理)指令集,非常适合进行快速傅里叶变换等复杂的信号处理操作。使用CubeMX配置STM32硬件外设,通过Keil5搭配HAL库开发,使得我们可以在嵌入式系统中轻松实现FFT功能。
正文:
基础知识:
本文将直接进入fft的使用,关于ADC的配置与介绍,大家可以看看这两篇博客。
——《stm32-HAL 电赛信号教程》
——《STM32——三重ADC交替采样—极限采样率7.2M》
DSP库调用:
(1)先添加DSP库
(2)还得要添加下面三个宏
ARM_MATH_CM4,__CC_ARM,ARM_MATH_M
(3)再最后添加上这些头文件 ,OK,调用步骤完成。
#include \"arm_math.h\"#include \"arm_const_structs.h\"
代码示例:
(1)先进行采样数据个数,采样率,fft数据处理个数的定义。(注意:FFT_LENGTH需要与ADC1_DMA_Size 大小一致,采样率定义需要与cubemx中配置相同,具体请看前面提到的两篇博客)
/* Private define ------------------------------------------------------------*//* USER CODE BEGIN PD */#define FFT_LENGTH 1024#define ADC1_DMA_Size 1024int SAM_FRE = 2000000; //采样率
(2)再定义数组, fft_inputbuf [FFT_LENGTH*2] 是fft 输入数组;fft_outputbuf[FFT_LENGTH] 是fft 输出数组;ADC1_ConvertedValue[ ADC1_DMA_Size ]是ADC采集到的数据,这里先以1024个点为例。
/* USER CODE BEGIN PV */ float fft_inputbuf [FFT_LENGTH*2]; float fft_outputbuf[FFT_LENGTH]; uint32_t ADC1_ConvertedValue[ ADC1_DMA_Size ];
(3)调用函数 arm_cfft_radix2_instance_f32 scfft ,定义结构体scfft。
调用函数arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf,0,1);
arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH) 进行计算。
我这里直接生成一个频率200k与400k,电压为0.825V的混合信号进行处理,添加了1.65V的抬升信号。
arm_cfft_radix2_instance_f32 scfft; /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { while(flag==1) { HAL_ADC_Start_DMA(&hadc1,(uint32_t*)ADC1_ConvertedValue,ADC1_DMA_Size); //开启ADC_dma采集数据 HAL_Delay(1000); for(i=0;i<FFT_LENGTH;i++) {//fft_inputbuf[2*i] = (float)ADC1_ConvertedValue[ i ]*3.3f/4095.0f; //数组实部放采集到的数据 fft_inputbuf[2*i] = ((sin(2*PI*200000*i/SAM_FRE)+1)*1024+ (sin(2*PI*400000*i/SAM_FRE)+1)*1024)*3.3f/4095.0f;fft_inputbuf[2*i+1]=0; //虚部都放0 }// for(i=0;i<FFT_LENGTH;i++)// {//printf(\"%f\\r\\n\",fft_inputbuf[2*i]*3.3f/4095.0f);// } arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf,0,1); //其中1024表示需要处理的数据个数,这里可以是1024,2048,4096 arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH); //计算幅度 for(i=0;i<FFT_LENGTH;i++) {printf(\"%d,%f\\r\\n\",i,fft_outputbuf[ i ]);//未加窗 }
FFT结果如下图:
如果大家有了解FFT的原理,会知道,fft_outputbuf中的值并不是我们最终结果,这1024个点中,索引为0的点代表直流偏置信号,剩下的会关于第512个点对称,由于对称,我们只需要去分析前面512个点。那么我们怎么去计算信号的频率和幅度呢?
(1)直流偏置信号幅度:
由于单片机不能采集负电压,所以我们往往会先加上一个直流偏置。U表示索引为0的点的值,FFT_length表示FFT数据处理的个数。
Ud=1692/1024=1.6523
(2)频率:
N表示数据索引即1——512,FS表示采样率,FFT_length表示FFT数据处理的个数。(大家可以看到,我们计算出的频率是一个离散的范围)。
F1=102*2000000/1024=199218 HZ,F2=205*2000000/1024=400390 HZ
(3)幅度:
Un表示索引为n的点的值,FFT_length表示FFT数据处理的个数。
结语:
FFT处理往往会有频谱泄露,大家可以进行加窗处理(如下)。对了,提一嘴,反快速傅里叶变化——IFFT和这差不多,只不过改了个参数,也放在下面。好啦,希望这篇博客对大家有所帮助,有疑问或建议欢迎留言!如果需要完整代码,可以私我或留言。
for(i=0;i<FFT_LENGTH;i++) {fft_inputbuf_win [i*2] *= (0.5-0.5*cos((2*PI*i)/FFT_LENGTH-1)); }
arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,1,1);//反fft---ifft arm_cfft_f32(&arm_cfft_sR_f32_len1024,fft_inputbuf,1,1); for(i=0;i<FFT_LENGTH;i++) {printf(\"%f\\r\\n\",fft_inputbuf[ 2*i ]); }