-
2020-02-03 20:21:09
stm32频谱分析(fft)
本博客将持续更新带给大家日常生活中的简单经典的C语言小项目和生活中的电子DIY.以后会 陆续推出讲解视频发布在b站上。所有程序和日常学习资料全都可以在群文件中免费下载. 欢迎广大电子爱好朋友或者电子开发需求的朋友添加qq群:1039119322
最近在stm32上做了一个频谱分析仪。使用的是dsp库的fft算法。adc的获取方式使用的定时制采 样使用dma传输,然后利用fft进行处理,最后将频谱显示在正点原子屏幕上。使用stm32f4生成一个正弦波废话不多说,带大家先看一下效果。
一:运行效果图
经过测试,该系统可以进行测量频率40KHZ的信号。为了显示由另一块开发板生成的1KHZ的正弦波,故显示范围限制在10KHZ。可以自己调整
开机默认界面测试引脚连接,f1的PA6为信号输入引脚,f4的PA5为1KHZ正弦信号输出引脚,然后共一下地。
输入1KHZ信号由于用f4模拟的dac正弦信号,一个周期内只有32个点精度并不是很高。运行效果图
二:主要代码展示
限于页幅,完整代码可以添加qq群后免费下载。只贴出关键代码。
DMA初始化
void DMA1_Init()
{
DMA_InitTypeDef DMA_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_DeInit(DMA1_Channel1);
DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_ConvertedValue;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = 1024;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord ;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word ;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;
DMA_InitStructure.DMA_Priority = DMA_Priority_High ;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel1, &DMA_InitStructure);DMA_ITConfig(DMA1_Channel1,DMA_IT_TC, ENABLE); NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DMA_Cmd(DMA1_Channel1,ENABLE);
}
void AGPIO_Init()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //??GPIOA??
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}void Adc_Init(){
ADC_InitTypeDef ADC_InitStructure;
DMA1_Init();
AGPIO_Init();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); //??ADC1???
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //??ADC??
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //???
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //???
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_CC2; //???
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //???
ADC_InitStructure.ADC_NbrOfChannel = 1; //???
ADC_Init(ADC1, &ADC_InitStructure);
RCC_ADCCLKConfig(RCC_PCLK2_Div6); //??ADC??,?PCLK2?6??,?12Hz
ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 1, ADC_SampleTime_28Cycles5); //??ADC1??6?239.5???
ADC_DMACmd(ADC1,ENABLE);
ADC_Cmd(ADC1,ENABLE);
ADC_ResetCalibration(ADC1); //???
while(ADC_GetResetCalibrationStatus(ADC1)); //???
ADC_StartCalibration(ADC1);
while(ADC_GetCalibrationStatus(ADC1));
ADC_ExternalTrigConvCmd(ADC1, ENABLE);
}
三:程序升级规划
在此代码基础上完全可以再添加新的功能
一:按键可以控制进行fft的点数
二:按键可以控制采样频率更多相关内容 -
基于STM32的FFT频谱分析+波形识别
2019-07-10 10:35:16里面有两个文件夹,第一个是基础版本,第二个可以触屏改变采样频率,进而优化频率分辨率,使频谱分析更精确。频谱分析(50Hz~200Hz,其他范围内应该也可以)包括了基频,3,5,7次谐波的峰值,波形识别可识别正弦,... -
基于STM32的音乐频谱分析仪.pdf
2020-02-08 21:09:57本作品是基于 ARM Cortex-M4 内核的 32 位处理器 STM32F407 和快速傅里叶变换(FFT) 算法实现了音频信号的频谱分析。 输入信号先经过信号调理电路处理后,由 STM32F407 内部 12 位逐次比较型的模数转化器进行采样。... -
基于stm32dsp库fft频谱分析教程及源码
2021-05-25 14:37:26基于stm32dsp库fft频谱分析教程及源码,采样是用定时器触发采样并通过dma自动保存,保证采样间隔准确。 -
STM32实现FFT,求取幅度频谱
2022-02-28 20:10:53STM32实现FFT,求取幅度频谱 FFT不太对劲的理解 FFT的原理比较复杂,因为32使用FFT不用去管算法是如何运作的,我在这里就进行简单的介绍了。 因为是简单介绍,就只介绍下幅度频谱图,不考虑相位频谱图。 FFT...STM32实现FFT,求取幅度频谱
FFT不太对劲的理解
FFT的原理比较复杂,因为32使用FFT不用去管算法是如何运作的,我在这里就进行简单的介绍了。
因为是简单介绍,就只介绍下幅度频谱图,不考虑相位频谱图。
FFT可以将一个信号从时域变换到频域,比如一个1VPP的1k的正弦信号,它的时域和频域的示意图如下:
频域为我们观察信号提供了一个新的视角。比如下面是1k和2k信号的叠加。
从时域上看,1k+2k的波形不容易进行处理,也不好猜出来这个波形到底有什么特性(当然这个例子其实还是比较好猜测的,复杂情况就不好看了)。可是变换到频域后,特性非常的明显,处理起来就方便了。
STM32实现FFT
添加DSP库
STM32 DSP库的快速添加 基于cubemx 调用,使用DSP库_四臂西瓜的博客-CSDN博客_cubemx dsp
要采用第二种方法添加DSP,第一种方式添加的DSP库比较老,支持的FFT函数用起来不方便,这篇文章介绍的FFT函数老版本不支持。
设置ADC采集交流+串口重定向
STM32HAL ADC+TIM+DMA采集交流信号 基于cubemx_四臂西瓜的博客-CSDN博客
代码编写
DSP库里面的FFT支持32-4096,同时是 2 n 2^n 2n个整数的傅里叶变换。首先定义下面这些变量。其中只有fft_inputbuf,fft_outputbuf是用来进行FFT的。
#define FFT_LENGTH 1024 __IO uint8_t AdcConvEnd = 0; uint16_t adcBuff[FFT_LENGTH]; float fft_inputbuf[FFT_LENGTH * 2]; float fft_outputbuf[FFT_LENGTH];
定义完成变量后进行服务内容的书写。首先是ADC进行数据的采集,这一部分再赘述:
HAL_ADCEx_Calibration_Start(&hadc1); HAL_ADC_Start_DMA(&hadc1, (uint32_t *)adcBuff, FFT_LENGTH); HAL_TIM_Base_Start(&htim3); while (!AdcConvEnd) //等待转换完毕 ;
傅里叶变换分实数和虚数两种,使用最为频繁的是虚数。我们需要把ADC采集到的数据以虚数的形式存放到傅里叶变换的输入数组fft_inputbuf。虚数的存放方式如下
数组下标 0 1 2 3 … 数值 实部 虚部 实部 虚部 … 比如ADC采集到的第二个数据adcBuff[1],它的数据存入到fft_inputbuf[2],它的虚部是fft_inputbuf[3],补零。
我们调用arm_cfft_f32(&arm_cfft_sR_f32_len1024, fft_inputbuf, 0, 1);来对输入数据进行傅里叶变换。它的输入参数含义如下
- arm_cfft_sR_f32_len1024为傅里叶变换结构体,1024是要运算的点数。我们在进行其他点数的计算时,比如说32个点的FFT,就可以用arm_cfft_sR_f32_len32。
- fft_inputbuf为傅里叶变换需要处理的数据的首地址
- 第三个参数0是正变换,1是反变换
- 第四个参数一般是1
经过傅里叶变换后的结果仍然为复数,虚部和实部的比可以计算出频率点的相位,这个在这里不进行考虑。我们直接对复数取模。
取模是实部和虚部的平方和取平均来算,不过我们没必要自己去这样写,因为DSP库为我们提供了取模函数:arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH);参数含义如下:
- fft_inputbuf源数据,形式为复数
- fft_outputbuf取完模后的数据,形式为实数
- FFT_LENGTH是取模的点数
他们背后的运算是: f f t o u t p u t b u f [ i ] = f f t i n p u t b u f [ i ∗ 2 ] 2 + f f t i n p u t b u f [ i ∗ 2 + 1 ] 2 fftoutputbuf[i]=\sqrt{fftinputbuf[i*2]^2+fftinputbuf[i*2+1]^2} fftoutputbuf[i]=fftinputbuf[i∗2]2+fftinputbuf[i∗2+1]2
for (int i = 0; i < FFT_LENGTH; i++) { fft_inputbuf[i * 2] = adcBuff[i] * 3.3 / 4096;//实部赋值,* 3.3 / 4096是为了将ADC采集到的值转换成实际电压 fft_inputbuf[i * 2 + 1] = 0;//虚部赋值,固定为0. } arm_cfft_f32(&arm_cfft_sR_f32_len1024, fft_inputbuf, 0, 1); arm_cmplx_mag_f32(fft_inputbuf, fft_outputbuf, FFT_LENGTH);
运算完成的结果还需要进行下面的处理,背后的原理就跟FFT算法有关了,这里不做解释。
我们进行的是1024个点的FFT变换,那么fft_outputbuf下标0需要除以1024,其余的数除以512。
fft_outputbuf[0] /= 1024; for (int i = 1; i < FFT_LENGTH; i++)//输出各次谐波幅值 { fft_outputbuf[i] /= 512; }
最后再把运算结果打印出来即可。
printf("FFT Result:\r\n"); for (int i = 0; i < FFT_LENGTH; i++)//输出各次谐波幅值 { printf("%d:\t%.2f\r\n", i, fft_outputbuf[i]); }
总的代码如下图
运行结果
ADC以100k的频率去采集信号发生器产生的976hz的正弦信号,信号VPP=2v,直流偏置为2V。(注意别超过内部ADC的测量范围0-3.3V)
FFT Result: 0: 1.97 1: 0.00 2: 0.00 3: 0.00 4: 0.00 5: 0.00 6: 0.00 7: 0.00 8: 0.00 9: 0.01 10: 1.05 11: 0.01 12: 0.00 13: 0.00 14: 0.00 ...(后面的数据都为0)
结果分析
FFT计算出来的数据是对称的,我们只取前一半的数据,此时的前一半数据是512个。
FFT输出数组的下标表示的频率,计算关系为:
频 率 = 数 组 下 标 ∗ 采 样 率 f f t 计 算 的 点 数 频率=数组下标*\frac{采样率}{fft计算的点数} 频率=数组下标∗fft计算的点数采样率
利用这个公式分析下上方的运行结果。数组下标0对应的是0hz,也就是直流偏置,幅度为1.97,和输入信号的2V符合。数组下标10对应的频率为 100 k 1024 ∗ 10 ≈ 976.5 h z \frac{100k}{1024}*10\approx976.5hz 1024100k∗10≈976.5hz,对应幅度为1.05V,和输入信号的2VPP相符。精度问题
不知道读者有没有注意到待测频率为976hz,而不是我们平时常见的1k,2k?这是为了这个频率正好落在数组下标10点上。10对应的是 100 k 1024 ∗ 10 ≈ 976.5 h z \frac{100k}{1024}*10\approx976.5hz 1024100k∗10≈976.5hz,11对应的 100 k 1024 ∗ 11 ≈ 1075 h z \frac{100k}{1024}*11\approx1075hz 1024100k∗11≈1075hz,1k落在了两点中间,这样就引起了
栅栏效应
,给人的直观感受就是能量分散了。下面是把待测信号改成1k后的运行结果。FFT Result: 0: 1.98 1: 0.02 2: 0.03 3: 0.03 4: 0.03 5: 0.04 6: 0.05 7: 0.07 8: 0.10 9: 0.18 10: 0.95 11: 0.30 12: 0.13 13: 0.09 ...
可以看到10,11,9等下标都分到了电压(能量)。实际应用中应尽可能避免栅栏效应。
栅栏效应下的补偿
如果不可避免得碰到了栅栏效应,是可以通过数据处理尽可能的还原求取待测信号的幅度值。方法是把频率点附近的能量聚集起来,将附近频率点的幅度平方求和,再取平均。
比如上面1k的情况,就可以把8,9,10,11,12的能量聚集起来。
0. 1 2 + 0.1 8 2 + 0.9 5 2 + 0. 3 2 + 0.1 3 2 = 1.026 \sqrt{0.1^2+0.18^2+0.95^2+0.3^2+0.13^2}=1.026 0.12+0.182+0.952+0.32+0.132=1.026
经过补偿后的幅度值就跟VPP2V真实值更加接近了。我这里只取了5个点,如果不同主要频率点下标相差比较大,可以取更多;反之更少。
后记
本文章收录于:
本文为系列文章中的冰山一角,欢迎进入小站查看。
配套程序:
-
基于STM32的FFT算法移植(音乐频谱)
2018-07-10 10:08:04该资源为,将FFT算法移植到STM32F1系列上,通过ADC采集例如音频信号,并将信号通过算法实时动态的显示在LCD上,实现一个动态音乐频谱。 -
基于STM32F407(FFT)音乐五彩频谱显示
2019-04-14 16:27:14基于STM32F407音乐五彩频谱显示,内有效果图,音乐直接接PA5口即可使用! -
STM32自带adc实现低频示波器及FFT频谱显示-其它文档类资源
2021-02-13 10:30:22STM32自带adc实现低频示波器及FFT频谱显示 STM32自带adc实现低频示波器及FFT频谱显示 STM32自带adc实现低频示波器及FFT频谱显示 STM32自带adc实现低频示波器及FFT频谱显示 -
战舰STM32音乐频谱显示(全网资料整合)
2019-05-06 19:32:05本压缩包整合网上各类关于战舰V3STM32F103ZET6关于音乐播放+音乐频谱显示的资料,如何采集音乐数据进行FFT,如何在LCD屏上进行显示。 -
stm32FFT程序.rar
2020-08-04 09:13:27stm32f103 FFT的程序源码,已注释,对单片机adc获取的电压值就行快速傅里叶变换,进行频谱分析 -
STM32 FFT算法实现
2020-08-21 12:08:16在 MDK 里面搭建 STM32F4 的 DSP 运行环境(使用.lib 方式)是很简单的,分为 3 个步骤: 1, 添加文件。 首先,我们在例程工程目录下新建:DSP_LIB 文件夹,存放我们将要添加的文件: arm_cortexM4lf_math.lib 和相关...DSP 库运行环境搭建
在 MDK 里面搭建 STM32F4 的 DSP 运行环境(使用.lib 方式)是很简单的,分为 3 个步骤:
1, 添加文件。
首先,我们在例程工程目录下新建:DSP_LIB 文件夹,存放我们将要添加的文件:
arm_cortexM4lf_math.lib 和相关头文件( 文件由官方提供),如图:
然后,打开工程,新建 DSP_LIB 分组,并将 arm_cortexM4lf_math.lib 添加到工程里面,如图:
2, 添加头文件包含路径
添加好.lib 文件后,我们要添加头文件包含路径,将第一步拷贝的 Include 文件夹和 DSP_LIB文件夹,加入头文件包含路径。
3, 添加全局宏定义
最后,为了使用 DSP 库的所有功能,我们还需要添加几个全局宏定义:
1,__FPU_USED
2,__FPU_PRESENT
3,ARM_MATH_CM4
4,__CC_ARM
5,ARM_MATH_MATRIX_CHECK
6,ARM_MATH_ROUNDING
添加方法:点击C/C++选项卡,然后在 Define 里面进行设置,如图
这里,两个宏之间用“,”隔开。并且,上面的全局宏里面,我们没有添加__FPU_USED,因为这个宏定义在 Target 选项卡设置 Code Generation 的时候,选择了:Use FPU(如果没有设置 Use FPU,则必须设置!!),故 MDK 会自动添加这个全局宏,因此不需要我们手动添加了。
至此,STM32F4 的 DSP 库运行环境就搭建完成了。FFT介绍
FFT 即快速傅里叶变换,可以将一个时域信号变换到频域。因为有些信号在时域上是很难看出什么特征的,但是如果变换到频域之后,就很容易看出特征了,这就是很多信号分析采用 FFT 变换的原因。另外,FFT 可以将一个信号的频谱提取出来,这在频谱分析方面也是经常用的。简而言之,FFT 就是将一个信号从时域变换到频域方便我们分析处理。照一定大小采样频率 F 去采集信号,采集 N 个点,那么通过对这 N 个点进行 FFT 运算,就可以得到这个信号的频谱特性。 这里还涉及到一个采样定理的概念:在进行模拟/数字信号的转换过程中,当采样频率 F 大于信号中最高频率 fmax的 2 倍时(F>2*fmax),采样之后的数字信号完整地保留了原始信号中的信息,采样定理又称奈奎斯特定理。举个简单的例子:比如我们正常人发声,频率范围一般在 8KHz以内,那么我们要通过采样之后的数据来恢复声音,我们的采样频率必须为 8KHz 的 2 倍以上,也就是必须大于 16KHz 才行。
模拟信号经过 ADC 采样之后,就变成了数字信号,采样得到的数字信号,就可以做 FFT变换了。N 个采样点数据,在经过 FFT 之后,就可以得到 N 个点的 FFT 结果。为了方便进行FFT 运算,通常 N 取 2 的整数次方。
假设采样频率为 F,对一个信号采样,采样点数为 N,那么 FFT 之后结果就是一个 N 点的复数,每一个点就对应着一个频率点(以基波频率为单位递增),这个点的模值(sqrt(实部虚部2))就是该频点频率值下的幅度特性。具体跟原始信号的幅度有什么关系呢?假设原始信号的峰值为 A,那么 FFT 的结果的每个点(除了第一个点直流分量之外)的模值就是 A 的 N/2倍,而第一个点就是直流分量,它的模值就是直流分量的 N 倍。
这里还有个基波频率,也叫频率分辨率的概念,就是如果我们按照 F 的采样频率去采集一个信号,一共采集 N 个点,那么基波频率(频率分辨率)就是 fk=F/N。 这样,第 n 个点对应信号频率为:F*(n-1)/N;其中 n≥1,当 n=1 时为直流分量。
关于 FFT 就介绍到这。官方FFT使用
如果我们要自己实现 FFT 算法,对于不懂数字信号处理的朋友来说,是比较难的,不过,ST 提供的 STM32F4 DSP 库里面就有 FFT 函数给我们调用,因此我们只需要知道如何使用这些函数,就可以迅速的完成 FFT 计算,而不需要自己学习数字信号处理,去编写代码了,大大方便了我们的开发。STM32F4 的 DSP 库里面,提供了定点和浮点 FFT 实现方式,并且有基 4的也有基 2 的,大家可以根据需要自由选择实现方式。注意:对于基 4 的 FFT 输入点数必须是 4n,而基 2 的FFT 输入点数则必须是 2n,并且基 4 的 FFT 算法要比基 2 的快。 本次我将采用 DSP 库里面的基 4 浮点 FFT 算法来实现 FFT 变换,并计算每个点的幅值、频率、相位。
所用到的函数有:arm_status arm_cfft_radix4_init_f32( arm_cfft_radix4_instance_f32 * S, uint16_t fftLen,uint8_t ifftFlag,uint8_t bitReverseFlag) void arm_cfft_radix4_f32(const arm_cfft_radix4_instance_f32 * S,float32_t * pSrc) void arm_cmplx_mag_f32(float32_t * pSrc,float32_t * pDst,uint32_t numSamples)
第一个函数 arm_cfft_radix4_init_f32,用于初始化 FFT 运算相关参数,其中:fftLen 用于指定 FFT 长度(16/64/256/1024/4096),本章设置为1024;ifftFlag 用于指定是傅里叶变换(0)还是反傅里叶变换(1),本次设置为0;bitReverseFlag 用于设置是否按位取反,本章设置为 1;最后,所有这些参数存储在一个 arm_cfft_radix4_instance_f32 结构体指针 S 里面。
第二个函数 arm_cfft_radix4_f32 就是执行基 4 浮点 FFT 运算的,pSrc 传入采集到的输入信号数据(实部+虚部形式),同时 FFT 变换后的数据,也按顺序存放在 pSrc 里面,pSrc 必须大于等于 2 倍 fftLen 长度。另外,S 结构体指针参数是先由 arm_cfft_radix4_init_f32 函数设置好,然后传入该函数的。
第三个函数 arm_cmplx_mag_f32 用于计算复数模值,可以对 FFT 变换后的结果数据,执行取模操作。pSrc 为复数输入数组(大小为 2*numSamples)指针,指向 FFT 变换后的结果;pDst为输出数组(大小为 numSamples)指针,存储取模后的值;numSamples 就是总共有多少个数据需要取模。
通过这三个函数,我们便可以完成 FFT 计算,求出模值后便可以求出幅值、相位,知道采样率和FFT长度就可求出频率。软件设计
fft.c 代码
#include "arm_math.h" #include "common.h" #include "fft.h" /********************************************************************************* ************************STM32F407核心开发板****************************** ********************************************************************************** * 文件名称: fft.c * * 文件简述:DSP FFT使用 * * 创建日期:2020.08.21 * * 版 本:V1.0 * * 作 者:近视未看清人心 * * 说 明:完成FFT信号分析需要用到的函数集,包括求模,幅值、频率、相位 注意:FFT的长度,也就是采样点的个数在fft.h 宏定义中完成* ********************************************************************************** *********************************************************************************/ arm_cfft_radix4_instance_f32 scfft; //设置FFT参数的结构体变量(基4) /**************************************************************************** * 名 称: void FFTx4_init(u8 ifftFlag,u8 bitReverseFlag) * 功 能:FFT初始化函数(基4) * 入口参数:ifftFlag:于指定是傅里叶变换(0)还是反傅里叶变换(1) bitReverseFlag:是否按位取反 * 返回参数:无 * 说 明: ****************************************************************************/ void FFTx4_init(u8 ifftFlag,u8 bitReverseFlag) { arm_cfft_radix4_init_f32(&scfft,length,ifftFlag,bitReverseFlag);//初始化scfft结构体,设定FFT相关参数 } /**************************************************************************** * 名 称: void cFFTx4(float * pSrc) * 功 能:FFT计算函数(基4) * 入口参数:pSrc:pSrc 传入采集到的输入信 号数据(实部+虚部形式),同时 FFT 变换后的数据,也按顺序存放在 pSrc 里面 * 返回参数:无 * 说 明:pSrc的长度必须>=2*length ****************************************************************************/ void cFFTx4(float * pSrc) { arm_cfft_radix4_f32(&scfft,pSrc); //FFT计算(基4) } /**************************************************************************** * 名 称: void cmplxFFTx4(float * pSrc,float * pDst) * 功 能:对FFT结果求模值(基4) * 入口参数:pSrc:需要求模的复数指针(实部+虚部形式) pDst:存放模值 * 返回参数:无 * 说 明:pSrc的长度必须>=2*length ****************************************************************************/ void cmplxFFTx4(float * pSrc,float * pDst) { arm_cmplx_mag_f32(pSrc,pDst,length); //计算复数模值 } /**************************************************************************** * 名 称: void all_result_x4(float * pSrc,float * Amp,float * rate,float * Phase,u32 Fs) * 功 能:计算各点幅值、频率、相位 * 入口参数:pSrc:原始信号的复数指针(实部+虚部形式) Amp:存放幅值 rate:存放各点的频率 Phase:存放各点的相位 size:采样点的个数 * 返回参数:无 * 说 明:pSrc的长度必须>=2*size ****************************************************************************/ void all_result_x4(float * pSrc,float * Amp,float * rate,float * Phase,u32 Fs) { float pDst[length]; u32 i; arm_cfft_radix4_f32(&scfft,pSrc); //FFT计算(基4) arm_cmplx_mag_f32(pSrc,pDst,length); //计算复数模值 //计算各点幅值、频率、相位 for(i=0;i<length;i++) { if(i==0) Amp[0]=pDst[0]/length; //第一个点直接除以size(z直流分量) else Amp[i]=pDst[i]*2/length; //其他点除以size/2 rate[i]=Fs/length*i;// 各点频率 Phase[i]=atan2(pSrc[2*i+1], pSrc[2*i]); /* atan2求解的结果范围是(-pi, pi], 弧度制 */ } }
fft.h 代码
#ifndef _FFT_H #define _FFT_H #include "arm_math.h" #include "common.h" #define length 1024 //FFT长度(16/64/256/1024/4096),即采样点的个数 void FFTx4_init(u8 ifftFlag,u8 bitReverseFlag);//FFT初始化函数(基4) void cFFTx4(float * pSrc);//FFT计算函数(基4) void cmplxFFTx4(float * pSrc,float * pDst);//对FFT结果求模值(基4) void all_result_x4(float * pSrc,float * Amp,float * rate,float * Phase,u32 Fs);//计算各点幅值、频率、相位 #endif
main.c 代码
#include "common.h" #include "key.h" #include "fft.h" #include "usart3.h" /********************************************************************************* ********************** STM32F407应用开发板(高配版)************************* ********************************************************************************** * 文件名称: DSP FFT使用 * * 文件简述: * * 创建日期:2020.8.21 * * 版 本:V1.0 * * 作 者:近视未看清 * * 说 明:将时域序列信号FFT后幅值、频率、相位通过串口发送到PC * * * ********************************************************************************** *********************************************************************************/ float input[length*2];//前一个数表示实部,后一个表示虚部 float Amp[length];//存放幅值 float rate[length];//存放频率 float Phase[length]; int main(void) { u16 i; NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置系统中断优先级分组2 delay_init(); //初始化延时函数 KEY_Init(); //IO初始化 uart3_init(9600); FFTx4_init(0,1);//FFT初始化函数(基4) //生成信号序列 for(i=0;i<length;i++) { input[i*2]=100+10*arm_sin_f32(2*PI*i/length)+30*arm_sin_f32(2*PI*i*4/length)+50*arm_cos_f32(2*PI*i*8/length); //生成输入信号实部 input[2*i+1]=0;//时域,虚部为零 } while(1) { key_scan(0); if(keydown_data==KEY0_DATA) { all_result_x4(input, Amp, rate, Phase,1024);//计算各点幅值、频率、相位 printf("A: F; P\n"); for(i=0;i<length;i++) printf("%.2f: %.2f: %.2f\n",Amp[i],rate[i],Phase[i]);//向串口发送幅值、频率、相位 } delay_ms(5); } }
测试结果分析
由于 FFT 变换后的结果具有对称性,所以,实际上有用的数据,只有前半部分,后半部分和前半部分是对称关系,比如 第2个点和 最后一个点,5 和 1021,9和 1017 等,就是对称关系,因此我们只需要分析前半部分数据即可。这样,就只有第 1、2、5、9 这四个点幅值比较大,其他点的幅值几乎都是0,重点分析。
采样频率为 1024Hz,那么总共采集 1024 个点,频率分辨率就是 1Hz,对应到频谱上面,两个点之间的间隔就是 1Hz(跟图中第二列完全吻合)。第 1 点,即直流分量,幅值为100,第2、5、9点对应幅值分别为10,30,50,频率分别为1Hz,4Hz,8Hz。因此,可以看出跟上面生成的信号:
100+
10*sin(2*PI*i/1024)+
30*sin(2*PI*i*4/1024)+
50*cos(2*PI*i*8/1024)
几乎完全吻合。 -
基于STM32 DSP库的电力谐波分析,根据ADC采样值计算基波谐波的频率幅度相位
2019-10-30 14:19:23基于STM32 DSP库函数的电力谐波分析,输入时域信号采样值,进行Q31 FFT计算,在有频谱泄露、栅栏效应的前提下计算基波和谐波的频率、幅度、相位。根据论文《基于加汉宁窗的FFT高精度谐波检测改进算法》实现。函数... -
stm32f103+OLED12864+FFT音乐频谱(多种显示效果 提供原理图)
2018-08-13 23:36:16利用stm32f103+OLED12864+FFT制作的一款音乐频谱,观赏性好,制作简单。 -
STM32音乐频谱(32*64点阵)-电路方案
2021-04-22 20:32:03采用STM32F103C8T6作主控芯片,晶振频率8MHZ。音乐频谱增加轨至轨运放显示效果更有动感! FFT运算采用官方的DSP库,效率非常高!适用各种风格音乐! 程序采用的是256点FFT,运算一次FFT只需0.437MS,非常的快! 通过... -
stm32f4的FFT库
2020-10-11 17:26:01正点原子stm32f4的FFT库例程,能够对ADC采集的数据进行傅里叶分析,并对傅里叶分析结果提取各次谐波幅值进行频谱分析 -
STM32自带adc实现低频示波器及FFT频谱显示
2011-08-16 10:10:33STM32自带adc实现低频示波器及FFT频谱显示 -
STM32 +差分ad纯C实现2048点FFT,采集信号频谱图
2019-01-04 18:52:48自己写,用于学习交流,采样用ad在时域以一定频率等间隔采样,完成一组数据采集存储后,MCU进行FFT输出频谱数组 -
运用STM32F407写的FFT,分辨率是1Hz。可以测量信号频率,以及谐波分析,失真度。另外还可以测量两个波形的...
2019-04-02 15:13:58运用STM32F407写的FFT,分辨率是1Hz。可以测量信号频率,以及谐波分析,失真度。另外还可以测量两个波形的相位差 -
基于STM32F4的音乐频谱分析
2019-05-09 17:07:06基于STM32F4的音乐频谱分析** 1.制作材料: stm32f407核心板 16*16的可见灯点阵 蓝牙音频模块及其配套运放模块 若干杜邦线等等、 2.制作思路 先将整体电路搭建好,也就是如下图类似的电路整体图。(由于博...基于STM32F4的音乐频谱分析
1.制作材料:
stm32f407核心板
16*16的可见灯点阵
蓝牙音频模块及其配套运放模块
若干杜邦线等等、
2.制作思路
先将整体电路搭建好,也就是如下图类似的电路整体图。(由于博主自己搭的卖相不是很好,所以就从网上找了一个类似的光立方图)
列控制采用SS8550进行频谱幅度的控制,而行控制通过SS8050进行循环控制,由于人眼能够识别的频率分界线为25HZ,因此我们可以将行循环时间定义为956us,时间循环太短了无法进行控制,io口可能会发生错误显示。(此原因可能是由于stm32f4cpu运行速度太高,io口工作频率达不到。)
3.软件设计思路
博主直接移植的stm32f1的DSP库里的程序,没有用F4的dsp库(后来仔细看了下,F4与f1的dsp库差别在于,一个为整型,另外一个为浮点型,F4运行速度会快一点,但由于之前先做的频谱分析仪是用F1做的,所以就懒得改了。)
其中FFT的核心代码就放在这里了,有需求的可以摘抄看看。
4.总结
其实音乐频谱总体思路都挺简单的,但由于刚开始做的时候,不知道蓝牙还有音频模块,还以为需要自己写下协议啥的,导致看了几天蓝牙协议部分,当然最耗费时间的还是LED显示电路部分,采用的是杜邦线连接的,导致各种问题都出现了,接触不良,显示效果不好等等。
-
音乐频谱显示小玩具——FFT在STM32中的实现与应用
2020-04-16 18:52:18音乐频谱显示说白了就是“儿童版”频谱仪。笔者平时比较喜欢听音乐,闲暇之余听音乐的时候如果有个频谱显示的小玩具在旁边跳来跳去的也挺有意思的,所以笔者去万能的某宝上搜索了一下,发现便宜的都很小,大一点的都... -
毕业设计| STM32F103全彩FFT音乐频谱+LED年历闹钟显示
2021-12-06 00:15:53大家好,我是写代码的篮球球痴,今天在后台看到有人想找毕业设计的点子。然后我今天刚好看到一个不错的点子,给大家分享看今天分享的毕设是来自点-凡自制的FTF音乐频谱制作,先来看展示效果:——视... -
基于STM32的FFT音频实验.rar
2020-09-07 12:29:40本项目是基于STM32F1系列单片机,采集音乐频谱,并且通过LCD显示!可直接烧录到正点原子战舰板使用! -
STM32F407+FFT+详细解读!!!!
2020-10-18 19:35:39这张图是我通过stm32的ADC采集一个1KHZ的方波,然后进行1024个点的FFT得到的,此时的采样率大约为256KHZ,注意:这里的采样率是大约为256KHZ,与256KHZ有较大的偏差,这个偏差造成的后果就是右图的频谱图能量 -
基于stm32的音乐频谱显示
2015-06-09 11:10:23这是一个基于stm32的音乐频谱显示,最大采样频率达到6KHz,能够满足一般的音乐播放要求,其中采样FFT变换,将音频信号转变为恁俩信号显示出来 -
STM32控制LED亮灭-HAL库
2021-02-26 17:51:44使用cubemx生成 hal库 控制LED亮灭 -
STM32-低频示波器-FFT频谱显示.zip
2019-06-27 17:48:04用STM32 自带AD实现低频示波器实现FFT频谱显示。1BUFOUT为FFT计算结果。复数。1BUFMAG中为1BUFOUT的模值。 -
如何使用STM32提供的DSP库进行FFT
2020-07-14 02:32:09前些日子,因为需要在STM32F103系列处理器上,对采集的音频信号进行FFT,所以花了一些时间来研究如何高效并精确的在STM32F103系列处理器上实现FFT。在网上找了很多这方面的资料做实验并进行比较,最终选择了使用STM... -
第二次仿真作业_fft_proteus_stm32f103adc_
2021-10-04 10:13:28(函数主体是按照测量交流函数均值来写的,经过测算发现存在一定的误差,所以仿真就使用直流电压进行测量)(改变电源的输出电压,再按下KEY2,即可改变LCD显示电压)当按下KEY1时,LCD进行显示FFT频谱分析功能,...