数字频率计
此设计是我在东南大学大三短学期数字系统课程设计的课题。(如果有学弟学妹看到这篇文章,希望可以给你们提供一定的帮助,但也希望别直接controlc,controlv)
同时,本文中的代码因为经过再编辑,可能某些功能会有错误,敬请谅解。
设计要求:
(1)频率测量范围10Hz~1MHz
(2)量程自动转换,量程分为10KHz (1s) 、100KHz (0.1s) 、1MHz (10ms)三档。转换规则如下:当读数大于9999时,频率计处于超量程状态,下一次测量时,量程自动增大一档;当读数小于0999时,频率计处于欠量程状态,下一次测量时,量程自动减小一档
(3)数据采用记忆显示方式,即计数过程中不显示数据,待计数过程结束以后,显示计数结果,并将此显示结果保持到下一次计数结束。
(4)用发光二极管显示量程
本设计关键难点就是调档位,需要用到反馈控制
![](https://i-blog.csdnimg.cn/blog_migrate/79d08a1e2b7773a720f4934b2ea30435.png)
频率显示时仅用四位数码管即可(有小数点),由图可知,第一档时,数 字范围是 0001——9999,小数点位于左端;第二档时,数字范围是 1000—— 9999,小数点位于中间;第三档时,数字范围是 1000——9999,小数点位于右端。 所以可以确定最终输出应由两部分构成,数字信息和控制小数点位置的档位信息。
![](https://i-blog.csdnimg.cn/blog_migrate/092a60a5a7c4dcab16677a2d69c4a75a.png)
具体实现原理接下来按照模块来说明
1 分频器模块
功能:实现对2MHz时钟信号的分频,这里有三个分频器,可以分出1Hz,10Hz,100Hz的三个时钟信号,对应三个不同的档位。
逻辑:时序逻辑
分频器模块代码:
module divider1hz(input source,rst,output reg clk);
reg[31:0] storage;
always@(posedge source, negedge rst)
if(rst == 0)
begin
clk <= 0;
storage <= 0;
end
else
begin
if(storage == 32'd999999)// dividing level 1MHZ to 1 HZ
begin
clk <= ~clk;
storage <= 0;
end
else storage <= storage + 1;
end
endmodule
module divider10hz(input source,rst,output reg clk);
reg[31:0] storage;
always@(posedge source, negedge rst)
if(rst == 0)
begin
clk <= 0;
storage <= 0;
end
else
begin
if(storage == 32'd99999)// dividing level 1MHZ to 100 HZ
begin
clk <= ~clk;
storage <= 0;
end
else storage <= storage + 1;
end
endmodule
module divider100hz(input source,rst,output reg clk);
reg[31:0] storage;
always@(posedge source, negedge rst)
if(rst == 0)
begin
clk <= 0;
storage <= 0;
end
else
begin
if(storage == 32'd9999)// dividing level 1MHZ to 100 HZ
begin
clk <= ~clk;
storage <= 0;
end
else storage <= storage + 1;
end
endmodule
2 数据选择器模块
功能:根据控制模块输出的档位信号level,对三个分频器的信号进行选择(档位选择)
逻辑:组合逻辑
数据选择器模块代码:
module mux3to1 (input level1,level10,level100,clk1MHz,input[1:0] level,output reg clk);
always @(*)
begin
case(level)
2'b01 : clk = level1;
2'b10 : clk = level10;
2'b11 : clk = level100;
default: clk = level1;
endcase
end
endmodule
3 计数器模块
功能:记录一个clk周期内的待测信号上升沿的个数,理论范围是0~999999
逻辑:时序逻辑
计数器模块代码:
module counter(input signal,clk,reset,output reg [31:0] dout);
reg [31:0]N;//used for count
reg [31:0]M;//used for out
always@(posedge signal , negedge reset)
begin
if (reset == 0)
begin
N <= 0;
M <= 0;
dout <= 0;
end
else
if(clk == 1)
begin
N <= N + 1;
M <= N + 1;
end
else
begin
dout <= M + M;
N <= 0;
end
end
endmodule
4 控制器模块
功能:接收计数器的输出,并调节档位值level反馈给多路选择器,同时把显示结果(0~9999)输出给输出转换模块
逻辑:时序逻辑
控制器模块代码:
module control(din,reset,level,dout,clk);
input din,reset,clk;
output level,dout;
wire [31:0]din;
reg [1:0]level;
reg [15:0]dout;
always@(negedge reset, posedge clk)
begin
if(reset == 0)
begin
level <= 2'd1;
dout <= 16'd0;
end
else
begin
if(din < 32'd1000)
begin
if(level==2'd1)
begin
level<=2'd1;
dout <= din[15:0];
end
else
begin
level<=level-2'd1;
end
end
else
begin
if(din>32'd9999)
begin
if(level==2'd3)
begin
dout=16'd9999;
level<=2'd3;
end
else
begin
level<=level+2'd1;
end
end
else
begin
dout <= din[15:0];
end
end
end
end
endmodule
5 输出转换模块
功能:控制器的输出是16位2进制数,本模块的作用是把16位2进制数转换为四个四位的BCD码数据输出,对应个、十、百、千位,可显示在数码管上。
逻辑:组合逻辑
输出转换模块代码:
module bin2bcd(input[15:0] binary,output reg[3:0] Thou,Hun,Ten,One);
integer i;
always@(*)
begin
for(i=15;i>=0;i=i-1)
begin
if(i == 15)
begin
Thou = 0;
Hun = 0;
Ten = 0;
One = 0;
Thou = Thou<<1;//shift leftssss one
Thou[0] = Hun[3];
Hun = Hun<<1;
Hun[0] = Ten[3];
Ten = Ten<<1;
Ten[0] = One[3];
One = One<<1;
One[0] = binary[i];
end
else
begin
if(Thou>=5) Thou = Thou + 3;
else i = i;
if(Hun>=5) Hun = Hun + 3;
else i = i;
if(Ten>=5) Ten = Ten + 3;
else i = i;
if(One>=5) One = One + 3;
else i = i;
Thou = Thou<<1;//shift leftssss one
Thou[0] = Hun[3];
Hun = Hun<<1;
Hun[0] = Ten[3];
Ten = Ten<<1;
Ten[0] = One[3];
One = One<<1;
One[0] = binary[i];
end
end
end
endmodule
总模块
总模块代码:
module FreqMeter(
input wire clk1MHz,
input wire rst,
input wire data,
output wire [15:0]out,
output wire w_divider1_out,w_divider10_out,w_divider100_out,w_divider_clk_out,
output wire [1:0]w_level_out,
output wire [31:0]w_counter_out,
output wire [15:0]w_controller_out
);
wire w_divider1,w_divider10,w_divider100;
wire[1:0] w_level;
wire w_divider_clk;
wire[31:0] w_counter;
wire[15:0] w_controller;
assign w_divider1_out = w_divider1;
assign w_divider10_out = w_divider10;
assign w_divider100_out = w_divider100;
assign w_divider_clk_out = w_divider_clk;
assign w_level_out = w_level;
assign w_counter_out = w_counter;
assign w_controller_out = w_controller;
divider1hz Divider1hz(.source(clk1MHz),.rst(rst),.clk(w_divider1));
divider10hz Divider10hz(.source(clk1MHz),.rst(rst),.clk(w_divider10));
divider100hz Divider100hz(.source(clk1MHz),.rst(rst),.clk(w_divider100));//实例化分频器
mux3to1 Mux3to1(.level1(w_divider1),.level10(w_divider10),//实例化数据选择器
.level100(w_divider100),.level(w_level),.clk(w_divider_clk),.clk1MHz(clk1MHz));
counter Counter(.signal(data),
.clk(w_divider_clk),.reset(rst),.dout(w_counter));//实例化计数器
control Control(.din(w_counter),.reset(rst),.level(w_level),
.dout(w_controller),.clk(w_divider_clk));//实例化控制器
bin2bcd Bin2bcd(.binary(w_controller),
.Thou(out[15:12]),.Hun(out[11:8]),.Ten(out[7:4]),.One(out[3:0]));//实例化转换器
endmodule
总模块的testbench
总模块的testbench代码:
`timescale 10ns/10ns
module FreqMeter_TB;
reg CLK;
reg RST;
reg DATA;
wire[15:0] OUT;
parameter FREQ = 400;//unit KHz
parameter DATA_DELAY = 50000 / FREQ ;
parameter SIMULATION_TIME = 1000000000;
integer i,j;
wire W_DIVIDER1_OUT;
wire W_DIVIDER10_OUT;
wire W_DIVIDER100_OUT;
wire W_DIVIDER_CLK_OUT;
wire[1:0] W_LEVEL_OUT;
wire[31:0] W_COUNTER_OUT;
wire[15:0] W_CONTROLLER_OUT;
FreqMeter freqmeter(
.clk1MHz(CLK),
.rst(RST),
.data(DATA),
.out(OUT),
.w_divider1_out(W_DIVIDER1_OUT),
.w_divider10_out(W_DIVIDER10_OUT),
.w_divider100_out(W_DIVIDER100_OUT),
.w_divider_clk_out(W_DIVIDER_CLK_OUT),
.w_level_out(W_LEVEL_OUT),
.w_counter_out(W_COUNTER_OUT),
.w_controller_out(W_CONTROLLER_OUT)
);
initial
begin
CLK = 0;
for(i=0;i<SIMULATION_TIME/25;i=i+1)
begin
#25 CLK = ~CLK;
end
end
initial
begin
RST = 1;
#1000 RST = 0;
#50 RST = 1;
end
initial
begin
DATA = 0;
for(j=0;j < SIMULATION_TIME / DATA_DELAY;j=j+1)
begin
#DATA_DELAY DATA = ~DATA;
end
end
endmodule
仿真测试
当时在学校用的quartus2写的代码并跑了仿真,记得当时只能用波形文件跑仿真,现在想想不用写testbench也挺好的23333。
这里testbench里面写的测试信号频率是4000KHz,所以应该是第三档,数码管上理应显示“400.0”。
测试波形:
总结
时隔两年,因为要找工作而正在学习verilog HDL,再回顾曾经的代码,
有惊喜,原来我大学的时候也认真地学过某个学科2333;原来的我似乎比现在要聪明,这个东西当时怎么想出来的啊?
有嫌弃,代码风格很乱,很多地方理解的不是很深入,时序之类的完全没搞懂,确实,初学者还是容易把写verilog当成写代码而不是设计电路。
以上