【STM32】STM32F4调用DSP库实现FFT运算

news/2024/10/30 15:20:56/

写在前面

最近在整理之前的stm32笔记,打算把一些有价值的笔记发到CSDN分享一下。

奎斯特定理

在进行模拟/数字信号的转换过程中,当采样频率F大于信号中最高频率 fmax 的 2 倍时(F>2*fmax),采样之后的数字信号完整地保留了原始信号中的信息。

采样结果

  • 设采样频率(单位时间可以采多少个信号样本)为Fs,信号频率F,采样点数为N。那么FFT之后结果就是一个为N点的复数。每一个点对应一个频率点,并且这个点对应的幅值就是该频率下的幅度特性。

  • 而每个点的相位,就是在该频率下的信号的相位。

  • 假设原始信号的峰值为A

    那么FFT的第一个点(直流分量)的模值就是A的N倍

    FFT的其他点的模值就是A的N/2倍

    比如

  • 第一个点表示直流分量(即0Hz),而最后一个点N的再下一个点(实际上这个点是不存在的,这里是假设的第N+1个点,也可以看做是将第一个点分做两半分,另一半移到最后)则表示采样频率Fs,这中间被N-1个点平均分成N等份,每个点的频率依次增加。第n所表示的频率为:Fn=(n-1) * Fs/N。

    比如采样频率为10240Hz,采样点数为1024。即采样一次的时间为1/10240s,100ms可以采样1024个点,1s可以采样10次。则结果中第1个点代表0Hz,第2个点代表10Hz,第3个点代表20Hz,以此类推,第1024个点代表1024Hz。则频率分辨率为10Hz

  • 如果要提高频率分辨力,则必须增加采样点数,也即采样时间。频率分辨率和采样时间是倒数关系。 假设FFT之后某点n用复数a+bi表示,那么这个复数的模就是An=根号aa+bb,相位就是Pn=atan2(b,a)。根据以上的结果,就可以计算出n点(n≠1,且n<=N/2)对应的信号的表达式为An/(N/2)cos(2piFnt+Pn),即2An/Ncos(2piFn*t+Pn)。对于n=1点的信号,是直流分量,幅度即为A1/N。 由于FFT结果的对称性,通常我们只使用前半部分的结果,即小于采样频率一半的结果。

stm32中相关函数

/** 函数1  初始化FFT运算相关参数*	fftLen 			用于指定 FFT长度(16/64/256/1024/4096)本章设置为1024*	ifftFlag 		用于指定是傅里叶变换(0)还是反傅里叶变换(1) 本章设置为0*	bitReverseFlag  用于设置是否按位取反 本章设置为 1* */
arm_status arm_cfft_radix4_init_f32(arm_cfft_radix4_instance_f32 * S,uint16_t fftLen,uint8_t ifftFlag,uint8_t bitReverseFlag)/**  函数2  执行基 4 浮点FFT运算* 	S结构体指针参数 先由 arm_cfft_radix4_init_f32 函数设置好 然后传入该函数的* 	pSrc 		  传入采集到的输入信号数据(实部+虚部形式)同时FFT变换后的数据也按顺序存放在pSrc里面pSrc 				  必须大于等于2倍fftLen长度* */
void arm_cfft_radix4_f32(const arm_cfft_radix4_instance_f32 * S,float32_t * pSrc)/**  函数3  计算复数模值 对 FFT 变换后的结果数据,执行取模操作*	pSrc 		复数输入数组(大小为 2*numSamples)指针,指向 FFT 变换后的结果*	pDst		输出数组(大小为 numSamples)指针,存储取模后的值*	numSamples  就是总共有多少个数据需要取模* */
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 就是总共有多少个数据需要取模

stm32配置

配置时钟树

在这里插入图片描述

添加DSP库

可以从MDK中添加,可以手动添加,这里演示从STM32CubeMX添加
在这里插入图片描述
如果没有安装先点击Install,安装完之后点击框框选中
在这里插入图片描述
选择添加DSP库
在这里插入图片描述

配置定时器触发ADC

这里以TIM3触发ADC1-CH1为例

TIM3挂在APB1上, APB1默认timer时钟为84MHz,这里配置TIM3频率为84000000/(42*100)=20000Hz=20KHz

即采样频率为20KHz,50us触发一次ADC采一次数据,如果是1024个点0.0512s可以采完一次
在这里插入图片描述
配置ADC,先打开DMA
在这里插入图片描述
别忘了点circular
在这里插入图片描述
ADC基础设置
在这里插入图片描述

打开串口,方便调试

很简单,自行配置。打开串口是为了方便查看运算后的信息

生成代码

添加宏

ARM_MATH_CM4//F4是这个
ARM_MATH_MATRIX_CHECK   
ARM_MATH_ROUNDING    

在这里插入图片描述

验证库是否正常

添加头文件

#include "arm_math.h"

编译,可以通过!

编写用户函数

