开发一个官方网站要多少钱,免费百度网站建设,本地网站搭建视频教程,怎样做古玩网站系列文章目录 文章目录 系列文章目录前言MATLABSTM32调用DSPSTM32中实现FFT关于初相位 FPGA 前言
最近在学习如何在STM32中调用FFT MATLAB
首先对FFT进行一下说明#xff0c;我们输入N个点的数据到FFT中#xff0c;FFT会返回N个点的数据#xff0c;这些数据都是复数#…系列文章目录 文章目录 系列文章目录前言MATLABSTM32调用DSPSTM32中实现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 2e3; //将不想要的频率的相位筛掉
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(i0;iFFT_SIZE;i)
{inputSignal[i*2] (float)SPI_AD_DATA.RX_DATA_BUF[i503] - 128; //25002inputSignal[i*21] 0;
}最后对计算出的相位要进行处理因为FFT如果选择的数据是非整数个周期计算出的相位是有一定偏移但是在同一频率下同一FFT计算点数下这个偏移量是固定的所以可以计算两个之间的相位差但无法得到相位。
相位差就把得到的两个相位做差就行但需要把负相位加360度转到正最后计算出的相位差还要取绝对值
关于初相位
前面MATLAB介绍了初相位的问题那么STM32中不能计算大部分频率的相位吗其实有一种方法如果能改变ADC的采样率因为频率计算不会出现初相位这样问题那么得到频率后更改ADC的采样率使得在我们设计的采样点数下采集整数个周期就能得到初相。
但感觉实现起来有难度
FPGA
通过调用Quaruts的官方IP核就能实现FFT名单时需要写一个外部控制模块这里我利用ROM来进行测试可以取intel官网找他们的IP手册 LengthFFT变换长度64、128、256、512、1024、2048、4096、8192、16384、32768或65536。可变流还允许8、16、32、131072和262144。
Direction傅里叶变换的方向选择ForwardFFT快速傅里叶变换ReverseFFT快速傅里叶反变换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 error01 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_outsqrt_data_out;
localparam frames_FFT13d1024-1d1;
/*输入数据帧数: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_imag8d0;
/* FFT控制信号1:FFT0:FFT */
assign inverse 1b0;/* 输入错误信号 */
assign sink_error 2b0;
/*置1即可FFT随时可以输出数据*/
assign source_ready1d1;reg [2:0]state;
localparam start_state 3b00;
localparam start_next_state 3b01;
localparam end_input_state 3b10;
localparam wait_output_state 3b11; /*数据读取部分,FFT数据输入*/
always (posedge clk or negedge rst_n) begin if(rst_n 1d0)begin sink_valid1d0;sink_sop1d0;sink_eop 1d0;statestart_state;endelse begincase (state)start_state:begin sink_valid1d1; sink_sop1d1;statestart_next_state;endstart_next_state:begin sink_sop1d0;if(frames_in frames_FFT-1d1) beginstate end_input_state; endendend_input_state:beginsink_valid1d0; sink_eop 1d1; state wait_output_state;endwait_output_state:begin sink_eop 1d0; FFT_output_start1d1;endendcaseend
end/*计算输入数据帧数*/
always (posedge clk or negedge rst_n) beginif(rst_n 1d0) beginframes_in d0;endelse beginif(sink_valid1d1) begin //这里sink_ready会自己拉低frames_in frames_in1d1;endelse beginframes_in frames_in;endend
end/*FFT输出数据读取*//* FFT输出的实部信号,数据是有符号数据需要转换 */
always (*)
beginif(source_valid1d1)fft_real_out source_real[15] ? (~source_real[15:0]1) : source_real;elsefft_real_out 0;
end/* 相当组合逻辑FFT输出的虚部信号 */
always (*)
beginif(source_valid1d1)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_valid1d1)fft_data_out fft_real_out*fft_real_out fft_image_out*fft_image_out;elsefft_data_out 0;
end/*相当组合逻辑FFT输出的指数信号,旋转因子 */
always (*)
beginif(source_valid1d1)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 1d0) beginROM_address d0;endelse beginif(ROM_address11d1022)ROM_addressROM_address1d1;elseROM_addressd0;end
end
endmodule之后用signaltap抓取数据进行测试
以Burst模式、1024数据长度、16Bits数据宽度、ROM为输入的模式进行测试其他数据宽度经过测试效果不佳 此频率代指一段FFT数据1024中有几个周期正弦波因为存在时序打拍所以这里峰值点减1为实际点位