数字频率计 FPGA 实现中的测频方法比较
在嵌入式和测量系统开发中,数字频率计已广泛应用于通信、工业控制及传感器接口等领域。FPGA 凭借并行处理能力,成为实现高精度、实时频率测量的理想平台。面对直接法、周期法、多周期同步、等精度等多种方案,需根据测量精度、动态范围、资源开销及实现复杂度进行选型。
一、直接测频法
工作原理
使用稳定的参考时钟生成精确时间窗口(门控),统计窗口内被测信号的上升沿数量。 公式:$$ f_x = \frac{N}{T_{gate}} $$ 其中 $ N $ 为计数值,$ T_{gate} $ 为门控时间。
缺陷与适用场景
致命缺陷是±1 计数误差。由于门控信号与被测信号不同步,可能错过第一个或多算最后一个脉冲。低频时相对误差大,高频时较准。
- 适合:被测频率较高(>10 kHz)、响应速度要求高、FPGA 资源紧张。
Verilog 实现精要
module direct_freq_meter (
input clk_ref, // 50MHz 参考时钟
input reset,
input signal_in, // 待测信号
output reg valid_out,
output reg [31:0] freq_out
);
reg [31:0] count;
reg gate_en;
reg signal_in_d1;
wire pos_edge = signal_in & ~signal_in_d1; // 边沿检测防重复计数
always @(posedge clk_ref or posedge reset) begin
if (reset) signal_in_d1 <= 0;
else signal_in_d1 <= signal_in;
end
localparam GATE_COUNT = 50_000_000; // 50M * 1s
reg [31:0] timer;
always @(posedge clk_ref or posedge reset) begin
if (reset) {timer, gate_en} <= 0;
else if (timer < GATE_COUNT - 1) begin
timer <= timer + 1;
gate_en <= 1;
end
else begin
gate_en <= 0;
timer <= 0;
end
end
always @(posedge clk_ref or posedge reset) begin
if (reset) count <= 0;
else if (gate_en && pos_edge) count <= count + 1;
else if (!gate_en) count <= 0;
end
reg gate_prev;
always @(posedge clk_ref) begin
gate_prev <= gate_en;
if (!gate_en && gate_prev) begin
freq_out <= count;
valid_out <= 1;
end
else valid_out <= 0;
end
endmodule
注意点:必须做边沿检测;输出应在门控结束后锁存;分辨率可通过延长门控时间提升但牺牲响应速度。
二、周期测频法
核心逻辑
不数脉冲个数,改测周期长度。将高速参考时钟当作'时间尺子',测量被测信号一个完整周期占了多少个参考时钟脉冲。 公式:$$ f_x = \frac{f_{ref}}{N} $$ 其中 $ N $ 是参考时钟在单个周期内的计数值。
优势与代价
- 优势:越低频越精准。
- 代价:必须等待至少一个完整周期,响应慢;对信号稳定性要求高。
实现要点
reg [31:0] counter;
reg capture_done;
reg edge_start;
always @(posedge clk_ref or posedge reset) begin
if (reset) begin
counter <= 0;
edge_start <= 0;
capture_done <= 0;
end
else begin
if (pos_edge(signal_in)) begin
if (!edge_start) begin
edge_start <= 1;
counter <= 0;
end
else begin
capture_done <= 1;
end
end
if (edge_start && !capture_done) counter <= counter + 1;
end
end
// 计算频率(注意除法精度)
always @(posedge clk_ref) begin
if (capture_done && counter != 0) begin
freq_out <= 100_000_000 / counter; // f_ref / N
valid_out <= 1;
capture_done <= 0;
edge_start <= 0;
end
else valid_out <= 0;
end

