开发板:野火征途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。