Verilog HDL:数字频率计

用Verilog HDL编写的一个数字频率计,在Intel FPGA开发平台Altera Quartus实现并使用Modelsim进行波形仿真。

设计任务及要求

设计任务:设计一个数字式频率计,测量数字信号和模拟信号的频率。
设计要求:被测信号为TTL脉冲信号;显示的频率范围为00~99Hz;测量精度为1Hz;用LED数码管显示频率数值。

设计思路

对整个数字频率计进行自底向上分块设计,分别实现频率测量、二进制到BCD码转换、 数码管动态译码显示三个子模块,最后将这三个子模块连接集成到一起,完成设计并实现任务要求。

频率测量模块实现的思路是,在1S的时间内,统计出输入的待测信号的上升沿个数,这个数值便是待测信号的频率。假设整体电路输入的时钟信号为50MHz,则要获得高电平时间为1s的采样信号,需要对电路输入输入信号进行25M倍的分频,从而得到一个频率为2Hz,占空比为50%的采样信号。

频率测量模块输出的是一个8位的二进制码,想要输入到数码管中进行显示,还需要设计一个二进制到BCD码转换模块进行处理。要将8位的二进制码转换为三个分别表示十进制的百位、十位、十位的BCD码,转换的思想是不断将这个8位的二进制码进行左移一位操作,每进行一次移位操作后查找各码对应的百位、十位、十位,当个位或十位的码值大于5时,就将该段码值加3。在进行8次移位操作后,得到一个18位的值中,18~17位的值为百位的BCD码,16~13位的值为十位的BCD码,12~9位的值为个位的BCD码。过程如下表所示:

操作 百位 十位 个位 二进制值(高) 二进制值(低)
初始值 1 1 1 1 1 1 1 1
左移1位 1 1 1 1 1 1 1 1
左移1位 1 1 1 1 1 1 1 1
左移1位 1 1 1 1 1 1 1 1
加3 1 0 1 0 1 1 1 1 1
左移1位 1 0 1 0 1 1 1 1 1
加3 1 1 0 0 0 1 1 1 1
左移1位 1 1 0 0 0 1 1 1 1
左移1位 1 1 0 0 0 1 1 1 1
加3 1 0 0 1 0 0 1 1 1 1
左移1位 1 0 0 1 0 0 1 1 1 1
加3 1 0 0 1 0 1 0 1 0 1
左移1位 1 0 0 1 0 1 0 1 0 1
BCD码 2 5 5

将二进制到BCD码转换模块输出的BCD码输入数码管动态译码显示模块中,设置好一定的数码管动态扫描频率,最后输出三位数码管选通信号及8位数码管数据信号,这样即完成了三个模块也是整个数字频率计电路的设计。

设计流程

频率测量、二进制到BCD码转换、 数码管动态译码显示三个模块分别命名为frequency、bin2dec、led_scan,编写好对应模块以及总体电路连接文件Frequency_Meter的Verilog代码,最后生成的总电路图如下:

总电路

频率测量模块

频率测量模块frequency输入50MHz的时钟信号clk_50M、置位信号rst_n以及输入信号sign_in,最后将输出测量出的输入信号的8位的二进制频率值freq。

