Verilog设计:频率检测模块

 

频率计

频率计又称频率计数器,是一种专门对被测信号频率进行测量的电子测量仪器。

基准时钟:通常就是FPGA板上的晶振,一般FPGA开发板会提供一个50Mhz 的晶振作为时钟源。

计数法

计数法:直接计数 单位时间内被测信号的脉冲数量;这种方法测量精度高、速度快,适合不同频率、不同精度的测频需要。

适合不同频率指的是一般的频率计在测高频信号和低频信号时的误差不一致,而采用计数法就能很好地适应不同频率的要求。

理想条件下:

 

 

 

 

 

频率检测模块也是在面试或者笔试的时候时常会遇到的一个问题,在这里对自己学习过程中遇到的一些方法做一些总结

频率测量的方法有很多:

这里介绍一种采用基准时钟(基准时钟)计数的方法来检测待测时钟。具体的方法是采用两个计数器BASE_CLK_CNT 和UT_CLK_CNT,基准时钟BASE_CLK驱动BASE_CLK_CNT,而待测时钟UT_CLK驱动UT_CLK_CNT。两个计数器同时开始计数,当UT_CLK_CNT计数至N时,停止计数;此时,根据基准时钟计数器BASE_CLK_CNT的值以及基准时钟BASE_CLK的频率可以计算出待测时钟UT_CLK的频率值。

上述方法必须要求两个计数器同时开始计数,这也是该方法设计的难点所在。由于基准时钟和待测时钟通常都是异步,若是不作处理就难以做到两个计数器同时开始计数,并且会引起系统的不稳定。

对待测时钟UT_CLK同步到基准时钟BASE_CLK下:

reg UT_CLK_D1;

reg UT_CLK_D2;

always @(posedge BASE_CLK or negedge RESET_n)

begin

     if(~RESET_n) begin
        UT_CLK_D1 <= 1'b0;
        UT_CLK_D2 <= 1'b0;
       end
else begin
        UT_CLK_D1 <= UT_CLK;
        UT_CLK_D2 <= UT_CLK_D1;
    end
end

不过这样做的结果就是引入测量误差,其绝对值小于4个BASE_CLK周期(测试周期开始的点0~2 BASE_CLK周期,测试周期结束的点0~2BASE_CLK周期,综合起来,应该小于4个BASE_CLK周期,一般情况下,应该是小于2个BASE_CLK周期)。

通常情况下,带来的误差是两条竖线之间的长度,比较差的情况下,误差会再加上一个BASE_CLK的周期。

UT_CLK_D2  的被测信号与BASE_CLK有相同的  上升沿  ,所以我们在UT_CLK_D2上升沿的时候再让两个计数器都开始计数。当UT_CLK_TCNT计数到   N  时,两个计数器   同时   停止计数。而对于  N   的选取,则要根据  测试精度   ,UT_CLK的  稳定  度,BASE_CLK的  频率   等各方面的要求来设定。不过建议数值最好选用2*X,这样便于后继的数据处理,在计算UT_CLK的时候,可以直接对BASE_CLK_CNT进行移位处理。

采用一个简单的状态机来实现这个测频电路。状态机有三个状态,FT_IDLE ,FT_TST和FT_DOUT。系统复位或者一次测量结束以后,状态机进入FT_IDLE状态,当TST_EN信号有效的时候,开始进行频率测量。当CLK_TST_CNT的计数值达到N(在该例子中,我们选择64)的时候,进入FT_DOUT状态。FT_DOUT状态持续的时间,由一个小的计数器的计数值来决定,我们可以通过改变这个计数器的值来让数据输出的时间符合后继处理电路的要求。

这个例程中直接给出BASE_CLK_CNT的值,UT_CLK的频率值还需要换算电路做进一步的处理,但这里不再给出。

下面是测频电路的verilog实现:

module FRE_TST(

BASE_CLK, //base clock,default is 100MHz

CLK_TST, //clock on test

RESET, //async system reset

TST_EN, //test enable signal

DATA_OUT, //24bit test data out to uart

DATA_OUT_EN //24bit test data out enable

);

input BASE_CLK;

input CLK_TST;

input RESET;

input TST_EN;

output [23:0] DATA_OUT;

