频率测量在电子设计和测量领域中经常用到,因此对频率测量方法的研究在实际工程应用中具有重要意义。常用的频率测量方法有两种:频率测量法 和周期测量法。频率测量法是在时间t内对被测信号的脉冲数N进行计数,然后求出单位时间内的脉冲数,即为被测信号的频率。周期测量法是先测量出被测信号的周期T,然后根据频率f=1/T求出被测信号的频率。但是上述两种方法都会产生±1个被测脉冲的误差,在实际应用中有一定的局限性。根据测量原理,很容易发现频率测量法适合于高频信号测量,周期测量法适合于低频信号测量,但二者都不能兼顾高低频率同样精度的测量要求。
1 等精度测量原理
等精度测量的一个最大特点是测量的实际门控时间不是一个固定值,而是一个与被测信号有关的值,刚好是被测信号的整数倍。在计数允许时间内,同时对标准信号和被测信号进行计数,再通过数学公式推导得到被测信号的频率。由于门控信号是被测信号的整数倍,就消除了对被测信号产生的±l周期误差,但是会产生对标准信号±1周期的误差。等精度测量原理如图1所示。
从以上叙述的等精度的测量原理可以很容易得出如下结论:首先,被测信号频率fx的相对误差与被测信号的频率无关;其次,增大测量时间段“软件闸门”或提高“标频”f0,可以减小相对误差,提高测量精度;最后,由于一般提供标准频率f0的石英晶振稳定性很高,所以标准信号的相对误差很小,可忽略。假设标准信号的频率为100 MHz,只要实际闸门时间大于或等于1s,就可使测量的最大相对误差小于或等于10-8,即精度达到1/100 MHz。
2等精度测量实现
等精度测频的原理图如图2所示。图中,预置软件闸门信号GATE是由FPGA的定时模块产生,GATE的时间宽度对测频精度的影响较少,故可以在较大的范围内选择。这里选择预置闸门信号的长度为1s。图中的CNT1和CNT2是2个可控的32位高速计数器,CNT1_ENA和CNT2_ENA分别是其计数使能端,基准频率信号f0从CNT1_CLK输入,待测信号fx从CNT2的时钟输入端CONT2_CLK输入,并将fx接到D触发器的clk端。测量时,由FPGA的定时模块产生预置的GATE信号,在GATE为高电平,并且fx的上升沿时,启动2个计数器,分别对被测信号和基准信号计数,关闭计数闸门必须满足,GATE为低电平,且在fx的上升沿。若在一次实际闸门时间Tx中,计数器对被测信号的计数值为Nx,对标准信号的计数值为N0,而标准信号的频率为f0,则被测信号的频率为fx,则fx=(N0/Ns)f0。图2中的所有功能都在FPGA端实现。
module cymometer
(
input clk_fs ,
input rst_n ,
input clk_fx ,// 待测信息
output reg [63:0] data_fx //输出频率
);
localparam CLK_FS = 26'd50_000_000;
localparam GATE_TIME = 16'd100; //门控时间,越大误差越小,但测量时间也会变长
//reg define
reg gate ;
reg gate_fs ;
reg gate_fs_r ;
reg gate_fs_d0 ;
reg gate_fs_d1 ;
reg gate_fx_d0 ;
reg gate_fx_d1 ;
reg [15:0] gate_cnt ;
reg [31:0] fs_cnt ;
reg [31:0] fs_cnt_temp ;
reg [31:0] fx_cnt ;
reg [31:0] fx_cnt_temp ;
//wire define
wire neg_gate_fs;
wire neg_gate_fx;
assign neg_gate_fs = gate_fs_d1 & (~gate_fs_d0);
assign neg_gate_fx = gate_fx_d1 & (~gate_fx_d0);
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n)
gate_cnt <= 16'd0;
else if(gate_cnt == GATE_TIME + 5)
gate_cnt <= 16'd0;
else
gate_cnt <= gate_cnt + 1'b1;
end
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n)
gate <= 1'b0;
else if(gate_cnt < 5)
gate <= 1'b0;
else if(gate_cnt < GATE_TIME + 5)
gate <= 1'b1;
else if(gate_cnt <= GATE_TIME + 5)
gate <= 1'b0;
else
gate <= 1'b0;
end
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
gate_fs_r <= 1'b0;
gate_fs <= 1'b0;
end
else begin
gate_fs_r <= gate;
gate_fs <= gate_fs_r;
end
end
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n) begin
gate_fx_d0 <= 1'b0;
gate_fx_d1 <= 1'b0;
end
else begin
gate_fx_d0 <= gate;
gate_fx_d1 <= gate_fx_d0;
end
end
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
gate_fs_d0 <= 1'b0;
gate_fs_d1 <= 1'b0;
end
else begin
gate_fs_d0 <= gate_fs;
gate_fs_d1 <= gate_fs_d0;
end
end
always @(posedge clk_fx or negedge rst_n) begin
if(!rst_n) begin
fx_cnt_temp <= 0;
fx_cnt <= 0;
end
else if(gate)
fx_cnt_temp <= fx_cnt_temp + 1'b1;
else if(neg_gate_fx) begin
fx_cnt_temp <= 0;
fx_cnt <= fx_cnt_temp;
end
end
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
fs_cnt_temp <= 0;
fs_cnt <= 0;
end
else if(gate_fs)
fs_cnt_temp <= fs_cnt_temp + 1'b1;
else if(neg_gate_fs) begin
fs_cnt_temp <= 0;
fs_cnt <= fs_cnt_temp;
end
end
always @(posedge clk_fs or negedge rst_n) begin
if(!rst_n) begin
data_fx <= 0;
end
else if(gate_fs == 1'b0)
data_fx <= (CLK_FS * fx_cnt ) / fs_cnt;
end
endmodule