简易频率计

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文章主要讲述了简易频率计的设计。


一、简易频率测量法

常用频率测量法:频率测量法、周期测量法、等精度测量法。
周期测量法是先测量出被测信号的周期 T,然后根据频率 f=1/T 求出被测信号的频率。
频率测量法是在时间 t 内对被测信号的脉冲数 N 进行计数,然后求出单位时间内的脉冲数,即为被测信号的频率。
但是上述两种方法都会产生±1 个基准时钟误差或被测时钟的误差,在实际应用中有一定的局限性。
根据测量原理,很容易发现周期测量法适合于低频信号测量,频率测量法适合于高频信号测量,但二者都不能兼顾高低频率同样精度的测量要求。

二、等精度测量法

等精度测量的一个最大特点是测量的实际门控时间不是一个固定值,而是一个与被测信号有关的值,刚好是被测信号的整数倍。在计数允许时间内,同时对基准时钟和被测信号进行计数,再通过数学公式推导得到被测信号的频率。由于门控信号是被测信号的整数倍,就消除了对被测信号产生的±l 周期误差,但是会产生对基准时钟±1 周期的误差。

1.原理时序图

在这里插入图片描述
首先将软件闸门同步到被测时钟信号,求出实际闸门期间被测时钟信号的打拍次数X;然后引入标准时钟信号fs,将软件闸门同步到标准时钟信号,求出在该范围下标准时钟信号的实际计数Y。最后,被测信号freq = X * Ffx / Y。

2.整体模块设计

在这里插入图片描述

3.被测时钟信号计算模块设计

在这里插入图片描述

4.被测时钟信号计算模块波形设计

在这里插入图片描述

5.被测时钟信号计算模块设计(freq_meter_calc)

module freq_meter_calc(
    input               sys_clk,
    input               sys_rst_n,
    input               clk_test,

    output reg [31:0]   freq
    );

assign  sys_rst = ~sys_rst_n;
parameter CNT_GATE_S_MAX = 27'd74_999_999;  // =1.5s/20ns
parameter GATE_RISE_MAX  = 27'd12_499_999;  // =0.25s/20ns
parameter CLK_STAND_FREQ = 27'd100_000_000;

reg     [26:0]      cnt_gate_s       ;
reg                 gate_s           ;
reg                 gate_a           ;
reg     [47:0]      cnt_clk_test     ;
reg                 gate_a_test_reg  ;
reg     [47:0]      cnt_clk_test_reg ;
reg     [47:0]      cnt_clk_stand    ;
reg                 gate_a_stand_reg ;
reg     [47:0]      cnt_clk_stand_reg;
reg                 calc_flag        ;
reg     [63:0]      freq_reg         ;
reg                 calc_flag_reg    ;

wire                gate_a_fall_t    ;
wire                clk_stand        ;
wire                gate_a_fall_s    ;

//软件闸门计数器
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        cnt_gate_s <= 27'd0;
    else if(cnt_gate_s == CNT_GATE_S_MAX)
        cnt_gate_s <= 27'd0;
    else
        cnt_gate_s <= cnt_gate_s + 1'b1;

//同步在系统时钟下的软件闸门
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        gate_s <= 1'b0;
    else if((cnt_gate_s > GATE_RISE_MAX ) 
            && (cnt_gate_s <= CNT_GATE_S_MAX - GATE_RISE_MAX ))
        gate_s <= 1'b1;
    else
        gate_s <= 1'b0;

//同步在测试时钟下的软件闸门
always@(posedge clk_test or posedge sys_rst)
    if(sys_rst)
        gate_a <= 1'b0;
    else 
        gate_a <= gate_s;

