基於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當成寫代碼而不是設計電路。

以上

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