MATLAB、FPGA、STM32中调用FFT计算频率、幅值及相位差

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • MATLAB
  • STM32
    • 调用DSP
    • STM32中实现FFT
    • 关于初相位
  • FPGA


前言

最近在学习如何在STM32中调用FFT


MATLAB

首先对FFT进行一下说明,我们输入N个点的数据到FFT中,FFT会返回N个点的数据,这些数据都是复数,其模值就是我们用来计算频率和幅值,模值越大代表该频率占比越多,模值/N*2就是幅值。每个复数的角度代表了该频率的相位差(这里的初相位不一定准确,只有特定条件下是初相位)。FFT的结果一般是中心对称的,只需要看左半部分就行。

例如:

t = 0:1:255;
x = 30*cos(2*pi/10*t)
figure
plot(x);
y = fft(x)
figure
plot(abs(y))
tol = 2e+3;  //将不想要的频率的相位筛掉
y(abs(y) < tol) = 0;
theta = angle(y);
figure
plot(theta/pi*180);

我们看到第27个点的幅值是最大的,那么该点该表的频率就是我们所求的,假设采样率为X,那么该点的频率为X27/采样点数,该点的幅值=2930/2562,该点的相位这里显示为-71度,但很明显我们的初相位是0
在这里插入图片描述

这里我们不变采样点数,移相30度

x = 30*cos(2*pi/10*t-30/180*pi)

频率没变
在这里插入图片描述
可以看到相位确实变了30度,这就是为什么说计算不了初相位,但是对于同一频率,同样采样点数下的两个波,可以计算相位差的原因
在这里插入图片描述
那么什么情况下计算的结果是初相位呢,只有FFT的输入数据刚好是整数个周期的时候,得到的才是初相位25

x = 30*cos(2*pi/25.6*t-30/180*pi) 

这里输入256个数据,那么我们设计为刚好输入10个周期的情况,很明显,得到的-30度就是我们的初相位
在这里插入图片描述

STM32

调用DSP

首先添加DSP的环境
在这里插入图片描述
之后,添加对应的.lib文件
在这里插入图片描述
添加需要的头文件
在这里插入图片描述
添加CMSIS\DSP\include的路径
在这里插入图片描述
之后就可以顺利编译了

STM32中实现FFT

#include "arm_math.h"
#include "arm_const_structs.h"// FFT 相关参数的定义
#define FFT_SIZE 256
#define SAMPLING_FREQUENCY 10000000
float32_t inputSignal[FFT_SIZE*2];// FFT 输入信号数组
float32_t fftOutput[FFT_SIZE];// FFT 输出数组
uint32_t index_;// 存放 FFT 输出中最大值的索引
float32_t maxValue;
float32_t Vpp;
float32_t frequency ;// 用于存放计算结果的频率变量
float phase;//角度
#define     M_PI            (3.1415926f)void fftCalculate(void)// FFT 计算函数
{uint8_t i;arm_cfft_f32(&arm_cfft_sR_f32_len256, inputSignal, 0, 1);// 执行 FFT 计算//这个就是快速傅里叶变换的主要接口,第一个参数可以理解为你输入到FFT里的采样点的个数;第二个参数为输入数组;第三个参数为正反变换,一般使用填0;第四个参数为位反转使能,一般使用填1。输出会覆盖掉输入arm_cmplx_mag_f32(inputSignal, fftOutput, FFT_SIZE );// 计算 FFT 输出的幅度,输入inputSignal,输出fftOutputindex_ = 0;// 查找 FFT 输出中的最大值maxValue =  fftOutput[1]; //跳过直流分量	for (uint32_t i = 1; i < FFT_SIZE /2; i++)  //这里的数组第0个数据放的是直流分量,跳过就行{if (fftOutput[i] > maxValue){        maxValue = fftOutput[i];index_ = i;}}phase = atan2(inputSignal[2*index_+1],inputSignal[2*index_]) * 180 / 3.1415926f;	//相位frequency = (float32_t)index_ * (float32_t)SAMPLING_FREQUENCY / (float32_t)FFT_SIZE;// 根据最大值的索引计算信号的频率Vpp = maxValue * 2 / 256; //幅值
}

