基于verilog的数字频率计

数字频率计 

此设计是我在东南大学大三短学期数字系统课程设计的课题。(如果有学弟学妹看到这篇文章,希望可以给你们提供一定的帮助,但也希望别直接controlc,controlv)

同时,本文中的代码因为经过再编辑,可能某些功能会有错误,敬请谅解。

设计要求:

(1)频率测量范围10Hz~1MHz

(2)量程自动转换,量程分为10KHz (1s) 、100KHz (0.1s) 、1MHz (10ms)三档。转换规则如下:当读数大于9999时,频率计处于超量程状态,下一次测量时,量程自动增大一档;当读数小于0999时,频率计处于欠量程状态,下一次测量时,量程自动减小一档

(3)数据采用记忆显示方式,即计数过程中不显示数据,待计数过程结束以后,显示计数结果,并将此显示结果保持到下一次计数结束。

(4)用发光二极管显示量程

本设计关键难点就是调档位,需要用到反馈控制

档位对应频率范围

频率显示时仅用四位数码管即可(有小数点),由图可知,第一档时,数 字范围是 0001——9999,小数点位于左端;第二档时,数字范围是 1000—— 9999,小数点位于中间;第三档时,数字范围是 1000——9999,小数点位于右端。 所以可以确定最终输出应由两部分构成,数字信息和控制小数点位置的档位信息。 

数字频率计整体原理图

具体实现原理接下来按照模块来说明

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当成写代码而不是设计电路。

以上

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章