1.为什么用周期测频法:
使用直接测频法测量低频信号时,不仅会带来较大的相对误差,而且为兼顾测量需求(闸门宽度要大于待测信号周期)与数据更新周期,需根据待测低频信号的频率范围人为设定相应的闸门宽度,导致程序不够灵活。因此,引入周期测频法对低频信号进行测量。
2.周期测频法思路:
周期测频法仍采用闸门计数的基本方法,但此时是以待测信号为闸门,控制基准时钟进行计数,来得到待测信号一个周期所持续的时间。
网络上的一些资料在使用周期测频法时,仅在待测信号的高电平部分进行计数,再将计数值扩大二倍作为待测信号周期,这种方法仅在待测信号占空比为50%时(如图1)才能得到正确结果。在图2这种待测信号占空比不定的情况下,无法确定高低电平时间之比,需要对待测信号的完整周期进行测量。
在此处,采用双计数器的形式,分别在待测信号高、低电平时进行计数,并在待测信号上升沿处将两个计数结果求和后作为结果输出。
图1.待测信号占空比为50%时
图2.待测信号占空比不为50%时
3.误差分析:
出于和直接测频法相同的原因,被计数量仍会出现一定的绝对误差。为减小相对误差,需要增大计数结果,这里可以采用提高基准时钟频率的方式。
4.具体实现:
源代码
module f_measurement (clk,rst_p,signal_in,T);
input clk; /*板载时钟50M*/
input rst_p; /*高电平复位信号*/
input signal_in; /*待测信号,1mHz——1Hz*/
outputreg[35:0] T; /*T最大值=待测信号最长周期/基准时钟周期*/
reg[35:0] T_high,T_low,T_high_reg;
/*测量待测信号高电平宽度*/
always@(posedge clk orposedge rst_p)
begin
if(rst_p)
T_high<=36'd0;<>
else
begin
if(signal_in)
T_high<=t_high+36'd1;<>
else
T_high<=36'd0;<>
end
end
/*寄存高电平宽度数据(在待测信号变为低电平后,高电平计数器会清零,需要将计数结果进行存储,等待待测信号出现上升沿时使用)*/
always@(negedge signal_in orposedge rst_p)
begin
if(rst_p)
T_high_reg<=36'd0;<>
else
T_high_reg<=t_high;<>
end
/*测量待测信号低电平宽度*/
always@(posedge clk orposedge rst_p)
begin
if(rst_p)
T_low<=36'd0;<>
else
begin
if(!signal_in)
T_low<=t_low+36'd1;<>
else
T_low<=36'd0;<>
end
end
/*待测信号上升沿更新测量结果*/
always@(posedge signal_in orposedge rst_p)
begin
if(rst_p)
T<=36'd0;<>
else
T<=t_high_reg+t_low;<>
end
endmodule
仿真激励
`timescale1 ns/1 ps
module f_measurement_vlg_tst();
reg clk;
reg rst_p;
reg signal_in; wire[35:0] T;
f_measurement i1 (
.T(T),
.clk(clk),
.rst_p(rst_p),
.signal_in(signal_in));
initial begin
rst_p=1;
clk=0;
#20.1
rst_p=0;
end
initial begin
/*0.25Hz,占空比75%的待测信号*/
signal_in=1; #3000000000
signal_in=0;
#1000000000
signal_in=1;
end
always begin #10
clk=!clk;
end
endmodule
可见,此时测量结果为199999999个基准时钟周期,即待测信号周期=199999999*20ns=3.99999998s,与其真实频率0.25Hz基本保持一致,达到了测量效果。(浮点乘除运算不是FPGA所擅长的,此处仅对信号周期进行测量,将结果传递至单片机,通过除法计算频率