Verilog代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
`timescale 1 ns/1 ps
module frequency
(
clk_50M, rst_n, sign_in,
freq
);

input clk_50M, rst_n, sign_in; //输入:50MHz时钟信号,置位信号,输入信号
output reg [7:0] freq; //输出:频率值

reg [31:0] count1, count2; //分频计数,频率计数
reg clk_1Hz, en; //高电平1Hz时钟信号,使能信号
wire load, clr; //载入信号,清零信号

always @ (posedge clk_50M or negedge rst_n) //等待clk上升沿或rst_n下降沿
begin
if(!rst_n) //低电平有效
begin
count1 <= 0;
clk_1Hz <= 0;
end
else if(count1 < 25000000 - 1) //对输入的clk作25000000倍分频得到clk_1Hz
begin
count1 <= count1 + 1 ;
clk_1Hz <= 1;
end
else if(count1 < 50000000 - 1)
begin
count1 <= count1 + 1;
clk_1Hz <= 0;
end
else
begin
count1 <= 0;
clk_1Hz <= 0;
end
end

always @ (posedge clk_1Hz or negedge rst_n) //等待clk_1Hz上升沿或rst_n下降沿
begin
if(!rst_n)
en <= 0;
else
en <= ~en; //翻转使能信号
end

assign load = ~en; //载入信号源
assign clr = load && (~clk_1Hz); //清零信号源

always @ (posedge sign_in or posedge clr) //等待Sign_in上升沿或clr上升沿
begin
if(clr)
count2 <= 0;
else if(en)
count2 <= count2 + 1; //计数sign_in上升沿
else
count2 <= count2;
end

always @ (posedge load or negedge rst_n) //等待load上升沿或rst_n下降沿
begin
if(!rst_n)
freq <= 0;
else
freq <= count2; //输出频率值
end

endmodule

8位二进制码转位BCD码模块

二进制到BCD码转换模块bin2dec也输入50MHz的时钟信号clk_50M、置位信号rst_n,加上frequency模块输出的freq值输入到bin端,计算8位的二进制频率值的十进制百位值hundred、十位值ten、个位值one的BCD码并输出到数码管动态译码显示模块led_scan。

Verilog代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
`timescale 1 ns/1 ps
module bin2dec
(
clk_50M, rst_n, bin,
one, ten, hundred
);

input [7:0] bin; //输入:8位二进制码
input clk_50M, rst_n; //50MHz时钟信号,置位信号

output reg [3:0] one,ten; //输出:个位,十位
output reg [3:0] hundred; //百位

reg [3:0] count; //移位计数

reg [17:0] shift_reg = 18'b000000000000000000; //移位寄存


always @ (posedge clk_50M or negedge rst_n) //等待clk上升沿或rst_n下降沿
begin
if(!rst_n) //低电平有效
count <= 0;
else if(count == 9)
count <= 0;
else
count <= count + 1;
end