output DATA_OUT_EN;

parameter FT_IDLE = 3'b001,

FT_TST = 3'b010,

FT_DOUT = 3'b100;

reg [2:0] FTSTSM, FTSTSMNXT;

wire PHASE_FT_IDLE = FTSTSM[0];

wire PHASE_FT_TST = FTSTSM[1];

wire PHASE_FT_DOUT = FTSTSM[2];

wire PHASENXT_FT_DOUT = FTSTSMNXT[2];

wire PHASENXT_FT_TST = FTSTSMNXT[1];

reg CLK_TST_Q;

reg CLK_TST_2Q;

always @(posedge BASE_CLK or negedge RESET)

begin

if(~RESET)

begin

CLK_TST_Q <= 1'b0;

CLK_TST_2Q <= 1'b0;

end

else

begin

CLK_TST_Q <= CLK_TST;

CLK_TST_2Q <= CLK_TST_Q;

end

end

reg [23:0] BASE_CLK_CNT;

reg [11:0] CLK_TST_CNT;

always @(posedge CLK_TST_2Q or negedge RESET) begin

if(~RESET)

CLK_TST_CNT <= 12'b0;

else if(PHASE_FT_IDLE)

CLK_TST_CNT <= 12'b0;

else if(CLK_TST_CNT == 12'b0000_0100_0001)

CLK_TST_CNT <= 12'b0;

else if(PHASE_FT_TST )

CLK_TST_CNT <= CLK_TST_CNT + 12'b1;

else

CLK_TST_CNT <= CLK_TST_CNT;

end

wire TST_END_TMP = (CLK_TST_CNT == 12'b0000_0100_0001) ; //count 65

reg TST_END_TMP_Q;

always @(posedge BASE_CLK or negedge RESET)

begin

if(~RESET)

TST_END_TMP_Q <= 1'b0;

else

TST_END_TMP_Q <= TST_END_TMP;

end

wire TST_END = ~TST_END_TMP_Q & TST_END_TMP;

reg [2:0] data_out_cnt;

always @(posedge BASE_CLK or negedge RESET) begin

if(~RESET)

data_out_cnt <= 3'b0;

else if(PHASE_FT_IDLE)

data_out_cnt <= 3'b0;

else if(PHASE_FT_DOUT)

data_out_cnt <= data_out_cnt + 3'b1;

else

data_out_cnt <= data_out_cnt;

end

wire DATA_OUT_END;

assign DATA_OUT_END = (data_out_cnt == 3'd5);

always @(posedge BASE_CLK or negedge RESET) begin

if(~RESET)

BASE_CLK_CNT <= 24'h0;

else if( PHASE_FT_TST )

BASE_CLK_CNT <= BASE_CLK_CNT + 24'b1;

else if(PHASE_FT_IDLE)

BASE_CLK_CNT <= 24'h0;

else

BASE_CLK_CNT <= BASE_CLK_CNT;

end

always @(posedge BASE_CLK or negedge RESET) begin

if(~RESET)

FTSTSM <= FT_IDLE;

else

FTSTSM <= FTSTSMNXT;

end

always @(FTSTSM or TST_EN or CLK_TST_2Q or TST_END or DATA_OUT_END) begin

FTSTSMNXT = FTSTSM;

case (FTSTSM)

FT_IDLE:

if(TST_EN & CLK_TST_2Q)

FTSTSMNXT = FT_TST;

else

FTSTSMNXT = FT_IDLE;

FT_TST:

if(TST_END)

FTSTSMNXT = FT_DOUT;

else

FTSTSMNXT = FT_TST;

FT_DOUT:

if(DATA_OUT_END)

FTSTSMNXT = FT_IDLE;

else

FTSTSMNXT = FT_DOUT;

default:

FTSTSMNXT = FT_IDLE;

endcase

end

assign DATA_OUT_EN = PHASE_FT_DOUT;

assign DATA_OUT = BASE_CLK_CNT;

endmodule

下面是仿真的结果:

BASE_CLK的周期是5ns,CLK_TST的周期是1000ns。

 

 

编写一个频率检测模块,输入端口:50M时钟,待检测时钟(范围1M-200M)

输入端口A:50M时钟

输入端口B:被测时钟

输入端口C:复位信号

输出端口D:频率值(单位Mhz)

 

module time_detect(
input clk_ref_in   ,   //参考时钟50M
input clk_in      ,   //待测时钟
input rst         ,
output [15:0] number //
);

//=======================================================================\
//****************************    寄存器定义      *************************

reg [7:0]  clk_cnt = 0;
reg      high_lev = 0;
reg      high_lev_ff1;
reg      high_lev_ff2;
reg      high_lev_ff3;
reg [15:0] clk_ref_cnt = 0;
reg [15:0] clk_ref_cnt_lat = 0;
reg [15:0] clk_F = 0

//============================  clk_in时钟域  ============================\
/********  clk_in:1~200M,先降频,经200倍分频后的时钟不大于1M    ************/ 
//计数器
always @ (posedge clk_in or posedge rst)
begin
 if(rst)
     clk_cnt <= 0
 else if(clk_cnt == 199)
    clk_cnt <= 0;
 else 
  clk_cnt <= clk_cnt + 1;
end

//分频输出
always @ (posedge clk_in or posedge rst)
begin 
 if(rst)
     high_lev <= 0;
 else if(clk_cnt == 99)
     high_lev <= 1;
 else if(clk_cnt == 199)
  high_lev <= 0;
 else;
end


//=========================   clk_ref_in参考时钟域 =========================\
/*对输入分频时钟作跨时钟域处理:大三拍*/
always @ (posedge clk_ref_in) 
begin
 high_lev_ff1 <= high_lev;
 high_lev_ff2 <= high_lev_ff1;
 high_lev_ff3 <= high_lev_ff2;
end

/*ref时钟域下对分频后的时钟高电平进行计数*/
always @ (posedge clk_ref_in or posedge rst)
begin
 if(rst)
         clk_ref_cnt <= 0;
 else if(high_lev_ff3)
         clk_ref_cnt <= clk_ref_cnt + 1;
 else
         clk_ref_cnt <= 0;
end

/*用一个锁存器clk_ref_cnt_lat锁存高电平计数最大值:即将分频时钟下降沿达到最大值*/
always @ (posedge clk_ref_in or posedge rst)
begin 
 if(rst)
      clk_ref_cnt_lat <= 0;
 else if({high_lev_ff3,high_lev_ff2} == 2'b10)
      clk_ref_cnt_lat <= clk_ref_cnt;
 else 
end

/*计算频率*/
always @ (posedge clk_ref_in or posedge rst)
begin
 if(rst)
       clk_F <= 0;
 else
       clk_F <= 5000/clk_ref_cnt_lat;
end  

assign number = clk_F;
emdmodule

 

  • 15
    点赞
  • 99
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在STM32微控制器中,我们可以使用ADC模块来进行频率检测。通过配置ADC的采样时间和分辨率,我们可以准确地捕捉到输入信号的变化,并对其进行分析和处理。 首先,我们需要使用STM32内部时钟来设置ADC的采样频率。通过设定ADC的预分频因子和采样周期,我们可以调整ADC的采样频率,使其适合输入信号的频率。通常情况下,采样频率应该是输入信号频率的几倍,以确保我们能够准确地捕捉到信号的快速变化。 其次,我们需要选择适当的采样时间和分辨率来进行频率检测。采样时间是指ADC每次进行采样的持续时间,它决定了我们能够捕捉到的信号的最高频率。分辨率则决定了我们能够对输入信号进行多精度的测量,从而提高频率检测的准确性。 在进行频率检测时,我们可以通过定时器来计算ADC采样周期的持续时间,从而得到输入信号的周期。通过计算输入信号的周期,我们就可以得到其频率。 为了提高频率检测的准确性,我们还可以使用中断来触发ADC的采样和转换。当输入信号发生变化或达到我们设置的阈值时,中断将被触发,ADC将进行相应的采样和转换。通过处理中断函数,并根据采样结果进行频率计算,我们可以实时地检测输入信号的频率。 总结来说,STN32微控制器的ADC模块可以用于频率检测。通过配置ADC的采样频率、采样时间和分辨率,以及使用定时器和中断来进行计算和处理,我们可以准确地检测输入信号的频率
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值