基于FPGA的频率计

1 简介

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

2 传统测量法

传统测量法有两种:周期测量法频率测量法

2.1 周期测量法
  1. 原理:先测出被测信号的周期 T T T,然后根据频率 f = 1 T f=\frac{1}{T} f=T1 求出被测信号的频率。
  2. 实现方法:数一个被测信号的周期内有多少个基准时钟,用基准时钟的总周期数来代替被测信号的周期 T T T
    在这里插入图片描述
    误差:被测信号和基准时钟存在一定的错位 △ t △t t。但假如被测信号的频率比较低,脉冲宽度很宽,当基准时钟频率很低,与被测时钟频率相差较大时,相对误差比较小。

周期测量法适合低频信号频率测量。

2.2 频率测量法
  1. 原理:在时间 t t t 内对被测信号的脉冲数 N N N 进行计数,然后求出单位时间的脉冲数即为被测信号的频率。
  2. 实现方法:在基准时钟的时间 t t t 内,数有多少个被测信号的脉冲。脉冲数除以 t t t 即为被测信号的频率。
    在这里插入图片描述
    误差:被测信号和基准时钟存在一定的错位。但假如被测信号的频率比较高,每个脉冲宽度很窄,当采样宽度足够宽时,误差就会比较小。

频率测量法适合高频信号频率测量。


3 等精度测量法

3.1 原理:

在这里插入图片描述
图中, g a t e gate gate 信号为门控信号,它的持续时间 G a t e Gate Gate_ t i m e time time 为整数倍的被测信号周期。 c l k clk clk _ f x f_x fx为被测信号(公式中表示被测信号频率), c l k clk clk _ f s f_s fs为基准时钟(公式中表示基准时钟信号频率)。

由:
被 测 信 号 周 期 个 数 × 被 测 信 号 周 期 长 度 被测信号周期个数 × 被测信号周期长度 ×
= 基 准 时 钟 周 期 个 数 × 基 准 时 钟 周 期 长 度 = 基准时钟周期个数 × 基准时钟周期长度 =×
= G a t e = Gate =Gate_ t i m e time time
可以推导出最终被测信号频率的计算公式

c l k clk clk _ f x fx fx = = = c l k − f s f s − c n t ⋅ \frac{clk-fs}{fs-cnt}· fscntclkfs f x fx fx _ c n t cnt cnt

误差:只会差一个基准时钟周期。只要 G a t e Gate Gate_ t i m e time time 足够大,相对误差就可以忽略。

3.2 代码实现

顶层设计模块:

module top_cymometer(
    //system clock
    input                  sys_clk  ,    // 时钟信号(系统时钟,50MHz)
    input                  sys_rst_n,    // 复位信号
    input                  clk_fx   ,    // 被测时钟
	output [7:0] led0,
	output [7:0] led1,
	output [7:0] led2,
	output [7:0] led3,
	output [63:0]data_fx
);
 
//parameter define
parameter    CLK_FS = 26'd50000000;      // 基准时钟频率值
 
 
gate//生成门控信号
(
        .clk_fs      (sys_clk  ),     // 基准时钟信号
        .rst_n       (sys_rst_n),  // 复位信号
 
        //cymometer interface
        .clk_fx      (clk_fx   ), //待测信号
		  .gate(gate ) , //门控信号
		  .gate_fs(gate_fs) // 同步到基准时钟的门控信号
		  );
 
pexg//边沿捕获
(
        .clk_fs      (sys_clk  ),     // 基准时钟信号
        .rst_n       (sys_rst_n),  // 复位信号
 
		  .gate(gate ) , //门控信号
		  .gate_fs(gate_fs), // 同步到基准时钟的门控信号
       .clk_fx      (clk_fx), //待测信号
		
		  .neg_gate_fs(neg_gate_fs),
		  .neg_gate_fx(neg_gate_fx)
		  );
		  
cnt  
    (
	 
	 //system clock
        .clk_fs      (sys_clk  ),     // 基准时钟信号
        .rst_n       (sys_rst_n),  // 复位信号
 
        //cymometer interface
        .clk_fx      (clk_fx   ), //待测信号
		  .gate(gate )  ,//门控信号
		  .gate_fs(gate_fs) ,// 同步到基准时钟的门控信号
		  .neg_gate_fs(neg_gate_fs),
		  .neg_gate_fx(neg_gate_fx),
        
		  .fs_cnt(fs_cnt)      ,          // 门控时间内基准时钟的计数值
		  .fx_cnt(fx_cnt)      ,           // 门控时间内被测时钟的计数值		
        .data_fx_temp(data_fx)  
);
endmodule

gate模块:产生周期是待测信号周期整数倍(代码中为5000倍)的门信号gate

module gate
(
        input                 clk_fs ,     // 基准时钟信号
        input                 rst_n   ,  // 复位信号
 
        //cymometer interface
        input                 clk_fx ,//待测信号
		  output		reg				gate , //门控信号
		  output    reg           gate_fs // 同步到基准时钟的门控信号
		  );
		  
localparam   GATE_TIME = 16'd5_000;        // 门控时间设置
reg    [15:0]   gate_cnt    ;           // 门控计数
 
reg             gate_fs_r   ;           // 用于同步gate信号的寄存器
 