always @ (posedge clk_50M or negedge rst_n) //等待clk上升沿或rst_n下降沿
begin
if(!rst_n)
shift_reg = 0;
else if(count == 0)
shift_reg = {10'b0000000000, bin};
else if(count <= 8) //进行8次移位操作
begin
if(shift_reg[11:8] >= 5) //个位和大于等于5时,加3
begin
if(shift_reg[15:12] >= 5) //十位大于等于5时,加3
begin
shift_reg[15:12] = shift_reg[15:12] + 2'b11;
shift_reg[11:8] = shift_reg[11:8] + 2'b11;
shift_reg = shift_reg << 1; //整体左移
end
else
begin
shift_reg[15:12] = shift_reg[15:12];
shift_reg[11:8] = shift_reg[11:8] + 2'b11;
shift_reg = shift_reg << 1;
end
end
else
begin
if(shift_reg[15:12] >= 5)
begin
shift_reg[15:12] = shift_reg[15:12] + 2'b11;
shift_reg[11:8] = shift_reg[11:8];
shift_reg = shift_reg << 1;
end
else
begin
shift_reg[15:12] = shift_reg[15:12];
shift_reg[11:8] = shift_reg[11:8];
shift_reg = shift_reg << 1;
end
end
end
end

always @ (posedge clk_50M or negedge rst_n) //等待clk上升沿或rst_n下降沿
begin
if(!rst_n)
begin
one <= 0;
ten <= 0;
hundred <= 0;
end
else if(count == 9)
begin
one <= shift_reg[11:8]; //个位存放在移位寄存的8~11位
ten <= shift_reg[15:12]; //十位存放在移位寄存的12~15位
hundred <= shift_reg[17:16]; //百位存放在移位寄存的16~17位
end
end

endmodule

数码管动态扫描模块

也向数码管动态译码显示模块led_scan输入50MHz的时钟信号clk_50M,最后就会输出8位数码管显示数据值data以及3位的数码管选通信号sel,组成整个逻辑电路。

Verilog代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
`timescale 1 ns/1 ps
module led_scan
(
clk_50M, hundred, ten, one,
sel, data
);

input clk_50M; //输入:50MHz时钟信号
input [3:0] hundred, ten, one; //个位,十位,百位
output reg [7:0] data; //输出:显示数据
output reg [2:0] sel; //选通信号

parameter updata_interval = 249999; //扫描频率50Hz
integer cnt_scan; //用于时钟信号

reg [3:0] data_display; //显示数据寄存
reg [1:0] cur_sel; //选通数据寄存

always @ (posedge clk_50M) //等待clk上升沿
begin
cnt_scan <= cnt_scan + 1;
if(cnt_scan == updata_interval)
begin
cnt_scan <= 0;
cur_sel <= cur_sel + 1;
end
end

always @ (*) //选通扫描
begin
sel = 3'b000;
case(cur_sel)
0: begin
data_display <= hundred;
sel <= 3'b110;
end
1: begin
data_display <= ten;
sel <= 3'b101;
end
2: begin
data_display <= one;
sel <= 3'b011;
end
default:
begin
data_display <= 0;
sel <= 3'b011;
end
endcase
end

always @ (data_display) //显示数据选择
begin
case(data_display)
4'h0 : data = 8'hc0;
4'h1 : data = 8'hf9;
4'h2 : data = 8'ha4;
4'h3 : data = 8'hb0;
4'h4 : data = 8'h99;
4'h5 : data = 8'h92;
4'h6 : data = 8'h82;
4'h7 : data = 8'hf8;
4'h8 : data = 8'h80;
4'h9 : data = 8'h90;
default: data = 8'hxx;
endcase
end

endmodule

总电路

将三个模块连接到一起。

Verilog代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
`timescale 1 ns/1 ps
module Frequency_Meter
(
clk_50M, rst_n, sign_in,
sel, data
);

input clk_50M, rst_n, sign_in; //输入:50MHz时钟信号,置位信号,输入信号

output [2:0] sel; //输出:数码管选通信号
output [7:0] data; //数码管显示数据

wire [7:0] freq; //频率输出线
wire [3:0] hundred, ten, one; //百、十、个位输出线

frequency A( //频率计
.clk_50M(clk_50M),
.rst_n(rst_n),
.sign_in(sign_in),
.freq(freq)
);

bin2dec B( //8位二进制码转位BCD码
.clk_50M(clk_50M),
.rst_n(rst_n),
.bin(freq),
.hundred(hundred),
.ten(ten),
.one(one)
);

led_scan C( //数码管动态扫描
.clk_50M(clk_50M),
.hundred(hundred),
.ten(ten),
.one(one),
.sel(sel),
.data(data)
);
endmodule

程序运行及结果

仿真过程中,输入50MHz的时钟信号、rst_n一直置为1,输入一个100Hz的脉冲信号,经过1s多时间的运行,频率测试模块的仿真结果如下图,结果准确无误。

频率测量模块仿真波形

对二进制到BCD码转换模块输入50MHz的时钟信号、rst_n一直置为1,bin端输入分时输入几个不同的二进制数,得到的仿真结果如下图。结果中准确将二进制值得十进制各位的BCD码计算了出来。

BCD码计算模块波形

整个电路的仿真结果如下:

整个电路波形

参考资料

  1. verilog编写数字频率计-csdn
  2. 利用verilog将二进制码转换为十进制BCD码-csdn
  3. 西勒提.《Verilog HDL高级数字设计》[M].电子工业出版社.2014.
  4. 代码资料-GitHub

更新历史:

  • 2018.1.1 完成初稿
文章作者: Hugsy
文章链接: http://binweber.top/2018/01/01/verilog/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Weber
支付宝打赏~
微信打赏~