STM32实现FFT,求取幅度频谱

这篇具有很好参考价值的文章主要介绍了STM32实现FFT,求取幅度频谱。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

STM32实现FFT,求取幅度频谱

FFT不太对劲的理解

FFT的原理比较复杂,因为32使用FFT不用去管算法是如何运作的,我在这里就进行简单的介绍了。

因为是简单介绍,就只介绍下幅度频谱图,不考虑相位频谱图。

​ FFT可以将一个信号从时域变换到频域,比如一个1VPP的1k的正弦信号,它的时域和频域的示意图如下:

stm32 fft,冬令营,stm32,单片机,arm

​ 频域为我们观察信号提供了一个新的视角。比如下面是1k和2k信号的叠加。

stm32 fft,冬令营,stm32,单片机,arm

​ 从时域上看,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];  

stm32 fft,冬令营,stm32,单片机,arm

​ 定义完成变量后进行服务内容的书写。首先是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[i2]2+fftinputbuf[i2+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]);
}

总的代码如下图

stm32 fft,冬令营,stm32,单片机,arm

运行结果

​ 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 1024100k10976.5hz,对应幅度为1.05V,和输入信号的2VPP相符。

精度问题

不知道读者有没有注意到待测频率为976hz,而不是我们平时常见的1k,2k?这是为了这个频率正好落在数组下标10点上。10对应的是 100 k 1024 ∗ 10 ≈ 976.5 h z \frac{100k}{1024}*10\approx976.5hz 1024100k10976.5hz,11对应的 100 k 1024 ∗ 11 ≈ 1075 h z \frac{100k}{1024}*11\approx1075hz 1024100k111075hz,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傅里叶变换——0积分下载文章来源地址https://www.toymoban.com/news/detail-815632.html

到了这里,关于STM32实现FFT,求取幅度频谱的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32单片机-输入捕获、FFT测频

    本内容介绍基于STM32F103VET6的一个实际工程中添加采集A相电压信号或B相电流信号频率的功能,分别通过输入捕获与FFT实现,均测试可用。紫色文字是超链接,点击自动跳转至相关博文。持续更新,原创不易! 一、硬件连接 1、电压信号处理电路仿真 2、单片机连接 二、程序部

    2024年02月16日
    浏览(35)
  • 在STM32中对信号进行FFT运算

    首先需要在程序文件中添加DSP库并将其文件夹加入程序中 其中包括头文件与arm_cortexM4lf_math.lib 在keil中需要进行如下操作 1、魔术棒C/C++Define ,ARM_MATH_CM4,__CC_ARM,ARM_MATH_MATRIX_CHECK,ARM_MATH_ROUNDING 2、在程序中添加arm_cortexM4lf_math.lib文件 3、添加头文件路径 …DSP_LIBInclude 4、打开Use M

    2024年02月16日
    浏览(32)
  • 基于STM32F407实现离散傅里叶变换(FFT、DFT),计算指定频率的幅值

    前言: 本人的课题是关于EIT采集系统设计,所谓的EIT,简单的说就是往人体注入特定频率的电流信号,通过采集反馈的电压信号,进而使用成像算法重构人体内部的阻抗分布。由于采集到的电压包含其它频率的热噪声,为了只保留注入频率的信号成分,需要对采集到的电压信

    2024年02月16日
    浏览(56)
  • STM32 ADC+定时器+DMA+FFT

    本次实现的功能为单片机DAC输出一个正弦波,然后ADC定时采样用DMA输出,最后对DAC输出的波形进行FFT。 单片机STM32F103ZET6 内部时钟 一、配置ADC ADC端口为PA1,采用DMA输出,定时器3触发 定时器时钟64M,分频后为102.4KHz ADC采样时间为102.4KHz/100=1.024KHz 二、配置DAC DAC端口PA4 DMA传输

    2024年02月13日
    浏览(42)
  • 利用STM32ZET6 进行FFT采集频率

      准备电赛的中,尝试了几种测量频率的方法,也参考了一些博主,没有一种可以测量范围很广的方法,而且大多数都要豆豆下载,本人属于看见要给钱就会直接撤退的,当然也不是批判官方,毕竟还是有许多好东西的。下面的代码都是我在准备比赛时写的,可以测量。如果

    2024年04月24日
    浏览(27)
  • STM32 DSP库CUBEMX配置+FFT频率计算

    使用DSP中的函数加快计算。 本文首先讲述如何通过添加dsp库。 再讲述使用DSP库进行实数FFT运算。(FFT运算用到了前面讲述的STM32CubeMX-ADC hal库 3定时器触发) 参考1文章 参考2文章 先找到文件路径 然后设置如下路径 双击如下并找到路径 D:STM32CubeMXSTM32Cube_FW_F4_V1.26.2DriversCMS

    2024年02月16日
    浏览(39)
  • 2018年电赛A题 软件部分 STM32 FFT 时域到频域 STM32cubeMX HAL

    题目要求:任意波信号发生器输出非正弦信号时,基波频率范围为50Hz~200Hz,测量电流信号基波频率,频率测量精度优于1%;测量基波及各次谐波分量的幅度(振幅值),电流谐波测量频率不超过1kHz,测量精度优于5% 。 实现方式:利用STM32单片机内置ADC对待测信号进行采集,

    2024年02月15日
    浏览(88)
  • STM32CubeMx移植DSP库 傅立叶变化(FFT)测试

    本篇文章采用的是ST公司的STM32L496的DSP库进行FFT函数测试,将计算得到的数据通过串口工具打印出来,其他支持DSP库的型号也是类似做法。 STM32L496为超低功耗Cortex-M4内核的MCU,具有64个引脚,并且外设资源丰富(4个IIC硬件通信接口、5个串口通信接口、3个SPI通信接口、3个12位

    2023年04月08日
    浏览(41)
  • Matlab中利用FFT实现信号频谱搬移

    在fft的理论中,fft的频移特性表示为: 也就是说,要想对信号f(t)实现频域的频谱搬移,只要在时域乘以一个矩阵,即可实现频谱的搬移。常用的振幅调制和解调就是如此,频谱搬移前后对比如下: 其特点就是仅频谱搬移,不产生新的频谱分量。利用欧拉公式: e^(ix)可以

    2024年01月20日
    浏览(50)
  • STM32中arm_math.h库中fft的相关使用

    ①rfft实数傅里叶变换 ②cfft复数傅里叶变换 在使用cfft时会报错arm_cfft_sR_f32_lenXXX未定义,需要incluarm_const_structs.h”

    2024年02月13日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包