使用Verilog实现秒表计时器功能。
要求:
- 数码管从0-20s计时,20s时自动归零
- 动态驱动数码管
- 有暂停按钮和归零按钮
- 有按键消抖
- 编写仿真文件,得到仿真波形图
原理:
- 要实现计时功能,需要用到时钟信号,在时钟信号上升时计数器加一
- 动态驱动数码管是指开发板上的数码管为共阴极,一次只可使一个数码管导通,但通过时钟信号可以让不同数码管不停切换显示,只要其切换频率大于人眼可见频率,即可视为多个数码管同时显示不同数字。
- 使用100Hz频率时钟,若要使计时器每1s加一,需要对时钟信号分频,将100Hz信号分为1Hz信号。
CODE
采用多模块设计
- count.v(顶层文件)
/*
工程名:count
文件名:count.v
*/
module count(CLK,RST,btn,seg,cat);
input CLK,RST,btn;
wire [4:0] Q; //计数器
wire [3:0] Q0; //计数器个位
wire [3:0] Q1; //计数器十位
output [6:0] seg; //数码管
output [7:0]cat; //数码管编号
reg [4:0] Q_TMP = 0; //计数器前置
reg pulse = 0; //暂停信号
wire btn_debounce; //消抖后信号
reg clk_out = 0; //1HZ分频时钟
reg [5:0] counter = 0; // 定义时钟计数器,用于计数时钟周期
assign Q = Q_TMP; //Q始终等于Q_TMP
always @(posedge CLK) begin
counter <= counter + 1; // 时钟计数器加1
if (counter == 49) begin // 当时钟计数器达到49时,输出一个时钟周期
clk_out <= ~clk_out; // 取反输出时钟信号
counter <= 0; // 时钟计数器清零
end
end
always @(posedge clk_out or posedge RST) begin //计数器控制
if (RST) Q_TMP = 0;
else if (Q_TMP==19) Q_TMP = 0;
else if(pulse) Q_TMP <= Q_TMP; //pulse为1时暂停
else Q_TMP <= Q_TMP + 1; //pulse为0时不暂停
end
always @(posedge btn_debounce) begin //暂停控制
pulse <= ~pulse; //按下按钮将使暂停控制器翻转;
end
switch v2( //拆分器,将两位数Q拆分为个位Q0和十位Q1
.Q(Q),
.OQ0(Q0),
.OQ1(Q1)
);
seg7 v1( //动态驱动数码管
.A0(Q0),
.A1(Q1),
.Y(seg),
.clk(CLK),
.cat(cat)
);
key_debounce v3( //按键消抖
.clk(CLK),
.i_key(btn),
.o_key(btn_debounce)
);
endmodule
- switch.v
/*
工程名:count
文件名:switch.v
*/
module switch(Q,OQ0,OQ1); //拆分模块
input [3:0]Q; //输入两位数
output reg[3:0]OQ0; //个位数字
output reg[3:0]OQ1; //十位数字
always@(*) begin
if(Q<=9) begin
OQ0 = Q;
OQ1 = 0;
end
else begin
OQ0 = Q - 10;
OQ1 = 1;
end
end
endmodule
- seg7.v
/*
工程名:count
文件名:seg7.v
*/
module seg7(A0,A1,Y,clk,cat); //动态驱动数码管(当前驱动两位)
input[3:0] A0; //个位
input [3:0]A1; //十位
output reg[7:0] cat; //数码管使能端(置0启动)
input clk; //时钟信号
output[6:0] Y; //单个数码管信号
reg [6:0] Y;
always@(*)begin //由于只用驱动两位,故只需使用时钟信号的上升沿和下降沿即可实现动态驱动(驱动三位及以上需增加中介reg变量)
if(clk) begin
case(A0)
4'b0000 : Y=7'b1111110;//0
4'b0001 : Y=7'b0110000;//1
4'b0010 : Y=7'b1101101;//2
4'b0011 : Y=7'b1111001;//3
4'b0100 : Y=7'b0110011;//4
4'b0101 : Y=7'b1011011;//5
4'b0110 : Y=7'b1011111;//6
4'b0111 : Y=7'b1110000;//7
4'b1000 : Y=7'b1111111;//8
4'b1001 : Y=7'b1111011;//9
default : Y=7'b0000000;
endcase
cat = 8'b11111110; //数码管0使能(个位)
end
else if(~clk) begin
case(A1)
4'b0000 : Y=7'b1111110;//0
4'b0001 : Y=7'b0110000;//1
4'b0010 : Y=7'b1101101;//2
4'b0011 : Y=7'b1111001;//3
4'b0100 : Y=7'b0110011;//4
4'b0101 : Y=7'b1011011;//5
4'b0110 : Y=7'b1011111;//6
4'b0111 : Y=7'b1110000;//7
4'b1000 : Y=7'b1111111;//8
4'b1001 : Y=7'b1111011;//9
default : Y=7'b0000000;
endcase
cat = 8'b11111101; //数码管1使能(十位)
end
end
endmodule
- key_debounce.v
/*
工程名:count
文件名:key_debounce.v
*/
module key_debounce(input clk, i_key,output o_key ); //按键消抖
reg r_key_buf1, r_key_buf2;
always@(negedge clk)begin
r_key_buf2 <= r_key_buf1;
r_key_buf1 <= i_key;
end
assign o_key = clk & r_key_buf1 & (~r_key_buf2);
endmodule
仿真
- 仿真文件
`timescale 1ms / 1ms
module count_tb;
reg clk;
reg btn0;
reg reset;
wire [6:0] seg;
wire [7:0] cat;
// 实例化被测试模块
count uut (
.CLK(clk),
.RST(reset),
.btn(btn0),
.seg(seg),
.cat(cat)
);
always#5 clk = ~clk; // 100Hz时钟
// 初始化和模拟输入
initial begin
// 初始化
clk = 0;
btn0 = 0;
reset = 0;
#3000;
reset = 1;
#20
reset = 0;
#4000
// 模拟按钮按下
#10;
btn0 = 1;
#20;
btn0 = 0;//pulse
#3000;
#10 btn0 = 1;
#20 btn0 = 0;//continue
#3000;
#10 btn0 = 1;
#40 btn0 = 0;//pulse
#3000;
$stop;
end
endmodule
- 仿真波形
可以看出,已完美实现计时器功能,拥有暂停、归零功能,按钮已进行消抖。