第一个参数可以理解为你输入到FFT里的采样点个数,除了256还有512等等;第二个参数为输入数组;第三个参数为正反变换,一般使用填0;第四个参数为位反转使能,一般使用填1。输出会覆盖掉输入,所以最后计算出的复数会写入到inputSignal中。后面计算相位就需要用到这个数据

arm_cfft_f32(&arm_cfft_sR_f32_len256, inputSignal, 0, 1);

首先要注意,输入到arm_cfft_f32中的数据inputSignal是由一个实部一个虚部组成的,所以调用的时候要手动将虚部设置为0

for(i=0;i<FFT_SIZE;i++)
{inputSignal[i*2] = (float)SPI_AD_DATA.RX_DATA_BUF[i+503] - 128; //2+500+2inputSignal[i*2+1] = 0;
}

最后对计算出的相位要进行处理,因为FFT如果选择的数据是非整数个周期,计算出的相位是有一定偏移,但是在同一频率下,同一FFT计算点数下,这个偏移量是固定的,所以可以计算两个之间的相位差,但无法得到相位。

相位差就把得到的两个相位做差就行,但需要把负相位加360度转到正,最后计算出的相位差还要取绝对值

关于初相位

前面MATLAB介绍了初相位的问题,那么STM32中不能计算大部分频率的相位吗,其实有一种方法,如果能改变ADC的采样率,因为频率计算不会出现初相位这样问题,那么得到频率后,更改ADC的采样率,使得在我们设计的采样点数下,采集整数个周期,就能得到初相。

但感觉实现起来有难度

FPGA

通过调用Quaruts的官方IP核就能实现FFT名单时需要写一个外部控制模块,这里我利用ROM来进行测试,可以取intel官网找他们的IP手册
在这里插入图片描述在这里插入图片描述

在这里插入图片描述
Length:FFT变换长度64、128、256、512、1024、2048、4096、8192、16384、32768或65536。可变流还允许8、16、32、131072和262144。

Direction:傅里叶变换的方向,选择Forward:FFT(快速傅里叶变换),Reverse:FFT(快速傅里叶反变换),Bi-directional(用户可通过输入控制是FFT还是IFFT)

Calculation:计算的延迟
Throughput Latency:处理延迟

Burst:突发架构,需要的内存资源最小,平均吞吐量最低。
Buffered Brust:缓冲突发架构需要的内存资源比流式低,平均吞吐量较低。
Streaming:流式架构可以连续变换处理。
Variable Streaming:可变流式架构可以连续处理,并且可以运行时控制变换长度。在线改变FFT的大小,速度和流式差不多。前三种模式运算速度依次增大,占用资源也一次增大。
Input Otder:输入数据的顺序(顺序模式或逆序模式)
Output Order:输出数据的顺序(顺序模式或逆序模式)

Representation:数据结构和旋转因子
Block Floating(块浮点):应用Burst、Buffered Brust、Streaming
Fixed Point(定点)和Single Floating Point(单浮点):用于Variable Streaming
块浮点就是在数据的一帧数据中有一个共同的缩放因子,当一帧数据中有大有小的时候,共用一个缩放因子会造成误差增大。
Data Input Width:输入数据的数据宽度,8, 10, 12, 14, 16, 18, 20,24, 28, 32。
Twiddle Width:旋转因子的数据宽度,旋转因子的数据宽度不能大于输入数据的数据宽度。
Data Output Width::输出数据的数据宽度。FFT的计算结果是输出的实部和虚部与缩放因子(EXP)的结合,缩放因子为负,输出数据需要左移(增大),为正则右移,输出的实部和虚部,缩放因子都是有符号数。
在这里插入图片描述
clk : (in) 时钟信号
reset_n : (in)复位信号,低电平有效,至少一个时钟周期
inverser : (in)低电平为FFT,高电平为IFFT
sink_valid : (in)输入数据有效信号
sink_sop : (in)输入数据起始信号,与第一个数据对齐,只需保持一个时钟周期即可
sink_eop : (in)输入数据结束信号,与最后一个数据对齐,只需保持一个时钟周期
sink_real : (in)输入数据的实部
sink_imag : (in)输入数据的虚部,一般直接置0
sink_ready : (out) IP核准备好接收数据了,实测一直高电平
sink_error : (in)输入错误信号,置0即可,不会影响。00 = no error,01 = missing start of packet (SOP),10 = missing end of packet (EOP),11 = unexpected EOP

