不枉初心,砥砺前行

皮皮祥的博客

欢迎留言,评论

导航

频率检测器

频率计

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

基准时钟:通常就是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下:

  1.  
    reg UT_CLK_D1;
  2.  
     
  3.  
    reg UT_CLK_D2;
  4.  
     
  5.  
    always @(posedge BASE_CLK or negedge RESET_n)
  6.  
     
  7.  
    begin
  8.  
     
  9.  
    if(~RESET_n) begin
  10.  
    UT_CLK_D1 <= 1'b0;
  11.  
    UT_CLK_D2 <= 1'b0;
  12.  
    end
  13.  
    else begin
  14.  
    UT_CLK_D1 <= UT_CLK;
  15.  
    UT_CLK_D2 <= UT_CLK_D1;
  16.  
    end
  17.  
    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实现:

  1.  
    module FRE_TST( 
  2.  
  3.  BASE_CLK, //base clock,default is 100MHz
  4.   
  5.  CLK_TST, //clock on test
  6.   
  7.  RESET, //async system reset
  8.   
  9.  TST_EN, //test enable signal
  10.   
  11.  DATA_OUT, //24bit test data out to uart
  12.  
  13.  DATA_OUT_EN //24bit test data out enable
  14.   
  15.  );
  16.   
  17.  input BASE_CLK;
  18.   
  19.  input CLK_TST;
  20.   
  21.  input RESET;
  22.   
  23.  input TST_EN;
  24.   
  25.  output [23:0] DATA_OUT;
  26.   
  27.  output DATA_OUT_EN;
  28.   
  29.  parameter FT_IDLE = 3'b001,
  30.   
  31.  FT_TST = 3'b010,
  32.   
  33.  FT_DOUT = 3'b100;
  34.   
  35.  reg [2:0] FTSTSM, FTSTSMNXT;
  36.   
  37.  wire PHASE_FT_IDLE = FTSTSM[0];
  38.   
  39.  wire PHASE_FT_TST = FTSTSM[1];
  40.   
  41.  wire PHASE_FT_DOUT = FTSTSM[2];
  42.   
  43.  wire PHASENXT_FT_DOUT = FTSTSMNXT[2];
  44.   
  45.  wire PHASENXT_FT_TST = FTSTSMNXT[1];
  46.   
  47.  reg CLK_TST_Q;
  48.   
  49.  reg CLK_TST_2Q;
  50.   
  51.  always @(posedge BASE_CLK or negedge RESET)
  52.   
  53.  begin
  54.   
  55.  if(~RESET)
  56.   
  57.  begin
  58.   
  59.  CLK_TST_Q <= 1'b0;
  60.   
  61.  CLK_TST_2Q <= 1'b0;
  62.   
  63.  end
  64.  
  65.  else
  66.   
  67.  begin
  68.   
  69.  CLK_TST_Q <= CLK_TST;
  70.   
  71.  CLK_TST_2Q <= CLK_TST_Q;
  72.   
  73.  end
  74.   
  75.  end
  76.   
  77.  reg [23:0] BASE_CLK_CNT;
  78.   
  79.  reg [11:0] CLK_TST_CNT;
  80.   
  81.  always @(posedge CLK_TST_2Q or negedge RESET) begin
  82.   
  83.  if(~RESET)
  84.  
  85.  CLK_TST_CNT <= 12'b0;
  86.   
  87.  else if(PHASE_FT_IDLE)
  88.   
  89.  CLK_TST_CNT <= 12'b0;
  90.   
  91.  else if(CLK_TST_CNT == 12'b0000_0100_0001)
  92.   
  93.  CLK_TST_CNT <= 12'b0;
  94.   
  95.  else if(PHASE_FT_TST )
  96.   
  97.  CLK_TST_CNT <= CLK_TST_CNT + 12'b1;
  98.   
  99.  else
  100.   
  101.  CLK_TST_CNT <= CLK_TST_CNT;
  102.   
  103.  end
  104.   
  105.  wire TST_END_TMP = (CLK_TST_CNT == 12'b0000_0100_0001) ; //count 65
  106.   
  107.  reg TST_END_TMP_Q;
  108.   
  109.  always @(posedge BASE_CLK or negedge RESET)
  110.   
  111.  begin
  112.   
  113.  if(~RESET)
  114.   
  115.  TST_END_TMP_Q <= 1'b0;
  116.   
  117.  else
  118.   
  119.  TST_END_TMP_Q <= TST_END_TMP;
  120.   
  121.  end
  122.  wire TST_END = ~TST_END_TMP_Q & TST_END_TMP;
  123.   
  124.  reg [2:0] data_out_cnt;
  125.   
  126.  always @(posedge BASE_CLK or negedge RESET) begin
  127.   
  128.  if(~RESET)
  129.   
  130.  data_out_cnt <= 3'b0;
  131.   
  132.  else if(PHASE_FT_IDLE)
  133.   
  134.  data_out_cnt <= 3'b0;
  135.   
  136.  else if(PHASE_FT_DOUT)
  137.   
  138.  data_out_cnt <= data_out_cnt + 3'b1;
  139.  
  140.  else
  141.   
  142.  data_out_cnt <= data_out_cnt;
  143.   
  144.  end
  145.   
  146.  wire DATA_OUT_END;
  147.  
  148.  assign DATA_OUT_END = (data_out_cnt == 3'd5);
  149.   
  150.  always @(posedge BASE_CLK or negedge RESET) begin
  151.  if(~RESET)
  152.   
  153.  BASE_CLK_CNT <= 24'h0;
  154.   
  155.  else if( PHASE_FT_TST )
  156.   
  157.  BASE_CLK_CNT <= BASE_CLK_CNT + 24'b1;
  158.   
  159.  else if(PHASE_FT_IDLE)
  160.   
  161.  BASE_CLK_CNT <= 24'h0;
  162.   
  163.  else
  164.   
  165.  BASE_CLK_CNT <= BASE_CLK_CNT;
  166.   
  167.  end
  168.  
  169.  always @(posedge BASE_CLK or negedge RESET) begin
  170.   
  171.  if(~RESET)
  172.   
  173.  FTSTSM <= FT_IDLE;
  174.   
  175.  else
  176.   
  177.  FTSTSM <= FTSTSMNXT;
  178.  
  179.  end
  180.   
  181.  always @(FTSTSM or TST_EN or CLK_TST_2Q or TST_END or DATA_OUT_END) begin
  182.   
  183.  FTSTSMNXT = FTSTSM;
  184.   
  185.  case (FTSTSM)
  186.   
  187.  FT_IDLE:
  188.   
  189.  if(TST_EN & CLK_TST_2Q)
  190.   
  191.  FTSTSMNXT = FT_TST;
  192.   
  193.  else
  194.   
  195.  FTSTSMNXT = FT_IDLE;
  196.  
  197.  FT_TST:
  198.  
  199.  if(TST_END)
  200.   
  201.  FTSTSMNXT = FT_DOUT;
  202.   
  203.  else
  204.   
  205.  FTSTSMNXT = FT_TST;
  206.   
  207.  FT_DOUT:
  208.   
  209.  if(DATA_OUT_END)
  210.  
  211.  FTSTSMNXT = FT_IDLE;
  212.  
  213.  else
  214.   
  215.  FTSTSMNXT = FT_DOUT;
  216.  
  217.  default:
  218.  
  219.  FTSTSMNXT = FT_IDLE;
  220.   
  221.  endcase
  222.  
  223.  end
  224.   
  225.  assign DATA_OUT_EN = PHASE_FT_DOUT;
  226.  
  227.  assign DATA_OUT = BASE_CLK_CNT;
  228.   
  229.  endmodule

下面是仿真的结果:

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

 

 

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

输入端口A:50M时钟

输入端口B:被测时钟

输入端口C:复位信号

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

 

  1.  module time_detect(
  2.  input clk_ref_in , //参考时钟50M
  3.  input clk_in , //待测时钟
  4.  input rst ,
  5.  output [15:0] number //
  6.  );
  7.   
  8.  //=======================================================================\
  9.  //**************************** 寄存器定义 *************************
  10.  
  11.  reg [7:0] clk_cnt = 0;
  12.  reg high_lev = 0;
  13.  reg high_lev_ff1;
  14.  reg high_lev_ff2;
  15.  reg high_lev_ff3;
  16.  reg [15:0] clk_ref_cnt = 0;
  17.  reg [15:0] clk_ref_cnt_lat = 0;
  18.  reg [15:0] clk_F = 0;
  19.  
    //============================ clk_in时钟域 ============================\
  20.  
    /******** clk_in:1~200M,先降频,经200倍分频后的时钟不大于1M ************/
  21.  
    //计数器
  22.  always @ (posedge clk_in or posedge rst)
  23.  begin
  24.  if(rst)
  25.  clk_cnt <= 0
  26.  else if(clk_cnt == 199)
  27.  clk_cnt <= 0;
  28.  else
  29.  clk_cnt <= clk_cnt + 1;
  30.  end 
  31.  
  32.  //分频输出
  33.  always @ (posedge clk_in or posedge rst)
  34.  begin
  35.  if(rst)
  36.  high_lev <= 0;
  37.  else if(clk_cnt == 99)
  38.  high_lev <= 1;
  39.  else if(clk_cnt == 199)
  40.  high_lev <= 0;
  41. else;
  42.  end
  43.  
  44.   
  45.  //========================= clk_ref_in参考时钟域 =========================\
  46.  /*对输入分频时钟作跨时钟域处理:大三拍*/
  47.  always @ (posedge clk_ref_in)
  48.  begin
  49.  high_lev_ff1 <= high_lev;
  50.  high_lev_ff2 <= high_lev_ff1;
  51.  high_lev_ff3 <= high_lev_ff2;
  52.  end
  53.   
  54.  /*ref时钟域下对分频后的时钟高电平进行计数*/
  55.  always @ (posedge clk_ref_in or posedge rst)
  56.  begin
  57.  if(rst)
  58.  clk_ref_cnt <= 0;
  59.  else if(high_lev_ff3)
  60.  clk_ref_cnt <= clk_ref_cnt + 1;
  61.  else
  62.  clk_ref_cnt <= 0;
  63.  end
  64.   
  65.  /*用一个锁存器clk_ref_cnt_lat锁存高电平计数最大值:即将分频时钟下降沿达到最大值*/
  66.  always @ (posedge clk_ref_in or posedge rst)
  67.  begin
  68.  if(rst)
  69.  clk_ref_cnt_lat <= 0;
  70.  else if({high_lev_ff3,high_lev_ff2} == 2'b10)
  71.  clk_ref_cnt_lat <= clk_ref_cnt;
  72.  else
  73.  end
  74.   
  75.  /*计算频率*/
  76.  always @ (posedge clk_ref_in or posedge rst)
  77.  begin
  78.  if(rst)
  79.  clk_F <= 0;
  80.  else
  81.  clk_F <= 5000/clk_ref_cnt_lat;
  82.  end
  83.  
  84.  assign number = clk_F;
  85.  emdmodule

 

posted on 2022-02-15 17:28  皮皮祥  阅读(514)  评论(0)    收藏  举报