FPGA简易频率计

开发板:野火征途EP4CE10F17C8

参考教程:野火简易频率计课程

一、测量频率常用的三种方法:

1.频率测量法:在时间T内对被测时钟的周期N进行计数,然后求出单位时间内的时钟周期数,即为被测时钟信号的时钟频率。(适合测量高频信号)

2.周期测量法:先测量出被测时钟信号的时钟周期T,然后根据频率f=1/T求出被测时钟信号的频率。(适合测量低频信号)

3.等精度测量法:软件闸门(有±1个误差)、被测时钟信号、实际闸门(被测时钟信号周期是实际闸门的整数倍)、标准时钟信号(参照实际闸门有±1个误差)

二、减小误差的方法:

a.提高软件闸门的时间,那么实际闸门的时间也会提高、标准信号周期数增加,误差减小。

b.提高标准时钟周期信号的频率,实际实际闸门不变,标准信号周期数增加,误差减小。

三、等精度频率测量法计算方法:

实际闸门(Tx)、被测时钟信号周期数X、标准信号时钟周期数Y、标准信号频率Ffs、被测时钟信号频率Ffx。

Tx=Tfx*x=1/Ffx*x

Tx=Tfs*Y=1/Ffs*Y

X Y Ffs已知,联立解Ffx。

课程采用quartus软件编写verilog代码,采用PLL_IP核生成clk_test被测信号,在验证ip核产生被测信号后,将clk_test引脚引出与信号发生器连接,将信号发生器给出信号的频率显示在开发板上的数码管中。

四、动态数码管部分

将信号的频率显示在开发板的6位8段数码管中,数码管为共阳极。

动态显示部分共分为:二进制码转BCD码子模块,HC595驱动控制子模块,动态显示模块。

五、代码

(1) 频率计算模块:

module  freq_meter_calc
(
    input   wire            sys_clk     ,   
    input   wire            sys_rst_n   ,   
    input   wire            clk_test    ,   //待检测时钟
    output  reg     [33:0]  freq            //待检测时钟频率
);
parameter   CNT_GATE_S_MAX  =   28'd37_499_999  ,   //软件闸门计数器计数最大值  1.5s
            CNT_RISE_MAX    =   28'd6_250_000   ;   //软件闸门拉高计数值,   1.25s
parameter   CLK_STAND_FREQ  =   28'd100_000_000 ;   //标准时钟时钟频率,    100Mhz
 
wire            clk_stand           ;   
wire            gate_a_flag_s       ;   
wire            gate_a_flag_t       ;   
 
reg     [27:0]  cnt_gate_s          ;   
reg             gate_s              ;   
reg             gate_a              ;   
reg             gate_a_test         ;   
reg             gate_a_stand        ;   
reg             gate_a_stand_reg    ;
reg             gate_a_test_reg     ;   
reg     [47:0]  cnt_clk_stand       ;   
reg     [47:0]  cnt_clk_stand_reg   ;   
reg     [47:0]  cnt_clk_test        ;   
reg     [47:0]  cnt_clk_test_reg    ;   
reg             calc_flag           ;   
reg     [63:0]  freq_reg            ;
reg             calc_flag_reg       ;
//cnt_gate_s:软件闸门计数器
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_gate_s  <=  28'd0;
    else    if(cnt_gate_s == CNT_GATE_S_MAX)
        cnt_gate_s  <=  28'd0;
    else
        cnt_gate_s  <=  cnt_gate_s + 1'b1;
		
//gate_s:软件闸门
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_s  <=  1'b0;
    else    if((cnt_gate_s>= CNT_RISE_MAX)
                && (cnt_gate_s <= (CNT_GATE_S_MAX - CNT_RISE_MAX)))
        gate_s  <=  1'b1;
    else
        gate_s  <=  1'b0;
		
