基于FPGA的等精度数字频率计设计实践
在现代电子测量系统中,频率作为最基础的信号参数之一,其测量精度直接影响通信、雷达、工业控制等领域的性能表现。然而,传统测频方法长期面临一个尴尬局面:高频时相对误差大,低频时分辨率不足——这种“顾此失彼”的特性严重制约了通用仪表的设计。
有没有一种方案,能让1Hz和100MHz的信号都用相同的精度去测量?答案是肯定的。等精度测频技术正是为解决这一难题而生。它不依赖被测信号本身的周期特性,而是通过巧妙的同步门控机制,在整个频段内实现恒定的相对误差。更关键的是,这项技术天然适合FPGA实现——利用硬件并行性和精确时序控制,完全可以摆脱MCU参与,构建出真正意义上的全硬件测量引擎。
本文基于Quartus II 13.1开发环境,采用Verilog HDL语言,完整实现了一个可在Cyclone系列FPGA上运行的等精度频率计。整个过程不仅涉及核心算法的工程化落地,还包括对边沿检测、状态机协调、数据锁存等细节的深度打磨。最终成果不仅能稳定测量从几Hz到数十MHz的信号,更重要的是,它的设计理念可轻松扩展至其他高精度时间测量场景。
理解等精度测频的核心,首先要跳出传统思路。直接测频法以待测信号为闸门,标准时钟计数;周期测频法则反过来。两者本质上都是单变量主导的测量模式,自然难以兼顾高低频表现。而等精度方案的关键突破在于引入了“双计数+同步结束”机制:
想象这样一个场景:我们设定一个1秒的测量窗口(由50MHz晶振驱动的定时器生成),但这个窗口的开启和关闭都必须与待测信号的上升沿对齐。也就是说,当第一个上升沿到来时,两个计数器同时启动——一个是记录待测信号脉冲数Nx,另一个是对50MHz标准时钟计数Ns。1秒后,系统并不立即停止,而是等待下一个上升沿才真正关闭计数。这样做的妙处在于,无论待测信号频率多低,每个完整周期都被完整计入,彻底消除了±1字的量化误差。
最终计算公式也极为简洁:
$$
f_x = \frac{N_x}{N_s} \times f_s
$$
由于Ns ≈ fs × Tgate,在Tgate固定的情况下,Ns接近常数,因此fx的测量误差主要取决于计数本身的准确性,而非信号频率高低。理论上,只要闸门时间足够长,即使是1Hz信号也能达到0.1%甚至更高的精度。
下面是该逻辑在Verilog中的实现片段,看似简单,却凝聚了多个关键设计考量:
// 模块:等精度频率计顶层
module freq_meter (
input clk_50m, // 50MHz 标准时钟
input rst_n, // 复位信号,低电平有效
input fx_in, // 待测信号输入
output [31:0] nx_out, // Nx 输出
output [31:0] ns_out, // Ns 输出
output valid_flag // 数据有效标志
);
parameter GATE_TIME_US = 1_000_000; // 1秒闸门时间(单位:μs)
localparam CLK_FREQ = 50_000_000;
// 内部信号
reg gate_en; // 主控门使能
reg lock_en; // 锁存使能
reg [31:0] counter_fx;
reg [31:0] counter_fs;
reg prev_fx;
// 生成闸门时间定时器(基于50MHz)
reg [31:0] timer_cnt;
wire timeout = (timer_cnt >= GATE_TIME_US * 50 - 1); // 50MHz → 每μs计50次
// 状态机定义
localparam IDLE = 2'b00,
STARTED = 2'b01,
COUNTING = 2'b10,
LOCKED = 2'b11;
reg [1:0] state;
// 上升沿检测
wire fx_rising = (~prev_fx) & fx_in;
// 主状态机
always @(posedge clk_50m or negedge rst_n) begin
if (!rst_n) begin
state <= IDLE;
gate_en <= 0;
lock_en <= 0;
timer_cnt <= 0;
counter_fx <= 0;
counter_fs <= 0;
prev_fx <= 0;
end else begin
prev_fx <= fx_in;
case (state)
IDLE: begin
gate_en <= 0;
lock_en <= 0;
if (fx_rising) begin
state <= STARTED;
end
end
STARTED: begin
gate_en <= 1;
counter_fx <= 0;
counter_fs <= 0;
timer_cnt <= 0;
state <= COUNTING;
end
COUNTING: begin
if (fx_rising)
counter_fx <= counter_fx + 1;
counter_fs <= counter_fs + 1;
if (timeout && fx_rising) begin // 同步关闭
gate_en <= 0;
lock_en <= 1;
state <= LOCKED;
end else begin
timer_cnt <= timer_cnt + 1;
end
end
LOCKED: begin
lock_en <= 0;
state <= IDLE;
end
default: state <= IDLE;
endcase
end
end
// 数据锁存
reg [31:0] nx_latched;
reg [31:0] ns_latched;
reg valid_reg;
always @(posedge clk_50m) begin
if (lock_en) begin
nx_latched <= counter_fx;
ns_latched <= counter_fs;
valid_reg <= 1;
end else if (valid_reg && state == IDLE) begin
valid_reg <= 0;
end
end
// 输出赋值
assign nx_out = nx_latched;
assign ns_out = ns_latched;
assign valid_flag = valid_reg;
endmodule
这段代码中最值得玩味的是
COUNTING
状态下的判断条件:
if (timeout && fx_rising) begin
gate_en <= 0;
lock_en <= 1;
state <= LOCKED;
end
这里没有使用“超时即关闭”的粗暴方式,而是将
timeout
请求与
fx_rising
事件做逻辑与操作。这意味着即使定时器已经到达1秒,系统仍会耐心等待下一个上升沿才执行锁存动作。这正是消除±1误差的灵魂所在。
当然,实际部署时还需注意若干工程细节。比如输入信号若存在抖动或噪声,可能导致误触发。虽然理想情况下应在前端增加施密特触发器进行硬件整形,但在FPGA内部也可通过两级同步加去抖逻辑缓解问题。另外,对于极低频信号(如<1Hz),建议动态延长闸门时间,否则Nx过小会导致有效位数不足。
整个设计在Quartus II 13.1环境下完成编译与下载。选择这个经典版本并非偶然——相比新版Quartus Prime,13.1对老旧PC更为友好,资源占用更低,且无需复杂的许可证配置。对于教学实验或小型项目而言,其稳定性远胜频繁更新带来的兼容性风险。
具体流程如下:创建工程后选定目标器件(如EP2C5T144C8),导入Verilog文件,并在Pin Planner中完成引脚分配。例如将
clk_50m
绑定至板载晶振对应的PIN_85,
fx_in
接用户输入端口PIN_23。随后进行全编译,工具链会自动完成综合、布局布线与时序分析。如果一切正常,生成的.sof文件可通过USB-Blaster下载至FPGA。
值得一提的是,为了确保时序收敛,务必在约束文件中明确定义主时钟:
create_clock -name clk_50m -period 20.000 [get_ports clk_50m]
否则静态时序分析可能无法正确识别时钟域,导致潜在的建立/保持时间违规。
一旦烧录成功,系统即可独立运行。接下来只需外接数码管显示模块或串口转USB芯片,就能实时观测测量结果。进一步地,可以添加BCD转换逻辑驱动多位数码管,或将原始Nx/Ns数据通过UART上传至上位机软件做后续处理。
回顾整个设计,其价值不仅体现在测量精度的提升,更在于提供了一种“全硬件闭环”的思维范式。不同于依赖处理器轮询或中断的传统架构,FPGA在这里扮演的是真正的自主测量单元角色。所有操作均由硬件逻辑自动完成,无需任何软件干预。这种设计极大提升了系统的实时性与可靠性,尤其适用于嵌入式仪表、自动化测试设备等对响应速度要求严苛的场合。
未来升级路径也很清晰。若需更高精度,可引入温补晶振(TCXO)或OCXO作为参考源;若追求更宽频带,可改用支持LVDS的Cyclone IV/V系列,配合差分输入提升高频响应能力。此外,加入自动量程切换功能后,还能实现类似商业仪器的智能测量体验。
总而言之,这个看似简单的频率计项目,实则是通往精密测量世界的一扇门。它教会我们的不仅是某个算法如何编码实现,更是如何利用FPGA的硬件优势,重新思考传统问题的解决方案。当越来越多的工程师开始习惯于“用逻辑代替代码”,电子系统的设计边界也将随之不断拓展。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
730

被折叠的 条评论
为什么被折叠?



