用FPGA设计频率计

介绍:频率测量在电子设计领域和测量领域经常被使用,相较于传统的模拟频率计,数字频率计具有更高的测量精度、更广的测量范围以及更强的抗干扰能力。

原理:运用等精度测量法进行测量,被测时钟信号的时钟频率fx的相对误差与被测时钟信号无关;增大“软件闸门”的有效范围或者提高“标准时钟信号”的时钟频率fs,可以减小误差,提高测量精度。

运用:对实际闸门下被测时钟信号和标准时钟信号的时钟周期进行计数。

实际闸门下被测时钟信号周期数为X,设被测信号时钟周期为T_{fx},它的时钟频率fx = \frac{1}{T_{fx}},由此可得等式:X * T_{fx} = \frac{X }{f_x} = T_x(实际闸门)。

实际闸门下标准时钟信号周期数为Y,设被测信号时钟周期为T_{fs},它的时钟频率f_s = \frac{1}{T_{fs}},由此可得等式:Y * T_{fs} = \frac{Y}{f_s }= T_x(实际闸门)。

其次,将两等式结合得到只包含各自时钟周期计数和时钟频率的等式:\frac{X}{fx} = \frac{Y}{fs} = T_x(实际闸门),等式变换,得到被测时钟信号时钟频率计算公式:f_x = \frac{X *fs}{Y}

示意图如下

实验目的:用数码管显示被测信号的频率与占空比

(本实验频率测量参考了野火的简易频率计的设计与验证)

链接:1. 简易频率计的设计与验证 — [野火]FPGA Verilog开发实战指南——基于Altera EP4CE10 征途Mini开发板 文档 (embedfire.com)

模块框图:

signal_meas模块代码

module  signal_meas
(
    input   wire clk_50Mhz      ,   //输入工作时钟,频率50MHz 
    input   wire clk100MHz      ,   //输入标准时钟100MHz  
    input   wire rst_n          ,   //输入复位信号,低电平有效
    input   wire clk_in         ,   //输入被测信号
         
    output  wire [26:0]data_F   ,   //输出被测信号频率
    output  wire [26:0]data_T       //输出被测信号占空比
);

parameter   NUM_1S_MAX  = 26'd49_999_999    ,
            NUM_250MS_MAX =26'd12_499_999   ;            
parameter  BZ_clk_F = 26'd50_000_000        ;

parameter ZKB_T = 16'd100_00;


reg [27:0] num;

reg gate_r;
reg gate_s;

reg gate_s_bz;
reg gate_s_bz1;

reg gate_s_empty;

wire flag_gate_s;
wire flag_gate_s_bz;

reg [26:0] num_in_x;
reg [26:0] sum_in_x;

reg [26:0] num_50mhz_y;
reg [26:0] sum_50mhz_y;

reg clk_TB;
reg clk_TS;

wire flag_gate_s_empty;

reg [47:0] num_empty_x;
reg [47:0] sum_empty_x;

reg [47:0] num_empty_y;
reg [47:0] sum_empty_y;

reg [64:0]  data_F_s  ;
reg [164:0] data_T_s  ;

assign data_F = data_F_s;
assign data_T = data_T_s;


//产生测量的数据刷新的内部触发信号
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        num <= 'd0;
    end
    else if(num == NUM_1S_MAX + NUM_250MS_MAX*2)begin
        num <= 'd0;
    end
    else begin
        num <= num + 'd1;
    end
end

// 产生1S的软件闸门的信号
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        gate_r <= 1'd0;
    end
    else if(num <= NUM_1S_MAX)begin
        gate_r <= 1'd1;
    end   
    else    begin
        gate_r <= 1'd0;
    end    

end

