ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于Vivado FFT IP核的实时信号幅度与频率估计方法

基于Vivado FFT IP核的实时信号幅度与频率估计方法 简介这份资源是一套基于Vivado开发环境、利用FFT IP核完成信号幅度与频率估计的完整工程包面向FPGA开发者和数字信号处理初学者也适合正在调试频谱分析模块的工程师参考。资源围绕频率与幅度两个关键点展开频率估计中给出了由m_axis_data_tuser、采样率与FFT点数共同确定的换算公式例如82乘250M除以1024对应20.0195MHz幅度估计则区分复信号与实信号两种输入明确指出实信号输出为幅度有效值的一半这一细节对实际工程判读非常重要。压缩包共457个文件约63.22MB以VHDL、Verilog源码为主体配合XDC约束、DO与TCL仿真脚本、XCI核配置、XPR工程文件以及大量仿真日志、波形配置和网表文件完整呈现了从创建工程、配置IP核到仿真验证的典型流程。已有1188人学习下载。解压后可按源码、仿真、约束、IP配置等模块快速查阅有助于理解FFT IP核的接口时序和频谱换算方法是一份可以直接对照学习的工程样例。1. 为什么说FFT IP核是估计幅度频率的最快路径做信号处理系统的人早晚都会碰到这个需求给我一路采样信号我要知道它的频率是多少、幅度多大。我最近在Vivado里做中频信号监测采样率50MHz需要实时估计5MHz左右信号的幅度和频率一开始也想自己写FFT折腾一圈下来还是老老实实用了IP核。1.1 一个看似简单的FFT需求先交代下应用背景。系统有一路ADC输出16bit采样率50MHz信号是5MHz附近的正弦波幅度会随外界环境波动。我需要估算两个量频率用于判断信号源是否漂移幅度用于自动增益控制。精度要求是频率误差不超过1%幅度误差不超过3%。最开始我觉得这个需求不复杂FFT算法在教科书里有完整推导基2蝶形、旋转因子、位反转都是现成的东西。真动起手来才发现自己写FFT要处理的工程问题远不止算法本身定点化怎么定标、中间级溢出怎么处理、FFT点数变化时旋转因子表怎么重新生成、时序能不能收敛。这些问题每一个都足够消耗一个下午而且写出来的东西是否正确还需要大量的测试向量去验证。1.2 用IP核的真实现状Xilinx的FFT IP核在Vivado里非常成熟支持从8点到65536点的变换长度数据位宽和相位因子位宽都可以配置还提供了多种实现架构。对于估计信号幅度和频率这个需求我的判断是IP核可以覆盖绝大部分功能而且经过官方大量验证出问题的概率远低于自己写的逻辑。有人说IP核是个黑盒子不好控制。实际用下来不是这样它的AXI4-Stream接口非常标准输入输出时序明确配置界面里把参数设好后重点就转移到算法层面的标定和验证上。换句话说IP核帮你把FFT计算这个体力活干完了你可以把精力放在怎么用FFT结果这个真正有技术含量的部分。2. Vivado FFT IP核配置前必须搞清楚的参数逻辑配置IP核看起来是鼠标点几个下拉框但每个选项背后都直接影响后面的数据处理。在我这里最关键的是变换长度、数据位宽和缩放策略下面逐一展开。2.1 变换长度怎么选先算频率分辨率频率分辨率的公式很简单Δf fs / Nfs是采样率N是FFT点数。我的系统采样率50MHz如果选4096点分辨率就是50MHz / 4096约等于12.2kHz。要分辨5.000MHz和5.012MHz的信号这个分辨率足够。如果后续需求要分辨更近的频点就得加大N或者降低有效采样率。FFT点数每翻一倍资源消耗大概也要翻倍尤其是存储资源和DSP48。所以我建议先按需要分辨的最小频率间隔去算N不要一上来就选65536点。4096点对我来说够用了计算延迟也不大。2.2 位宽设置输入16bit但要注意内部溢出我的ADC是16bit所以输入数据位宽选16bit相位因子位宽也选了16bit。这里有个容易忽略的点FFT内部蝶形运算每级都会引入数据位增长如果不做任何处理为了防止溢出中间数据位宽就得做得非常大。IP核给出了三种算术选项无缩放Unscaled、缩放Scaled、块浮点Block Floating Point。我建议大多数幅度频率估计场景用Scaled模式配合合适的缩放策略既控制住了位宽又保证了精度。为什么不用Unscaled因为输出位宽会明显变大后续的模值计算和存储都得多花资源而且动态范围需求没到那份上。为什么不用块浮点块浮点精度最好但实现资源相对复杂流水线延迟也会更大。折中下来Scaled模式最实用。2.3 实现架构的选择逻辑这个IP核有Pipelined Streaming、Radix-4 Burst I/O、Radix-2 Burst I/O三种架构。Pipelined Streaming吞吐最大可以连续处理数据流但资源消耗最多Radix-4 Burst和Radix-2 Burst都是突发式处理需要等一整帧数据收完再开始计算吞吐低但省资源。我的场景是一帧一帧地采集数据每帧4096个点采集完后有充足的时间做FFT计算所以选了Radix-4 Burst I/O。实测下来资源占用比Streaming大概少三分之一左右。如果你的应用是连续实时频谱显示那就得上Pipelined Streaming这个选择没有对错只有适不适合。3. AXI4-Stream接口驱动把数据正确喂进去、把结果正确拿出来配置好IP核后最关键的工程环节就是接口时序。FFT IP核用的是AXI4-Stream协议输入和输出都遵循tvalid/tready握手机制。这一节讲我实际编写驱动逻辑时踩到的细节。3.1 输入侧tvalid与tready的握手节奏输入接口有几个关键信号s_axis_data_tvalid、s_axis_data_tready、s_axis_data_tdata、s_axis_data_tlast。tready信号由IP核拉高时表示可以接收数据我们作为主控方需要把tvalid拉高并保持直到tready为高时完成一次数据传输。一个容易犯的错是看到tvalid拉高就以为数据被接收了实际上只有当tvalid和tready同时为高数据才真正有效。我第一次调试时就吃过亏控制逻辑里只判断了tready丢了好几拍数据。正确的做法是使用一个简单的valid-ready握手状态机把s_axis_data_tdata[15:0]作为实部、tdata[31:16]作为虚部。当输入是纯实数信号时虚部固定填0即可。这里要特别注意IP核的tdata位宽是(2乘以数据位宽加相位因子位宽)向上取整到8的整数倍实际有效位需要按配置去截取。tlast信号用于标记一帧数据的最后一个采样点。在帧模式下IP核检测到tlast后才会认为当前帧结束开始对这一帧做FFT运算。如果不拉tlastIP核会一直等待数据根本不会启动计算。3.2 输出侧数据就绪的事件响应输出接口包括m_axis_data_tvalid、m_axis_data_tlast、m_axis_data_tdata以及m_axis_data_tuser。m_axis_data_tuser携带帧索引当tvalid拉高时tuser等于0表示FFT结果有效。这个信号在连续多帧处理时用来区分帧边界。关于输出数据的位宽同样需要从tdata里按位截取。输出结果分实部和虚部各占数据位宽。比如配置了16bit输入、缩放模式输出每个频点的实部和虚部其实都已经经过了IP核内部的定点缩放。我一般会在数据有效时把实部和虚部拼成32bit写进BRAM供后级处理读取。还有一个容易被忽略的细节输出序。IP核可以配置输出顺序是自然序还是位反转序。我的算法需要按频率自然递增的顺序处理频点所以配置成自然序输出。如果只看峰值频点位反转序就得自己再倒腾一遍坐标非常麻烦建议直接配自然序。4. 从FFT结果到幅度和频率标定计算是重点FFT算完只是第一步真正决定幅度频率估计精度的是后面的标定。这里我不打算啰嗦教材上的公式推导直接给出我实际使用的计算流程和注意事项。4.1 幅度计算别忘了解除缩放和除以N/2FFT输出的每个频点是复数包含实部和虚部。峰值频点的幅度模值计算公式是mag sqrt(real^2 imag^2)但这个mag并不直接等于信号的真实幅度。它和真实幅度之间的关系取决于FFT长度N和IP核的缩放策略。以16bit输入、4096点FFT为例如果输入信号是幅度为A的正弦波理论上峰值频点的模值等于A乘以N/2。所以反推真实幅度A mag × 2 / N这里还没有考虑IP核的缩放。Scaled模式下IP核在每一级蝶形运算里做了定点缩放输出结果相对理论值会缩小2的s次方倍s是总的缩放位数。我在配置IP核时记录下这个缩放因子在幅度恢复时统一乘回来。我实际使用的恢复公式是A mag × 2^s × 2 / N其中s在Vivado的配置汇总里能看到。我配置了自动缩放策略每级缩1位4096点FFT有12级蝶形运算所以s12。把这个系数算对后实测输入1.0V幅度的正弦波恢复出来的幅度误差在1%以内。4.2 频率计算频点索引换算加细插值频率的计算相对直接f k × fs / Nk是峰值频点的索引fs是采样率N是FFT点数。以50MHz采样率、4096点为例如果峰值在索引417处频率就是417乘以50e6再除以4096约等于5.090MHz。不过直接用峰值索引算频率有个明显限制频率分辨率等于fs/N大约12.2kHz。如果需求是频率误差不超过1%对应的绝对误差是50kHz左右单纯用峰值索引其实也够。但如果要更精细就得做插值。我常用的方法是抛物线插值取峰值频点k以及左右两个相邻频点k-1、k1的幅值拟合一条抛物线用抛物线的顶点位置得到修正后的频点坐标。这个方法实现成本低在信噪比还行的情况下能把频率估计精度提升一个数量级以上。5. 实测中踩过的坑窗函数、直流分量和仿真验证再分享几个我实际调试中遇到的问题这些教训在文档里很少直接写明但对做幅度频率估计的人来说非常关键。5.1 频谱泄漏和窗函数的选择一开始我没有加窗函数直接对原始采样点做FFT。结果发现一个现象当信号频率正好落在两个频点之间时峰值幅度明显偏小旁边还出现一堆旁瓣幅度估计误差最大能到百分之十几。这就是频谱泄漏。解决办法是加窗函数。我用了汉宁窗Hann Window在数据进IP核之前把每个采样点乘以窗函数系数。加窗后旁瓣被压下去了幅度估计也稳定了。但代价是等效噪声带宽变宽频率分辨率会稍微变差。对于我的应用这个影响可以接受。需要注意加窗会改变幅度标定系数。汉宁窗的相干增益是0.5等于把信号能量折半了。在实际计算幅度时需要在恢复公式里再除以0.5也就是乘2。我在这里也卡了很久最后通过对比输入正弦波的理论幅度才定位到原因。5.2 直流分量和小信号估计在做幅度估计时如果信号有直流偏置FFT结果的0号频点会有一个很大的分量。这个分量本身不影响正弦峰值的定位但如果直流分量很大加上窗函数后频谱会拖尾可能掩盖旁边的低幅度信号。我建议在进FFT之前先做直流去除累加一帧数据的平均值然后每个采样点减去这个平均值。处理完之后0号频点会显著降低后续峰值搜索也干净很多。这个预处理操作虽然简单但对幅度估计稳定性的提升非常明显。5.3 仿真验证和实测对比我建议在Vivado里先用Testbench做一次纯仿真验证生成一个已知幅度和频率的正弦波数据喂给IP核检查输出的幅度频率是否和理论一致。仿真的好处是信号源是理想的可以快速定位是IP核配置问题还是标定问题。我在仿真里用5MHz、幅度为4095的正弦波采样率50MHzFFT点数4096。仿真跑通后再用ILA集成逻辑分析仪抓实际ADC数据对比实时计算的幅度和频率。这里有一个ILA使用的细节ILA的采样深度要能覆盖完整的一帧FFT处理过程至少抓取几万个采样点否则可能抓不到完整的数据帧。另外ILA抓到的数据是定点数换算成物理量时别忘了带进采样率和FFT长度这些参数。6. 把整套逻辑串起来资源开销与后续扩展整套链路都跑通之后我记录了一些实际的资源和性能数据也思考了这个方案后续的扩展方向这里一并分享出来。6.1 资源占用与实测数据我的工程在XC7Z020上实现FFT核选的是4096点、16bit输入、Radix-4 Burst I/O架构加窗和直流去除逻辑用了一个乘法器和一个累加器。整套逻辑的资源开销大约是LUT用了不到5%BRAM用了8块左右DSP48用了6个。这个占用比例相当低说明用IP核做FFT处理并不会给系统带来很大的资源压力。从性能上看4096点FFT在100MHz时钟下Radix-4 Burst模式完成一次变换大概需要几十微秒量级具体和配置里的延迟参数有关。对于我们需要周期性刷新幅度频率估计的场景这个速度完全够用。如果你用Pipelined Streaming架构吞吐会高很多但资源占用也要明显上一个台阶。6.2 从单帧估计到连续监测的扩展思路目前实现的是单帧估计采一帧数据做一次FFT出一个幅度和一个频率。如果要做连续监测有两个思路。一个思路是采用滑窗重叠每采到新的半帧数据就和上一帧的后半帧拼成一个新帧再做FFT。这样估计结果的更新率可以翻倍但对FFT核的调用频率要求也高了可能需要换Pipelined Streaming架构。另一个思路是不改变FFT核的配置在软件侧做结果平滑。把每次估计出来的幅度和频率做一阶低通滤波比如 y_new alpha乘以x加上(1减alpha)乘以y_old可以有效抑制单次估计的抖动。这个思路实现成本几乎为零我实际加了一个alpha等于0.3的平滑之后幅度估计的跳动明显减小了。另外如果你后续需要估计的不是单一正弦信号而是多个频率分量可以基于这套流程扩展在峰值搜索阶段记录前几个幅度最大的频点分别做插值和幅度恢复。FFT IP核本身不需要改改的只是后处理逻辑。这种复用性也是我推荐用IP核的一个重要原因——硬件计算单元是固定的算法升级都在软逻辑层面完成迭代起来非常快。本文还有配套的精品资源点击获取
返回列表