//gate_a:实际闸门
always@(posedge clk_test or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_a  <=  1'b0;
    else
        gate_a  <=  gate_s;
		
//得到待测信号的周期数 X
always@(posedge clk_test or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_a_test  <=  1'b0;
    else
        gate_a_test  <=  gate_a;		
		
//gate_a_test:实际闸门打一拍(待检测时钟下)
always@(posedge clk_test or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_a_test_reg <=  1'b0;
    else
        gate_a_test_reg <=  gate_a_test;	
		
//cnt_clk_test:待检测时钟周期计数器,计数实际闸门下待检测时钟周期数。
always@(posedge clk_test or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk_test    <=  48'd0;
    else    if(gate_a_test == 1'b0)
        cnt_clk_test    <=  48'd0;
    else    if(gate_a_test == 1'b1)
        cnt_clk_test    <=  cnt_clk_test + 1'b1;
		
//gate_a_flag_t:实际闸门下降沿(待检测时钟下)
assign  gate_a_flag_t = ((gate_a_test_reg == 1'b1) && (gate_a_test == 1'b0))
                        ? 1'b1 : 1'b0;
						
//cnt_clk_test_reg:实际闸门下待检测时钟周期数,  x
always@(posedge clk_test or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk_test_reg   <=  32'd0;
    else    if(gate_a_flag_t == 1'b1)
        cnt_clk_test_reg   <=  cnt_clk_test;	
 
//得到标准信号的周期数 y		
//使用PLL生成100Mhz信号
clk_stand	clk_stand_inst (
	.areset ( ~sys_rst_n ),
	.inclk0 ( sys_clk ),
	.c0 ( clk_stand )
	);
 
//gate_a_stand:实际闸门打一拍(标准时钟下)
always@(posedge clk_stand or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_a_stand    <=  1'b0;
    else
        gate_a_stand    <=  gate_a_test;
		
always@(posedge clk_stand or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        gate_a_stand_reg    <=  1'b0;
    else
        gate_a_stand_reg    <=  gate_a_stand;
		
//gate_a_flag_s:实际闸门下降沿(标准时钟下)
assign  gate_a_flag_s = ((gate_a_stand_reg == 1'b1) && (gate_a_stand == 1'b0))
                        ? 1'b1 : 1'b0;
						
//cnt_clk_stand:标准时钟周期计数器,计数实际闸门下标准时钟周期数。
always@(posedge clk_stand or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk_stand   <=  48'd0;
    else    if(gate_a_stand == 1'b0)
        cnt_clk_stand   <=  48'd0;
    else    if(gate_a_stand == 1'b1)
        cnt_clk_stand   <=  cnt_clk_stand + 1'b1;
		
//cnt_clk_stand_reg:实际闸门下标志时钟周期数
always@(posedge clk_stand or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_clk_stand_reg   <=  32'd0;
    else    if(gate_a_flag_s == 1'b1)
        cnt_clk_stand_reg   <=  cnt_clk_stand;
		
//step4: 利用公式进行频率计算		
//calc_flag:待检测时钟时钟频率计算标志信号
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        calc_flag   <=  1'b0;
    else    if(cnt_gate_s == (CNT_GATE_S_MAX - 1'b1))
        calc_flag   <=  1'b1;
    else
        calc_flag   <=  1'b0;
		
//freq:待检测时钟信号时钟频率
always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        freq_reg    <=  64'd0;
    else    if(calc_flag == 1'b1)
        freq_reg    <=  (CLK_STAND_FREQ * cnt_clk_test_reg / cnt_clk_stand_reg );    
 
 //calc_flag_reg:待检测时钟频率输出标志信号
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        calc_flag_reg <= 1'b0;
    else
        calc_flag_reg <= calc_flag;
 
 //freq:待检测时钟信号时钟频率
 always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        freq <= 34'd0;
    else if(calc_flag_reg == 1'b1)
        freq <= freq_reg[33:0];	  
 
endmodule

(2)  HC_595控制模块

module  hc595_ctrl
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [5:0]   sel         ,
    input   wire    [7:0]   seg         ,

    output  reg             ds          ,
    output  reg             shcp        ,
    output  reg             stcp        ,
    output  wire            oe
);

wire    [13:0]  data    ;
reg     [1:0]   cnt     ;
reg     [3:0]   cnt_bit ;

assign  data = {seg[0],seg[1],seg[2],seg[3],seg[4],seg[5],seg[6],seg[7],sel};

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt <=  2'd0;
    else    if(cnt == 2'd3)
        cnt <=  2'd0;
    else
        cnt <=  cnt + 1'b1;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_bit <=  4'd0;
    else    if((cnt_bit == 4'd13) && (cnt == 2'd3))
        cnt_bit <=  4'd0;
    else    if(cnt == 2'd3)
        cnt_bit <=  cnt_bit + 1'b1;
    else
        cnt_bit <=  cnt_bit;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        ds  <=  1'b0;
    else    if(cnt == 2'd0)
        ds  <=  data[cnt_bit];
    else
        ds  <=  ds;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shcp    <=  1'b0;
    else    if(cnt == 2'd2)
        shcp    <=  1'b1;
    else    if(cnt == 2'd0)
        shcp    <=  1'b0;
    else
        shcp    <=  shcp;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        stcp    <=  1'b0;
    else    if((cnt_bit == 4'd0) && (cnt == 2'd0))
        stcp    <=  1'b1;
    else    if((cnt_bit == 4'd0) && (cnt == 2'd2))
        stcp    <=  1'b0;
    else
        stcp    <=  stcp;

assign  oe = 1'b0;

endmodule

(3)二进制码转BCD码部分

module  bcd_8421
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [19:0]  data        ,

    output  reg     [3:0]   unit        ,
    output  reg     [3:0]   ten         ,
    output  reg     [3:0]   hun         ,
    output  reg     [3:0]   tho         ,
    output  reg     [3:0]   t_tho       ,
    output  reg     [3:0]   h_hun
);

reg     [4:0]   cnt_shift   ;
reg     [43:0]  data_shift  ;
reg             shift_flag  ;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_shift   <=  5'd0;
    else    if((cnt_shift == 5'd21) && (shift_flag == 1'b1))
        cnt_shift   <=  5'd0;
    else    if(shift_flag == 1'b1)
        cnt_shift   <=  cnt_shift + 1'b1;
    else
        cnt_shift   <=  cnt_shift;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_shift  <=  44'b0;
    else    if(cnt_shift == 5'd0)
        data_shift  <=  {24'b0,data};
    else    if((cnt_shift <= 20) && (shift_flag == 1'b0))
        begin
            data_shift[23:20]   <=  (data_shift[23:20] > 4) ? (data_shift[23:20] + 2'd3) : (data_shift[23:20]);
            data_shift[27:24]   <=  (data_shift[27:24] > 4) ? (data_shift[27:24] + 2'd3) : (data_shift[27:24]);
            data_shift[31:28]   <=  (data_shift[31:28] > 4) ? (data_shift[31:28] + 2'd3) : (data_shift[31:28]);
            data_shift[35:32]   <=  (data_shift[35:32] > 4) ? (data_shift[35:32] + 2'd3) : (data_shift[35:32]);
            data_shift[39:36]   <=  (data_shift[39:36] > 4) ? (data_shift[39:36] + 2'd3) : (data_shift[39:36]);
            data_shift[43:40]   <=  (data_shift[43:40] > 4) ? (data_shift[43:40] + 2'd3) : (data_shift[43:40]);
        end
    else    if((cnt_shift <= 20) && (shift_flag == 1'b1))
        data_shift  <=  data_shift << 1;
    else
        data_shift  <=  data_shift;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        shift_flag  <=  1'b0;
    else
        shift_flag  <=  ~shift_flag;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        begin
            unit    <=  4'b0;
            ten     <=  4'b0;
            hun     <=  4'b0;
            tho     <=  4'b0;
            t_tho   <=  4'b0;
            h_hun   <=  4'b0;
        end
    else    if(cnt_shift == 5'd21)
        begin
            unit    <=  data_shift[23:20];
            ten     <=  data_shift[27:24];
            hun     <=  data_shift[31:28];
            tho     <=  data_shift[35:32];
            t_tho   <=  data_shift[39:36];
            h_hun   <=  data_shift[43:40];
        end

endmodule

 (4)动态显示部分

module  seg_dynamic
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [19:0]  data        ,
    input   wire    [5:0]   point       ,
    input   wire            sign        ,
    input   wire            seg_en      ,

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

parameter   CNT_MAX =   16'd49_999;

wire    [3:0]   unit        ;
wire    [3:0]   ten         ;
wire    [3:0]   hun         ;
wire    [3:0]   tho         ;
wire    [3:0]   t_tho       ;
wire    [3:0]   h_hun       ;
reg     [23:0]  data_reg    ;
reg     [15:0]  cnt_1ms     ;
reg             flag_1ms    ;
reg     [2:0]   cnt_sel     ;
reg     [5:0]   sel_reg     ;
reg     [3:0]   data_disp   ;
reg             dot_disp    ;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_reg    <=  24'b0;
    else    if((h_hun) || (point[5]))
        data_reg    <=  {h_hun,t_tho,tho,hun,ten,unit};
    else    if(((t_tho) || (point[4])) && (sign == 1'b1))
        data_reg    <=  {4'd10,t_tho,tho,hun,ten,unit};
    else    if(((t_tho) || (point[4])) && (sign == 1'b0))
        data_reg    <=  {4'd11,t_tho,tho,hun,ten,unit};
    else    if(((tho) || (point[3])) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd10,tho,hun,ten,unit};
    else    if(((tho) || (point[3])) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,tho,hun,ten,unit};
    else    if(((hun) || (point[2])) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd10,hun,ten,unit};
    else    if(((hun) || (point[2])) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,4'd11,hun,ten,unit};
    else    if(((ten) || (point[1])) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd10,ten,unit};
    else    if(((ten) || (point[1])) && (sign == 1'b0))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,ten,unit};
    else    if(((unit) || (point[0])) && (sign == 1'b1))
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,4'd10,unit};
    else
        data_reg    <=  {4'd11,4'd11,4'd11,4'd11,4'd11,unit};

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_1ms <=  16'd0;
    else    if(cnt_1ms == CNT_MAX)
        cnt_1ms <=  16'd0;
    else
        cnt_1ms <=  cnt_1ms + 1'b1;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        flag_1ms    <=  1'b0;
    else    if(cnt_1ms == CNT_MAX - 1'b1)
        flag_1ms    <=  1'b1;
    else
        flag_1ms    <=  1'b0;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        cnt_sel <=  3'd0;
    else    if((cnt_sel == 3'd5) && (flag_1ms == 1'b1))
        cnt_sel <=  3'd0;
    else    if(flag_1ms == 1'b1)
        cnt_sel <=  cnt_sel + 1'b1;
    else
        cnt_sel <=  cnt_sel;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sel_reg <=  6'b000_000;
    else    if((cnt_sel == 3'd0) && (flag_1ms == 1'b1))
        sel_reg <=  6'b000_001;
    else    if(flag_1ms == 1'b1)
        sel_reg <=  sel_reg << 1;
    else
        sel_reg <=  sel_reg;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        data_disp   <=  4'd0;
    else    if((seg_en == 1'b1) && (flag_1ms == 1'b1))
        case(cnt_sel)
            3'd0:   data_disp   <=  data_reg[3:0]   ;
            3'd1:   data_disp   <=  data_reg[7:4]   ;
            3'd2:   data_disp   <=  data_reg[11:8]  ;
            3'd3:   data_disp   <=  data_reg[15:12];
            3'd4:   data_disp   <=  data_reg[19:16];
            3'd5:   data_disp   <=  data_reg[23:20];
            default :   data_disp   <=  4'b0;
        endcase
    else
        data_disp   <=  data_disp;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        dot_disp    <=  1'b1;
    else    if(flag_1ms == 1'b1)
        dot_disp    <=  ~point[cnt_sel];
    else
        dot_disp    <=  dot_disp;

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        seg <=  8'b1111_1111;
    else
        case(data_disp)
            4'd0:   seg <=  {dot_disp,7'b100_0000};
            4'd1:   seg <=  {dot_disp,7'b111_1001};
            4'd2:   seg <=  {dot_disp,7'b010_0100};
            4'd3:   seg <=  {dot_disp,7'b011_0000};
            4'd4:   seg <=  {dot_disp,7'b001_1001};
            4'd5:   seg <=  {dot_disp,7'b001_0010};
            4'd6:   seg <=  {dot_disp,7'b000_0010};
            4'd7:   seg <=  {dot_disp,7'b111_1000};
            4'd8:   seg <=  {dot_disp,7'b000_0000};
            4'd9:   seg <=  {dot_disp,7'b001_0000};
            4'd10:  seg <=  8'b1011_1111;
            4'd11:  seg <=  8'b1111_1111;
            default:seg <=  8'b1100_0000;
        endcase

always@(posedge sys_clk or negedge sys_rst_n)
    if(sys_rst_n == 1'b0)
        sel <=  6'b000_000;
    else
        sel <=  sel_reg;

bcd_8421    bcd_8421_inst
(
    .sys_clk     (sys_clk  ),
    .sys_rst_n   (sys_rst_n),
    .data        (data     ),

    .unit        (unit     ),
    .ten         (ten      ),
    .hun         (hun      ),
    .tho         (tho      ),
    .t_tho       (t_tho    ),
    .h_hun       (h_hun    )
);



endmodule

 (5)HC595_动态显示

module  seg_595_dynamic
(
    input   wire            sys_clk     ,
    input   wire            sys_rst_n   ,
    input   wire    [19:0]  data        ,
    input   wire    [5:0]   point       ,
    input   wire            sign        ,
    input   wire            seg_en      ,

    output  wire            ds          ,
    output  wire            oe          ,
    output  wire            shcp        ,
    output  wire            stcp
);

wire    [5:0]   sel ;
wire    [7:0]   seg ;


seg_dynamic seg_dynamic_inst
(
    .sys_clk     (sys_clk),
    .sys_rst_n   (sys_rst_n),
    .data        (data),
    .point       (point),
    .sign        (sign),
    .seg_en      (seg_en),

    .sel         (sel),
    .seg         (seg)
);

hc595_ctrl  hc595_ctrl
(
    .sys_clk     (sys_clk),
    .sys_rst_n   (sys_rst_n),
    .sel         (sel),
    .seg         (seg),

    .ds          (ds  ),
    .shcp        (shcp  ),
    .stcp        (stcp),
    .oe          (oe)
);

endmodule

(6)顶层模块

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


    output  wire            ds          ,
    output  wire            oe          ,
    output  wire            shcp        ,
    output  wire            stcp
);

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)
);



seg_595_dynamic seg_595_dynamic_inst
(
    .sys_clk     (sys_clk),
    .sys_rst_n   (sys_rst_n),
    .data        (freq),
    .point       (6'b001_000),
    .sign        (1'b0),
    .seg_en      (1'b1),

    .ds          (ds  ),
    .oe          (oe  ),
    .shcp        (shcp),
    .stcp        (stcp)
);

endmodule

六、上板验证与调试:

根据时序和计算方法编写程序,将测得的频率结果显示在数码管中。

(1)数码管显示单位为KHZ(顶层文件中seg_595_dynamic中的data直接例化freq)

  a.    信号发生器给出1HZ频率的信号,如图所示,数码管显示0.001KHZ=1HZ。

b.   信号发生器给出10HZ频率的信号,如图所示,数码管显示0.010KHZ=10HZ。

c.   信号发生器给出50.24KHZ频率的信号,数码管显示50.24KHZ。

(2) 数码管显示单位为MHZ(顶层文件中seg_595_dynamic中的data直接例化freq/1000)

a.  信号发生器给出1MHZ的信号,数码管显示频率为1MHZ。

b.  信号发生其给出24.9MHZ频率信号,数码管显示频率为24.9MHZ。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值