设计一个时钟秒表,共六个数码管,前两位显示分钟,中间两位显示时间秒,后两位显示毫秒的高两位,可以通过按键来开始、暂停以及重新开始秒表的计数。
为完成此项目共设计四个模块,按键消抖模块、时钟计时器模块、数码管显示模块以及顶层文件模块,详细代码设计如下:
按键消抖模块:
/*
* @Description: 按键销抖模块(延迟方法销抖)
* @Author: Fu Yu
* @Date: 2023-08-01 14:31:19
* @LastEditTime: 2023-08-01 16:29:44
* @LastEditors: Fu Yu
*/
module key_filter (
input wire clk ,
input wire rst_n ,
input wire key_in ,//按键输入,低有效
output wire key_down //按键输出,高有效
);
parameter MAX_20MS = 20'd999_999;//20ms
reg key_r;//同步
reg key_r1;//打拍
reg key_r2;
wire nedge;//下降沿
reg key_down_r;//保存key_down信号
reg [19:0] cnt_20ms;
reg flag;//计数开始标志信号
wire add_cnt_20ms;
wire end_cnt_20ms;
//****************************************************************
//--同步打拍,检测下降沿
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_r <= 1'b1;
key_r1 <= 1'b1;
key_r2 <= 1'b1;
end
else begin
key_r <= key_in;
key_r1 <= key_r;
key_r2 <= key_r1;
end
end
//下降沿检测
assign nedge = ~key_r1 & key_r2;
//****************************************************************
//--flag信号定义
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flag <= 1'b0;
end
else if(nedge) begin
flag <= 1'b1;
end
else if(end_cnt_20ms) begin
flag <= 1'b0;
end
else begin
flag <= flag;
end
end
//****************************************************************
//--计数
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 20'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 20'd0;
end
else begin
cnt_20ms <= cnt_20ms + 1'b1;
end
end
end
assign add_cnt_20ms = flag;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == MAX_20MS;
//****************************************************************
//--key_down
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
key_down_r <= 1'b0;
end
else if(end_cnt_20ms) begin
key_down_r <= ~key_r2;
end
else begin
key_down_r <= 1'b0;
end
end
assign key_down = key_down_r;
endmodule //key_filter
时钟计时器模块:
/*
* @Description: 通过输入的按键控制计数开始与暂停,实现秒表设计(毫秒、秒、分计时)
* @Author: Fu Yu
* @Date: 2023-08-01 14:46:43
* @LastEditTime: 2023-08-01 15:53:12
* @LastEditors: Fu Yu
*/
module counter_time (
input wire clk ,
input wire rst_n ,
input wire key_in ,//销抖后的脉冲信号,高有效
output wire [23:0] din_out //输出当前计数的值
);
parameter MAX_1MS = 16'd49_999;//1ms
parameter MAX_1S = 10'd999;//1ms*1000 = 1s
parameter MAX_1MIN = 6'd59;//1s*60 = 1min
parameter MAX_1H = 6'd59;//1min*60 = 1h
reg flag;//开始、暂停结束信号
reg [15:0] cnt_1ms;
wire add_cnt_1ms;
wire end_cnt_1ms;
reg [9:0] cnt_1s;
wire add_cnt_1s;
wire end_cnt_1s;
reg [5:0] cnt_1min;
wire add_cnt_1min;
wire end_cnt_1min;
reg [5:0] cnt_1h;
wire add_cnt_1h;
wire end_cnt_1h;
reg [7:0] data_min;//保存此时有多少分钟
reg [7:0] data_s;//保存此时有多少秒
reg [7:0] data_ms;//保存此时有多少毫秒,只取高两位
//****************************************************************
//--flag
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
flag <= 1'b0;
end
else if (key_in) begin
flag <= ~flag;
end
else begin
flag <= flag;
end
end
//****************************************************************
//--1ms计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1ms <= 16'd0;
end
else if(add_cnt_1ms)begin
if(end_cnt_1ms)begin
cnt_1ms <= 16'd0;
end
else begin
cnt_1ms <= cnt_1ms + 1'b1;
end
end
end
assign add_cnt_1ms = flag;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == MAX_1MS;
//****************************************************************
//--1s计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1s <= 10'd0;
end
else if(add_cnt_1s)begin
if(end_cnt_1s)begin
cnt_1s <= 10'd0;
end
else begin
cnt_1s <= cnt_1s + 1'b1;
end
end
end
assign add_cnt_1s = end_cnt_1ms;
assign end_cnt_1s = add_cnt_1s && cnt_1s == MAX_1S ;
//&& end_cnt_1ms
//****************************************************************
//--1min计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1min <= 6'd0;
end
else if(add_cnt_1min)begin
if(end_cnt_1min)begin
cnt_1min <= 6'd0;
end
else begin
cnt_1min <= cnt_1min + 1'b1;
end
end
end
assign add_cnt_1min = end_cnt_1s;
assign end_cnt_1min = add_cnt_1min && cnt_1min == MAX_1MIN ;
//&& end_cnt_1s
//****************************************************************
//--1h计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1h <= 6'd0;
end
else if(add_cnt_1h)begin
if(end_cnt_1h)begin
cnt_1h <= 6'd0;
end
else begin
cnt_1h <= cnt_1h + 1'b1;
end
end
end
assign add_cnt_1h = end_cnt_1min;
assign end_cnt_1h = add_cnt_1h && cnt_1h == MAX_1H;
// && end_cnt_1min
//****************************************************************
//--din_out
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
data_min <= 8'd0;
data_s <= 8'd0;
data_ms <= 8'd0;
end
else begin
data_min <= cnt_1h;
data_s <= cnt_1min;
data_ms <= cnt_1s/10;
end
end
assign din_out = {data_min,data_s,data_ms};
endmodule //counter_time
数码管显示模块:
/*
* @Description: 数码管显示时钟,前两位显示分钟,中间两位显示秒,最后两位显示毫秒
* @Author: Fu Yu
* @Date: 2023-08-01 15:19:52
* @LastEditTime: 2023-08-01 16:22:40
* @LastEditors: Fu Yu
*/
module seg_sel (
input wire clk ,
input wire rst_n ,
input wire [23:0] din ,
output wire [5:0] sel ,//位选信号
output wire [7:0] dig //段选信号
);
parameter MAX_1MS = 16'd49_999;//1ms
parameter ZERO = 7'b100_0000,
ONE = 7'b111_1001,
TWO = 7'b010_0100,
THREE = 7'b011_0000,
FOUR = 7'b001_1001,
FIVE = 7'b001_0010,
SIX = 7'b000_0010,
SEVEN = 7'b111_1000,
EIGHT = 7'b000_0000,
NINE = 7'b001_0000;
reg [5:0] point_n;//小数点位置
reg point_n_r;
reg [15:0] cnt_1ms;
wire add_cnt_1ms;
wire end_cnt_1ms;
reg [5:0] sel_r;
reg [7:0] dig_r;
reg [3:0] disp_data ;//每一位数码管显示的数值
//****************************************************************
//--1ms计数器
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_1ms <= 16'd0;
end
else if(add_cnt_1ms)begin
if(end_cnt_1ms)begin
cnt_1ms <= 16'd0;
end
else begin
cnt_1ms <= cnt_1ms + 1'b1;
end
end
end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == MAX_1MS;
//****************************************************************
//--小数点
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
point_n <= 6'b111111;
end
else begin
point_n <= 6'b110101;
end
end
//****************************************************************
//--sel信号
//****************************************************************
always @(posedge clk or negedge rst_n) begin
if(!rst_n) begin
sel_r <= 6'b111110;
end
else if(end_cnt_1ms) begin
sel_r <= {sel_r[4:0],sel_r[5]};//循环左移
end
end
assign sel = sel_r;
//****************************************************************
//--disp_data
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
disp_data <= 'd0;
point_n_r <= 1'b1;
end
else begin
case (sel_r)
6'b111_110 : begin disp_data <= din[23:16]/10 ; point_n_r <= point_n[0]; end//第一位数码管显示的数值
6'b111_101 : begin disp_data <= din[23:16]%10 ; point_n_r <= point_n[1]; end
6'b111_011 : begin disp_data <= din[15:8]/10 ; point_n_r <= point_n[2]; end
6'b110_111 : begin disp_data <= din[15:8]%10 ; point_n_r <= point_n[3]; end
6'b101_111 : begin disp_data <= din[7:0]/10 ; point_n_r <= point_n[4]; end
6'b011_111 : begin disp_data <= din[7:0]%10 ; point_n_r <= point_n[5]; end
default: disp_data <= 'd0;
endcase
end
end
//****************************************************************
//--dig
//****************************************************************
always @(*)begin
case (disp_data)
0 : dig_r <= {point_n_r,ZERO };
1 : dig_r <= {point_n_r,ONE };
2 : dig_r <= {point_n_r,TWO };
3 : dig_r <= {point_n_r,THREE };
4 : dig_r <= {point_n_r,FOUR };
5 : dig_r <= {point_n_r,FIVE };
6 : dig_r <= {point_n_r,SIX };
7 : dig_r <= {point_n_r,SEVEN };
8 : dig_r <= {point_n_r,EIGHT };
9 : dig_r <= {point_n_r,NINE };
default: dig_r <= 8'hff;
endcase
end
assign dig = dig_r;
endmodule //seg_sel
顶层文件模块:
module top (
input wire clk ,
input wire rst_n ,
input wire key_in ,
output wire [5:0] sel ,
output wire [7:0] dig
);
wire key;
wire [23:0] data;
key_filter u_key_filter(
. clk (clk) ,
. rst_n (rst_n),
. key_in (key_in),
. key_down (key)
);
counter_time u_counter_time(
. clk (clk) ,
. rst_n (rst_n) ,
. key_in (key) ,
. din_out (data)
);
seg_sel u_seg_sel(
. clk (clk) ,
. rst_n (rst_n) ,
. din (data) ,
. sel (sel) ,
. dig (dig)
);
endmodule //top
效果展示:
秒表