#define FFT_LENGTH		1024 		//FFT长度,默认是1024点FFT/*添加的头文件*/
#include "arm_math.h"
#include "stdio.h"/*printf重定向*/
int fputc(int ch, FILE *f)
{HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);return ch;
}/*全局变量,在main之前定义*/
arm_cfft_radix4_instance_f32 scfft;//定义scfft结构体
float FFT_InputBuf[FFT_LENGTH*2];	//FFT输入数组
float FFT_OutputBuf[FFT_LENGTH];	//FFT输出数组
uint16_t ADC_1_Value_DMA[1024] = {0};//存放ADC的值/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim3);//开启TIM3
HAL_ADC_Start_DMA(&hadc1, (uint32_t *)ADC_1_Value_DMA, FFT_LENGTH);//开启ADC
arm_cfft_radix4_init_f32(&scfft,FFT_LENGTH,0,1);//初始化scfft结构体,设定FFT参数
/* USER CODE END 2 *//*while(1)中*/
for(int i=0; i < FFT_LENGTH; i++)
{FFT_InputBuf[2*i]=ADC_1_Value_DMA[i]; //实部FFT_InputBuf[2*i+1]=0;				  //虚部
}
arm_cfft_radix4_f32(&scfft,FFT_InputBuf);					//FFT计算(基4)
arm_cmplx_mag_f32(FFT_InputBuf,FFT_OutputBuf,FFT_LENGTH);	//取模得幅值

之后可以用vofa+软件方便地查看频谱图。

可以动手实现的小项目

外接一个mic采集、oled实现音乐频谱等


http://www.ppmy.cn/news/520353.html

相关文章

【STM32】 DSP库函数的一些基本使用

对于一些刚接触STM32的童靴来说&#xff0c;DSP库一定是一个陌生的东西。通俗来说&#xff0c;DSP库就是为了让MCU能够使用像DSP&#xff08;数字信号处理的芯片&#xff09;功能弄的一些官方库函数&#xff0c;它是基于MCU的FPU&#xff08;浮点运算功能&#xff09;的&#x…

STM32_arm_sin_f32和sin的区别

FOC控制中会用到三角函数 调试通信的时候&#xff0c;也会用三角函数产生一个测试波形 优化三角函数的运行时间是一个不可避免的事情&#xff0c;而幸运的是&#xff0c;前人已经把树种上了。 用专用的浮点运算单元FPU来做浮点运算&#xff0c;比用STM32本身来做浮点运算要快…

STM32F1 FFT初试

1、STM32有dsp库&#xff0c;百度一堆的文章&#xff0c;注意有f4有硬件fft可以使用arm_rfft_fast_init_f32&#xff0c;F1不能使用&#xff0c;F1可以使用arm_cfft_radix4_init_f32&#xff0c;256长度1024长度&#xff0c;arm_cfft_radix2_init_f32 512长度。 2、采样率FFT_S…

UVM1.2究竟在UVM1.1上做了哪些升级

想必大家平时也没有很注意UVM1.1版本和UVM1.2版本的不同之处&#xff0c;只有在用一些以前UVM1.1能支持的功能&#xff0c;到了UVM1.2却出现编译报错&#xff0c;找不到对应的变量或者函数或者类的时候&#xff0c;才意识到这两个版本的差异。笔者也是遇到了1个打印问题&#x…

【webFlux】使用zip()或concatMap操作符处理两个表,基于条件修改两个表

要响应式地处理两个列表,并基于条件修改第一个列表的一个bean,并根据条件修改第二个列表中对应bean的属性,你可以使用RxJava的操作符来实现。 以下是一个示例代码,展示了如何响应式地处理两个列表并进行相应的修改操作: Observable<List<Bean1>> list1Obser…

基于Web的数字家庭网站设计与实现【附开题报告和万字文档(Lun文)】

主要功能 前台登录&#xff1a; ①主页&#xff1a;新闻信息展示、最新动态、家庭亲子视频展示、亲友动态展示 ②论坛&#xff1a;发布帖子 ③家庭亲自视频&#xff1a;视频类型分类、亲子视频标题 ④家庭日记&#xff1a;日记类型分类、日记标题 ⑤新闻信息&#xff1a;新闻类…

航天双五归零

技术归零中&#xff1a; 定位准确是前提&#xff0c;机理清楚是关键&#xff0c;问题复现是手段&#xff0c;措施有效是核心&#xff0c;举一反三是延伸。 管理归零中&#xff1a; 过程清楚是基础&#xff0c;责任明确是前提&#xff0c;措施落实是核心&#xff0c;严肃处理…

航天动力学

航天动力学是研究航天器和运载器在飞行中所受的力及其在力作用下的运动的学科&#xff0c;又称星际航行动力学。航天动力学研究的运动包括航天器的质心运动&#xff0c;称轨道运动&#xff1b;航天器相对于自身质心的运动和各部分的相对运动&#xff0c;称姿态运动&#xff1b;…