source_error : (out) 输出错误信号,若输入的数据格式有误,则不进行FFT变换,并给出错误值。
source_ready : (in)输入数据准备好,置1即可,随时可以输出数据
source_sop : (out)输出数据起始信号,与输出的第一个数据对齐
source_eop : (out)输出数据的终止信号,与输出的最后一个数据对齐
source_real : (out)输出数据的实部
source_imag : (out)输出数据的虚部
source_exp : (out)数据数据的缩放因子,只有流式、突发和缓冲突发模式有。
source_vaild : (out) IFFT控制线,FFT完成时,信号置高,输出数据

输入数据时序:
在这里插入图片描述
输出数据时序:
在这里插入图片描述
多次调用时序:
在这里插入图片描述

module FFT_control
(input                         clk,input                         rst_n,input   [15:0]          source_real,input   [15:0]          source_imag,input   [5:0]            source_exp,   input                        source_valid,            input    [1:0]           source_error, input                        source_sop, input                        source_eop, input                         sink_ready,output   reg            sink_valid,output   reg            sink_sop,output   reg            sink_eop,output   wire    [1:0]         sink_error,output   wire   [15:0]      sink_imag,                      //实部数据采集模块输入,虚部直接置0output   wire          source_ready,output   wire            inverse,    output   reg        	[ 10:0] 	ROM_address,output   wire        	[ 11:0] 	FFT_out,output   wire        	[ 15:0] 	sqrt_data_out);assign FFT_out=sqrt_data_out;
localparam frames_FFT=13'd1024-1'd1;
/*输入数据帧数:1024或2048看IP核内部使用*/ 
reg  [12:0] frames_in;reg  FFT_output_start;reg 		[ 15:0] 	fft_real_out;
reg 		[ 15:0] 	fft_image_out;
reg 		[ 31:0] 	fft_data_out;
reg 		[ 5:0] 	    fft_exp_out;reg   FFT_count_output;
reg   FFT_count_output_MAX;
reg   FFT_count_output_start;/* FFT输入数据虚部 */
assign  sink_imag=8'd0;
/* FFT控制信号,1:FFT,0:FFT */
assign 	inverse = 1'b0;/* 输入错误信号 */
assign 	sink_error = 2'b0;
/*置1即可,FFT随时可以输出数据*/
assign 	source_ready=1'd1;reg [2:0]state;
localparam 	start_state = 3'b00;
localparam 	 start_next_state= 3'b01;	
localparam 	 end_input_state= 3'b10;
localparam 	 wait_output_state= 3'b11;		/*数据读取部分,FFT数据输入*/
always @(posedge clk or negedge rst_n) begin    if(rst_n== 1'd0)begin									sink_valid<=1'd0;sink_sop<=1'd0;sink_eop <= 1'd0;state<=start_state;endelse begincase (state)start_state:begin		sink_valid<=1'd1;           sink_sop<=1'd1;state<=start_next_state;endstart_next_state:begin sink_sop<=1'd0;if(frames_in == frames_FFT-1'd1) beginstate <=end_input_state;		endendend_input_state:beginsink_valid<=1'd0;   sink_eop <= 1'd1;  state <=wait_output_state;endwait_output_state:begin sink_eop <= 1'd0;  FFT_output_start=1'd1;endendcaseend
end/*计算输入数据帧数*/
always @ (posedge clk or negedge rst_n)  beginif(rst_n== 1'd0) beginframes_in <= 'd0;endelse beginif(sink_valid==1'd1) begin              //这里sink_ready会自己拉低frames_in <= frames_in+1'd1;endelse beginframes_in <= frames_in;endend                
end/*FFT输出数据读取*//* FFT输出的实部信号,数据是有符号数据,需要转换 */
always @ (*)
beginif(source_valid==1'd1)fft_real_out = source_real[15] ? (~source_real[15:0]+1) : source_real;elsefft_real_out = 0; 
end/* 相当组合逻辑,FFT输出的虚部信号 */
always @ (*)
beginif(source_valid==1'd1)fft_image_out = source_imag[15] ? (~source_imag[15:0]+1) : source_imag;elsefft_image_out = 0;
end/* 相当组合逻辑,FFT输出的数据转换 ,这个数据不能直接用来就平方根,老不稳定,很奇怪,必须加时序,加了就会延后1拍*/
always @ (posedge clk )
beginif(source_valid==1'd1)fft_data_out <= fft_real_out*fft_real_out + fft_image_out*fft_image_out;elsefft_data_out <= 0;
end/*相当组合逻辑,FFT输出的指数信号,旋转因子 */
always @ (*)
beginif(source_valid==1'd1)fft_exp_out = source_exp;elsefft_exp_out = fft_exp_out;
end/* 平方根模块IP核,fft_data_out时序电路延迟1拍 */
ALtearsqrt      SQRT_Module		 
(.radical 	(fft_data_out ),.q 			(sqrt_data_out),
);always @(posedge clk or negedge rst_n) begin    if(rst_n== 1'd0) beginROM_address <= 'd0;endelse beginif(ROM_address<11'd1022)ROM_address<=ROM_address+1'd1;elseROM_address<='d0;end            
end 
endmodule

之后用signaltap抓取数据进行测试:

以Burst模式、1024数据长度、16Bits数据宽度、ROM为输入的模式进行测试(其他数据宽度经过测试效果不佳)
在这里插入图片描述
此频率代指一段FFT数据(1024)中有几个周期正弦波,因为存在时序打拍,所以这里峰值点减1为实际点位
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53867.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ctfshow-PHP反序列化

web254 源码 <?php/* # -*- coding: utf-8 -*- # Author: h1xa # Date: 2020-12-02 17:44:47 # Last Modified by: h1xa # Last Modified time: 2020-12-02 19:29:02 # email: h1xactfer.com # link: https://ctfer.com //mytime 2023-12-4 0:22 */ error_reporting(0)…

MATLAB | R2024b更新了哪些好玩的东西?

Hey, 又到了一年两度的MATLAB更新时刻&#xff0c;MATLAB R2024b正式版发布啦&#xff01;&#xff0c;直接来看看有哪些我认为比较有意思的更新吧! 1 小提琴图 天塌了&#xff0c;我这两天才写了个半小提琴图咋画&#xff0c;MATLAB 官方就出了小提琴图绘制方法。 小提琴图…

鸿蒙读书笔记1:《鸿蒙操作系统设计原理与架构》

笔记来自新书&#xff1a;《鸿蒙操作系统设计原理与架构》 HarmonyOS采用分层架构&#xff0c;从下到 上依次分为内核层、系统服务层、框架层和应用层。 1. 内核层 内核层主要提供硬件资源抽象和常用软件资源&#xff0c;包括进程/线程管 理、内存管理、文件系统和IPC&#xff…

Unity教程(十五)敌人战斗状态的实现

Unity开发2D类银河恶魔城游戏学习笔记 Unity教程&#xff08;零&#xff09;Unity和VS的使用相关内容 Unity教程&#xff08;一&#xff09;开始学习状态机 Unity教程&#xff08;二&#xff09;角色移动的实现 Unity教程&#xff08;三&#xff09;角色跳跃的实现 Unity教程&…

react js 路由 Router

完整的项目,我已经上传了 资料链接 起因, 目的: 路由, 这部分很难。 原因是, 多个组件,进行交互,复杂度比较高。 我看的视频教程 1. 初步使用 安装: npm install react-router-dom 修改 index.js/ 或是 main.js 把 App, 用 BrowserRouter 包裹起来 2. Navigate 点击…

redis基本数据类型和常见命令

引言 Redis是典型的key-value&#xff08;键值型&#xff09;数据库&#xff0c;key一般是字符串&#xff0c;而value包含很多不同的数据类型&#xff1a; Redis为了方便我们学习&#xff0c;将操作不同数据类型的命令也做了分组&#xff0c;在官网&#xff08; Commands | Do…

TS 常用类型

我们经常说TypeScript是JavaScript的一个超级 TypeScript 常用类型 TypeScript 是 JS 的超集&#xff0c;TS 提供了 JS 的所有功能&#xff0c;并且额外的增加了&#xff1a;类型系统 所有的 JS 代码都是 TS 代码 JS 有类型&#xff08;比如&#xff0c;number/string 等&…

《JavaEE进阶》----14.<SpringMVC配置文件实践之【验证码项目】>

本篇博客介绍的是Google的开源项目Kaptcha来实现的验证码。 这种是最简单的验证码。 也是很常见的一种验证码。可以去看项目结果展示。就可以明白这个项目了。 前言&#xff1a; 随着安全性的要求越来越高、很多项目都使用了验证码。如今验证码的形式也是有许许多多、更复杂的图…

基于SpringBoot的古城墙景区管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的古城墙景区管理系…

2024数学建模国赛官方评阅标准发布!

​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑​↑…

C++类与对象(下)--最后的收尾

内部类 • 如果⼀个类定义在另⼀个类的内部&#xff0c;这个内部类就叫做内部类。内部类是⼀个独⽴的类&#xff0c;跟定义在 全局相⽐&#xff0c;他只是受外部类类域限制和访问限定符限制&#xff0c;所以外部类定义的对象中不包含内部类。 #include<iostream> using…

安装FTP服务器教程

一。安装vsftpd yum install vsftpd 二。修改配置文件&#xff0c;匿名账户具有访问&#xff0c;上传和创建目录的权限 vim /etc/vsftpd/vsftpd.conf &#xff08;红色进行设置放开YES&#xff09; local_enable&#xff1a;本地登陆控制&#xff0c;no表示禁止&#xff0c;ye…

3. 轴指令(omron 机器自动化控制器)——>MC_MoveAbsolute

机器自动化控制器——第三章 轴指令 4 MC_MoveAbsolute变量▶输入变量▶输入输出变量▶输入输出变量 功能说明▶指令详情▶时序图▶重启运动指令▶多重启动运动指令▶异常 示例程序1▶参数设定▶动作示例▶梯形图▶结构文本(ST) 示例程序2▶参数设定▶动作示例▶梯形图▶结构文…

RDMA应用场景及效果

GPU Direct 参考&#xff1a;网络架构如何支持超万卡的大规模 AI 训练&#xff1f;| AICon_芯片与网络_InfoQ精选文章 GPU 网络的情况已经发生了很大变化。每个 GPU 都有自己的内部互联&#xff0c;例如 NVIDIA 的 A100 或 H800&#xff0c;它们内部的 NVLink 互联可以达到 6…

单个 java 虚拟机 生产者消费者

一、通过 java.lang.Object#wait()&#xff0c;java.lang.Object#notify&#xff0c;java.lang.Object#notifyAll来实现 生产者&#xff0c;消费者 public abstract class Goods {protected String type;protected String goodName;protected int number;public abstract …

【Authing身份云-注册安全分析报告-无验证方式导致安全隐患】

前言 由于网站注册入口容易被黑客攻击&#xff0c;存在如下安全问题&#xff1a; 1. 暴力破解密码&#xff0c;造成用户信息泄露 2. 短信盗刷的安全问题&#xff0c;影响业务及导致用户投诉 3. 带来经济损失&#xff0c;尤其是后付费客户&#xff0c;风险巨大&#xff0c;造…

香港打工人√三天通过微软mos认证

在繁忙的香港&#xff0c;时间就是金钱&#xff0c;效率就是生命。作为一名香港的打工人&#xff0c;在这座竞争激烈的城市中&#xff0c;不断提升自我是保持竞争力的关键。最近&#xff0c;我完成了一项挑战&#xff1a;在短短三天内通过微软MOS认证大师。以下是我备考的经验分…

微调大模型:提高其代码修复能力的尝试

目录 一、作品背景&#xff1a; 二、作品目标&#xff1a; 三、作品技术方案&#xff1a; (1)标记化 (2)量化 (3) LoRA&#xff08;低秩自适应&#xff09;配置 (4)训练配置 (6)模型保存 四、作品效果&#xff1a; 一、作品背景&#xff1a; 随着大型模型技术的日益成…

SOMEIP_ETS_107: SD_Consider_Entries_Order

测试目的&#xff1a; 验证DUT在接收到包含两个条目的消息时&#xff0c;能够按照正确的顺序处理&#xff1a;首先是带有TTL 0的SubscribeEventgroup以删除订阅&#xff0c;然后是常规的SubscribeEventgroup以重新订阅。 描述 本测试用例旨在确保DUT能够正确处理订阅消息的顺…

Frida0C - Module相关API

亲爱的读者你们好啊&#xff0c;今天主要分享一下 frida 相关的学习文档&#xff0c;见文章最后一节。 Module var module Process.findModuleByAddress(Module.findBaseAddress("libc.so")) module.enumerateSymbols()enumerateSymbols 返回该 so 的符号表。 还…