//门控信号计数器,使用被测时钟计数
always @(posedge clk_fx or negedge rst_n) begin
    if(!rst_n)
        gate_cnt <= 16'd0; 
    else if(gate_cnt == GATE_TIME + 5'd20)
        gate_cnt <= 16'd0;
    else
        gate_cnt <= gate_cnt + 1'b1;
end
	  
 
//门控信号,拉高时间为GATE_TIME个实测时钟周期
always @(posedge clk_fx or negedge rst_n) begin
    if(!rst_n)
        gate <= 1'b0;
    else if(gate_cnt < 4'd10)
        gate <= 1'b0;     
    else if(gate_cnt < GATE_TIME + 4'd10)
        gate <= 1'b1;
    else if(gate_cnt <= GATE_TIME + 5'd20)
        gate <= 1'b0;
    else 
        gate <= 1'b0;
end
 
//将门控信号同步到基准时钟下
always @(posedge clk_fs or negedge rst_n) begin
    if(!rst_n) begin
        gate_fs_r <= 1'b0;
        gate_fs   <= 1'b0;
    end
    else begin
        gate_fs_r <= gate;
        gate_fs   <= gate_fs_r;
    end
end
endmodule

pexg模块:用来捕获gate信号和基准时钟信号的低电平

module pexg
(
        input                 clk_fs ,     // 基准时钟信号
        input                 rst_n   ,  // 复位信号
 
 
        input                 clk_fx , 
			input gate,
			input gate_fs     ,  
		  output    neg_gate_fs,
		  output    neg_gate_fx
		  );
reg                gate_fs_d0  ;           // 用于采集基准时钟下gate下降沿
reg                gate_fs_d1  ;           // 
reg                gate_fx_d0  ;           // 用于采集被测时钟下gate下降沿
reg                gate_fx_d1  ;           // 		  
//wire define
 
//边沿检测,捕获信号下降沿
assign neg_gate_fs = gate_fs_d1 & (~gate_fs_d0);
assign neg_gate_fx = gate_fx_d1 & (~gate_fx_d0);
 
 
//打拍采门控信号的下降沿(被测时钟)
always @(posedge clk_fx or negedge rst_n) begin
    if(!rst_n) begin
        gate_fx_d0 <= 1'b0;
        gate_fx_d1 <= 1'b0;
    end
    else begin
        gate_fx_d0 <= gate;
        gate_fx_d1 <= gate_fx_d0;
    end
end
 
//打拍采门控信号的下降沿(基准时钟)
always @(posedge clk_fs or negedge rst_n) begin
    if(!rst_n) begin
        gate_fs_d0 <= 1'b0;
        gate_fs_d1 <= 1'b0;
    end
    else begin
        gate_fs_d0 <= gate_fs;
        gate_fs_d1 <= gate_fs_d0;
    end
end
endmodule

cnt模块:用来计数,得到 f s fs fs _ c n t cnt cnt f x fx fx _ c n t cnt cnt

module cnt
   #(parameter    CLK_FS = 26'd50_000_000,// 基准时钟频率
	parameter  MAX       =  10'd64)  // 定义数据位宽        
    (   //system clock
        input                 clk_fs ,     // 时钟信号
        input                 rst_n   ,  // 复位信号
 
        //cymometer interface
        input                 clk_fx ,     // 待测信号
		input gate,       // 门控信号(与待测时钟同步)
		input gate_fs,    // 与基准时钟同步的门控信号
		input  neg_gate_fx,//
		input  neg_gate_fs,//
        
		output reg    [MAX-1:0]   fs_cnt      ,           //门控时间内基准时钟信号的个数 
		output reg    [MAX-1:0]   fx_cnt      ,          // 门控时间内待测信号的个数
		output reg  	[MAX-1:0]   data_fx_temp  // 待测信号的频率值
);
 
 
reg    [MAX-1:0]   fs_cnt_temp ;           // fs_cnt 计数
reg    [MAX-1:0]   fx_cnt_temp ;           // fx_cnt 计数
 
//门控时间内待测信号的计数,设置的为5000个,这里重新计数,只是用于检验信号是否正确
always @(posedge clk_fx or negedge rst_n) begin
    if(!rst_n) begin
        fx_cnt_temp <= 32'd0;
        fx_cnt <= 32'd0;
    end
    else if(gate)begin
      fx_cnt_temp <= fx_cnt_temp + 1'b1;
    end   
    else if(neg_gate_fx) begin
        
        fx_cnt_temp <= 32'd0;
        fx_cnt <= fx_cnt_temp;
        
    end
end
 
//门控时间内基准时钟的计数
always @(posedge clk_fs or negedge rst_n) begin
    if(!rst_n) begin
        fs_cnt_temp <= 32'd0;
        fs_cnt <= 32'd0;
    end
    else if(gate_fs)
        begin
        fs_cnt_temp <= fs_cnt_temp + 1'b1;
        end
    else if(neg_gate_fs) begin
        
        fs_cnt_temp <= 32'd0;
		fs_cnt <= fs_cnt_temp;
    end
end
//计算待测信号的频率值
always @(posedge clk_fs or negedge rst_n) begin
    if(!rst_n) begin
        data_fx_temp <= 64'd0;
    end
    else if(gate_fs == 1'b0)
			data_fx_temp <=CLK_FS*fx_cnt/fs_cnt;
end
 
endmodule

RTL视图:
在这里插入图片描述

3.4 实际测量

按照DE0手册分配管脚
(注意:Quartus 18.1中没有DE0设备Cyclone III EP3C16F484,旧版Quartus 9.1中有,于是重新下载9.1版本。。)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分配管脚:
在这里插入图片描述
分配完成后,重新运行程序,然后将程序加载在开发板上。
在这里插入图片描述

将电脑、开发板和信号发生器连接:
在这里插入图片描述
100Hz:
在这里插入图片描述
在这里插入图片描述

500Hz:
在这里插入图片描述

在这里插入图片描述

1kHz:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
signaltap结果:
在这里插入图片描述

10kHz:
在这里插入图片描述
在这里插入图片描述

1MHz:
在这里插入图片描述
在这里插入图片描述
出现了误差。

  • 28
    点赞
  • 190
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值