//对软件闸门同步到实际闸门
always@( posedge clk_in or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        gate_s <= 1'd0;
    end
    else    begin
        gate_s <= gate_r;
    end
end

//计算实际闸门的clk_in电平周期个数
always@( posedge clk_in or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        num_in_x <= 'd0;
    end
    else if(gate_s ==1'b1)begin
        num_in_x <= num_in_x + 'd1;
    end
    else    begin
        num_in_x <= 'd0;
    end
end 

//产生gate_s闸门的clk_50Mhz的1个时钟周期的信号
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        gate_s_bz <= 1'b0;
    end
    else    begin
        gate_s_bz <= gate_s;
    end
end

//产生1S实际闸门的clk_in电平周期个数提取标志信号
assign  flag_gate_s = (~gate_s) & gate_s_bz;
  
//提取实际闸门的clk_in电平周期个数
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        sum_in_x <= 'd0;
    end
    else if(flag_gate_s)   begin
        sum_in_x <= num_in_x;
    end  
end

//计算实际闸门的clk_50Mhz电平周期个数
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        num_50mhz_y <= 'd0;
    end
    else if(gate_s_bz ==1'b1)begin
        num_50mhz_y <= num_50mhz_y + 26'd1;
    end
    else    begin
        num_50mhz_y <= 'd0;
    end
end 
 
//产生gate_s_bz的clk_50Mhz的延时1个时钟周期的信号
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        gate_s_bz1 <= 1'd0;
    end
    else    begin
        gate_s_bz1 <= gate_s_bz;
    end
end 
 
//产生1S实际闸门的clk_50Mhz电平周期个数提取标志信号
assign  flag_gate_s_bz = (~gate_s_bz) & gate_s_bz1;
  
//提取实际闸门的clk_50Mhz电平周期个数
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        sum_50mhz_y <= 'd0;
    end
    else if(flag_gate_s_bz)   begin
        sum_50mhz_y <= num_50mhz_y;
    end  
end

//进行被测量信号的频率测量
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        data_F_s <= 'd0;
    end
    else if((num > NUM_1S_MAX )&&(num < NUM_1S_MAX + NUM_250MS_MAX*2))   begin
        data_F_s <= (BZ_clk_F*sum_in_x/sum_50mhz_y);
    end  
end

// measurement empty proportion ,测量占空比
// 将clk_in同步在clk100MHz的时钟下,提高测量精度
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        clk_TB <= 1'd0;
    end
    else  begin
        clk_TB <= clk_in;
    end
end 

//对clk_TB再次同步,防止亚稳态现象
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        clk_TS <= 1'd0;
    end
    else  begin
        clk_TS <= clk_TB;
    end
end 

//计算在闸门内的时间
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        num_empty_x <= 'd0;
    end
    else if(gate_s ==1'b1)begin
        num_empty_x <= num_empty_x + 'd1;
    end
    else if(gate_s == 1'b0)   begin
        num_empty_x <= 'd0;
    end
end 

//计算在闸门内clk_TS高电平上的时间
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        num_empty_y <= 'd0;
    end
    else if((clk_TS ==1'b1)&&(gate_s == 1'b1))begin
        num_empty_y <= num_empty_y + 'd1;
    end
    else if(gate_s == 1'b0)   begin
        num_empty_y <= 'd0;
    end
end 

//new
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        gate_s_empty <= 1'b0;
    end
    else    begin
        gate_s_empty <= gate_s;
    end
end

//产生1S实际闸门的clk_TS电平周期个数提取标志信号
assign  flag_gate_s_empty = (~gate_s) & gate_s_empty;

//提取计算在闸门内的时间
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        sum_empty_x <= 'd0;
    end
    else if(flag_gate_s_empty)   begin
        sum_empty_x <= num_empty_x;
    end  
end

//提取在闸门内clk_TS高电平上的时间
always@( posedge clk100MHz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        sum_empty_y <= 'd0;
    end
    else if(flag_gate_s_empty)   begin
        sum_empty_y <= num_empty_y;
    end  
end

//计算占空比
always@( posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0)begin
        data_T_s <= 'd0;
    end
    else if((num > NUM_1S_MAX )&&(num < NUM_1S_MAX + NUM_250MS_MAX*2))   begin
        data_T_s <= (ZKB_T*sum_empty_y/sum_empty_x); 
    end  
end

endmodule

bcd_8421模块代码,见我的这一篇文章 :用FPGA设计8421BCD编码-CSDN博客

smg模块代码,见我的这一篇文章 :用FPGA设计数码管-CSDN博客

key_filter模块代码,见我的这一篇文章 :用FPGA设计软件按键消抖-CSDN博客

signal_meas_top模块代码

`timescale  1ns/1ns
module  signal_meas_top
(
    input   wire            clk_50Mhz   ,   //输入工作时钟,频率50MHz
    input   wire            sys_rst_n   ,   //输入复位信号,低电平有效
    input   wire            key_in      ,   //按键输入信号
    input   wire            clk_in      ,   //输入被测信号

    output  wire            clk20MHz    ,   //输出测试信号
    output  reg      [7:0]  smg_reg     ,   //输出单个数码管显示的数据信号
    output  reg      [7:0]  smg_rel         //输出数码管选择信号
);

wire            clk100MHz     ;
wire            locked        ;   
wire            rst_n         ; 
wire            key_flag      ;
     
wire    [26:0]  data_F        ; 
wire    [26:0]  data_T        ;  

wire    [31:0]  bcd_data_F    ; 
wire    [31:0]  bcd_data_T    ; 
reg     [31:0]  bcd_data      ; 

assign  rst_n = (sys_rst_n & locked);

reg led_flag_h ;
//把信号key_flag处理为电平反转信号
always@(  posedge  clk_50Mhz or negedge rst_n) begin 
    if (rst_n==1'b0) begin
        led_flag_h <= 1'b0;
    end
    else if (key_flag == 1'b1) begin
        led_flag_h <= ~led_flag_h;
    end
    else begin
        led_flag_h <= led_flag_h;
    end
end 

always @(posedge clk_50Mhz or negedge rst_n) begin
    if(rst_n == 1'b0) begin
        bcd_data <= 32'd0;
    end
    else if(led_flag_h == 1'b1) begin
        bcd_data <= bcd_data_F;
    end
    else begin
        bcd_data <= bcd_data_T;
    end
end

clk_wiz_0 clk_wiz_0_inst
(
// Clock out ports  
.clk_out1(clk100MHz),
.clk_out2(clk20MHz),
// Status and control signals               
.reset(~sys_rst_n), 

.locked(locked),
// Clock in ports
.clk_in1(clk_50Mhz)
);

signal_meas signal_meas_inst
(
    .clk_50Mhz     (clk_50Mhz )    ,
    .clk100MHz     (clk100MHz )    ,   
    .rst_n         (rst_n     )    ,
    .clk_in        (clk_in    )    ,

    .data_F        (data_F    )    ,
    .data_T        (data_T    )   
);    

bcd_8421 bcd_8421_inst1
(
     .clk_50Mhz (clk_50Mhz )   ,
     .rst_n     (rst_n     )   ,
     .data      (data_T    )   ,

     .bcd_data  (bcd_data_T)
);

bcd_8421 bcd_8421_inst2
(
     .clk_50Mhz (clk_50Mhz )   ,
     .rst_n     (rst_n     )   ,
     .data      (data_F    )   ,

     .bcd_data  (bcd_data_F)
);

key_filter key_filter_inst
(
    .clk_50Mhz  (clk_50Mhz)     ,   //系统时钟50Mhz
    .rst_n      (rst_n    )     ,   //全局复位
    .key_in     (key_in   )     ,   //按键输入信号
 
    .key_flag   (key_flag )         //key_flag为1时表示消抖后检测到按键被按下
                                    //key_flag为0时表示没有检测到按键被按下
);

smg smg_inst
(

    .clk_50Mhz  (clk_50Mhz)     , //系统时钟50Mhz
    .rst_n      (rst_n    )     , //全局复位
    .data_in    (bcd_data )     , //输入信号
    .point      (8'd0     )     , //小数点显示,高电平有效

    .smg_reg    (smg_reg  )     , //输出单个数码管显示的数据信号
    .smg_rel    (smg_rel  )       //输出数码管选择信号

);



endmodule

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值