數字頻率計
此設計是我在東南大學大三短學期數字系統課程設計的課題。(如果有學弟學妹看到這篇文章,希望可以給你們提供一定的幫助,但也希望別直接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當成寫代碼而不是設計電路。
以上