//在测试时钟下软件闸门进行的计数
always@(posedge clk_test or posedge sys_rst)
    if(sys_rst)
        cnt_clk_test <= 48'b0;
    else if(gate_a == 1'b0)
        cnt_clk_test <= 48'b0;
    else if(gate_a == 1'b1)
        cnt_clk_test <= cnt_clk_test + 1'b1;

//在测试时钟下的软件闸门时序打一拍
always@(posedge clk_test or posedge sys_rst)
    if(sys_rst)
        gate_a_test_reg <= 1'b0;
    else
        gate_a_test_reg <= gate_a;

//在系统时钟下的软件闸门下降沿
assign gate_a_fall_t = ((gate_a_test_reg == 1'b1) && (gate_a == 1'b0))
                        ? 1'b1 : 1'b0;

//在测试时钟下软件闸门进行的计数存入寄存器
always@(posedge clk_test or posedge sys_rst)
    if(sys_rst)
        cnt_clk_test_reg <= 48'b0;
    else if(gate_a_fall_t == 1'b1)
        cnt_clk_test_reg <= cnt_clk_test;

//(同步在测试时钟下的软件闸门)实际软件闸门在100MHz时钟下的计数
always@(posedge clk_stand or posedge sys_rst)
    if(sys_rst)
        cnt_clk_stand <= 48'b0;
    else if(gate_a == 1'b0)
        cnt_clk_stand <= 48'b0;
    else if(gate_a == 1'b1)
        cnt_clk_stand <= cnt_clk_stand + 1'b1;

//同步在100MHz时钟下的软件闸门
always@(posedge clk_stand or posedge sys_rst)
    if(sys_rst)
        gate_a_stand_reg <= 1'b0;
    else 
        gate_a_stand_reg <= gate_a;

//gate_a在100MHz时钟下的软件闸门下降沿
assign gate_a_fall_s = ((gate_a_stand_reg == 1'b1) && (gate_a == 1'b0))
                        ? 1'b1 : 1'b0;

//在100MHz时钟下软件闸门进行的计数存入寄存器
always@(posedge clk_stand or posedge sys_rst)
    if(sys_rst)
        cnt_clk_stand_reg <= 48'b0;
    else if(gate_a_fall_s == 1'b1)
        cnt_clk_stand_reg <= cnt_clk_stand;

//计算完成标志
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        calc_flag <= 1'b0;
    else if(cnt_gate_s == CNT_GATE_S_MAX)
        calc_flag <= 1'b1;
    else
        calc_flag <= 1'b0;

//计算freq_req fs=X*fs/Y 
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        freq_reg <= 64'd0;
    else if(calc_flag == 1'b1)
        freq_reg <= CLK_STAND_FREQ * cnt_clk_test_reg / cnt_clk_stand_reg;

//计算完成标志再打一拍
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        calc_flag_reg <= 1'd0;
    else 
        calc_flag_reg <= calc_flag;

//(LK_STAND_FREQ * cnt_clk_test_reg)位宽增加,所以设置中间变量freq_reg
// 做完除法位宽减小,将计算值的低32位赋予freq 
always@(posedge sys_clk or posedge sys_rst)
    if(sys_rst)
        freq <= 32'd0;
    else if(calc_flag_reg == 1'b1)
        freq <= freq_reg[31:0];

//连接IP生成100MHz时钟
clk_stand clk_stand_inst(
    .clk_out1   (clk_stand  ),    
    .reset      (sys_rst    ), 
    .clk_in1    (sys_clk    ));  

endmodule

这里用vivado生成100MHz时钟IP

6.整体设计

在这里插入图片描述


顶层模块设计需要生成一个测试时钟的IP,这里频率设置如下图所示

在这里插入图片描述
将计算的结果用晶码管进行显示(连接seg_dynamic模块)
顶层文件设计如下:

module freq_meter(
    input           sys_clk     ,
    input           sys_rst_n   ,
    input           clk_test    ,

    output          clk_out     ,
    output  [5:0]   sel         ,
    output  [7:0]   seg
   );

wire    [31:0]      freq        ;

freq_meter_calc freq_meter_calc_inst(
    .sys_clk    (sys_clk  ),
    .sys_rst_n  (sys_rst_n),
    .clk_test   (clk_test ),

    .freq       (freq) 
    );

clk_test clk_test_inst(
    .clk_out1   (clk_out    ),    
    .reset      (~sys_rst_n ), 
    .clk_in1    (sys_clk    )
    ); 
   
seg_dynamic seg_dynamic_inst(
    .sys_clk     (sys_clk   ),
    .sys_rst_n   (sys_rst_n ),
    .data        (freq/1000 ),
    .point       (6'b001_000),
    .sign        (1'b0      ),
    .seg_en      (1'b1      ),
    .sel         (sel       ),
    .seg         (seg       )   
    );

endmodule
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值