一.功能描述
- 6位数码管左边两位显示分钟,中间两位显示秒,右边两位显示毫秒的百位和十位;按键key[0]实现暂停计时和启动计时;按键key[1]实现清零。
二.模块设计图
三.设计思路
3.1按键消抖模块传输消抖成功后的按键信号
3.2设计计数器
- 10ms计时:计10ms
- ms计数器:每计10ms,ms计数器+1,最大值计到99
- sec计数器:ms计数器每计满一次,sec计数器+1,最大值到59
- min计数器:sec计数器每计满一次,min计数器+1,最大值到59
3.3设计暂停
- 设计flag信号,当flag为0时暂停计数,当flag为1时启动计数。每识别到key[0]时,flag反转。
3.4设计清零
-
在结束计数的条件做修改,当它计满或者按下key[1]的时候则计数器清零。
-
后来我才想到,好像复位就可以实现清零的操作。。。
3.5其余设计同上文电子时钟设计大同小异,可以参考前文
四.代码
- key_debounce.v
/**************************************功能介绍***********************************
Date : 2023年8月1日14:45:24
Author : Alegg xy.
Version : 1.0
Description: 四位按键消抖模块
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module key_debounce #(parameter WIDTH = 4,
parameter DELAY_20MS = 1000_000
)(
input clk ,
input rst_n ,
input [WIDTH-1:0] key_in ,
output [WIDTH-1:0] key_out
);
//---------<参数定义>---------------------------------------------------------
reg [WIDTH-1:0] key_out_r;
//状态机参数定义
localparam IDLE = 4'b0001,//空闲状态
FILETER_DOWN = 4'b0010,//按键按下抖动状态
HOLD_DOWN = 4'b0100,//按下稳定按下状态
FILTER_UP = 4'b1000;//按键释放抖动状态
//---------<内部信号定义>-----------------------------------------------------
reg [3:0] cstate ;//现态
reg [3:0] nstate ;//次态
reg [WIDTH-1:0] key_r0 ;//同步打拍
reg [WIDTH-1:0] key_r1 ;
reg [WIDTH-1:0] key_r2 ;
wire [WIDTH-1:0] n_edge ;//下降沿
wire [WIDTH-1:0] p_edge ;//上升沿
reg [19:0] cnt_20ms ;//20ms计数器
wire add_cnt_20ms;
wire end_cnt_20ms;
//状态转移条件定义
wire idle2filter_down ;
wire fiter_down2hold_down ;
wire hold_down2filter_up ;
wire filter_up2idle ;
//****************************************************************
//--cstate
//****************************************************************
//第一段:时序逻辑描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cstate <= IDLE;//复位的初始状态
end
else begin
cstate <= nstate;
end
end
//第二段:组合逻辑描述状态转移规律和状态转移条件
always @(*) begin
case(cstate)
IDLE : begin
if(idle2filter_down)begin
nstate = FILETER_DOWN;
end
else begin
nstate = cstate;
// state_n = IDLE;
end
end
FILETER_DOWN : begin
if(fiter_down2hold_down)begin
nstate = HOLD_DOWN;
end
else begin
nstate = cstate;
end
end
HOLD_DOWN : begin
if(hold_down2filter_up)begin
nstate = FILTER_UP;
end
else begin
nstate = cstate;
end
end
FILTER_UP : begin
if(filter_up2idle)begin
nstate = IDLE;
end
else begin
nstate = cstate;
end
end
default : nstate = IDLE;
endcase
end
assign idle2filter_down = cstate == IDLE && n_edge;
assign fiter_down2hold_down = cstate == FILETER_DOWN && end_cnt_20ms;
assign hold_down2filter_up = cstate == HOLD_DOWN && p_edge;
assign filter_up2idle = cstate == FILTER_UP && end_cnt_20ms;
//****************************************************************
//--n_edge、p_edge
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_r0 <= {WIDTH{1'b1}};
key_r1 <= {WIDTH{1'b1}};
key_r2 <= {WIDTH{1'b1}};
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
key_r2 <= key_r1;
end
end
assign n_edge = ~key_r1 & key_r2;
assign p_edge = ~key_r2 & key_r1;
//****************************************************************
//--cnt_20ms
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_20ms <= 'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 'd0;
end
else begin
cnt_20ms <= cnt_20ms + 1'b1;
end
end
end
assign add_cnt_20ms = cstate == FILETER_DOWN || cstate == FILTER_UP;
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == DELAY_20MS - 1;
//****************************************************************
//--key_out
//****************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
key_out_r <= 'd0;
end
else if(hold_down2filter_up)begin
key_out_r <= ~key_r2;
end
else begin
key_out_r <= 'd0;
end
end
assign key_out = key_out_r;
endmodule
- time_count.v
/**************************************功能介绍***********************************
Date :2023年8月1日14:46:12
Author : Alegg xy.
Version : 1.0
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module time_count(
input clk ,
input rst_n ,
input [3:0] key ,
output [6:0] ms ,
output [5:0] sec ,
output [5:0] min
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_10ms = 19'd500_000,
MAX_ms = 7'd99,
MAX_sec = 6'd59,
MAX_min = 6'd59;
reg flag;//flag为0暂停,flag为1启动计数
//---------<内部信号定义>-----------------------------------------------------
//定义flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
flag <= 1'b0;
end
else if(key[0])begin
flag <= ~flag;
end
else begin
flag <= flag;
end
end
//10ms计数器
reg [18:0] cnt_10ms ;
wire add_cnt_10ms ;
wire end_cnt_10ms ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_10ms <= 19'd0;
end
else if(add_cnt_10ms)begin
if(end_cnt_10ms)begin
cnt_10ms <= 19'd0;
end
else begin
cnt_10ms <= cnt_10ms + 1'd1;
end
end
end
assign add_cnt_10ms = flag;
assign end_cnt_10ms = (add_cnt_10ms && cnt_10ms == MAX_10ms - 1'd1) || key[1];
//ms计数器,每计满10ms,ms位就加一,最大值为99
reg [6:0] cnt_ms ;
wire add_cnt_ms ;
wire end_cnt_ms ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_ms <= 7'd0;
end
else if(add_cnt_ms)begin
if(end_cnt_ms)begin
cnt_ms <= 7'd0;
end
else begin
cnt_ms <= cnt_ms + 1'd1;
end
end
end
assign add_cnt_ms = end_cnt_10ms;
assign end_cnt_ms = (add_cnt_ms && cnt_ms == MAX_ms) || key[1];
//sec计数器,每计满100个10ms,sec位就加一,最大值为59
reg [5:0] cnt_sec ;
wire add_cnt_sec ;
wire end_cnt_sec ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_sec <= 6'd0;
end
else if(add_cnt_sec)begin
if(end_cnt_sec)begin
cnt_sec <= 6'd0;
end
else begin
cnt_sec <= cnt_sec + 1'd1;
end
end
end
assign add_cnt_sec = end_cnt_ms;
assign end_cnt_sec = (add_cnt_sec && cnt_sec == MAX_sec) || key[1];
//min计数器,每计满60s,min位就加一,最大值为59
reg [5:0] cnt_min ;
wire add_cnt_min ;
wire end_cnt_min ;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_min <= 6'd0;
end
else if(add_cnt_min)begin
if(end_cnt_min)begin
cnt_min <= 6'd0;
end
else begin
cnt_min <= cnt_min + 1'd1;
end
end
end
assign add_cnt_min = end_cnt_sec;
assign end_cnt_min = (add_cnt_min && cnt_min == MAX_min) || key[1];
assign ms = cnt_ms;
assign sec = cnt_sec;
assign min = cnt_min;
endmodule
- seg_dirver.v
/**************************************功能介绍***********************************
Date :2023年8月1日15:21:33
Author : Alegg xy.
Version : 1.0
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module seg_dirver(
input clk ,
input rst_n ,
input [6:0] ms ,
input [5:0] sec ,
input [4:0] min ,
output reg [5:0] sel ,
output reg [7:0] seg
);
//---------<参数定义>---------------------------------------------------------
parameter MAX_1ms = 16'd50000;
reg [3:0] data;//存取该位数值
//---------<内部信号定义>-----------------------------------------------------
//1ms计数器
reg [15:0] cnt_1ms ;
wire add_cnt_1ms ;
wire end_cnt_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'd1;
end
end
end
assign add_cnt_1ms = 1'b1;
assign end_cnt_1ms = add_cnt_1ms && cnt_1ms == MAX_1ms - 1'd1;
//数码管位选
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sel <= 6'b111_110;//循环移位实现时,需要给位选赋初值
end
else if(end_cnt_1ms)begin
sel <= {sel[4:0],sel[5]};//循环左移
end
end
always @(*)begin
case (sel)
6'b111_110 :data = min / 10;
6'b111_101 :data = min % 10;
6'b111_011 :data = sec / 10;
6'b110_111 :data = sec % 10;
6'b101_111 :data = ms / 10;
6'b011_111 :data = ms % 10;
default: data = 0;
endcase
end
always @(*)begin
case (data)
4'd0:seg = 8'b1100_0000;
4'd1:seg = 8'b1111_1001;
4'd2:seg = 8'b1010_0100;
4'd3:seg = 8'b1011_0000;
4'd4:seg = 8'b1001_1001;
4'd5:seg = 8'b1001_0010;
4'd6:seg = 8'b1000_0010;
4'd7:seg = 8'b1111_1000;
4'd8:seg = 8'b1000_0000;
4'd9:seg = 8'b1001_0000;
default: seg = 8'b1100_0000;
endcase
end
endmodule
- top_seg_dirver.v
/**************************************功能介绍***********************************
Date :
Author : Alegg xy.
Version :
Description:
*********************************************************************************/
//---------<模块及端口声名>------------------------------------------------------
module top_seg_dirver(
input clk ,
input rst_n ,
input [3:0] key_in ,
output [5:0] sel ,
output [7:0] seg
);
//---------<参数定义>---------------------------------------------------------
wire [3:0] key_link;
wire [6:0] ms_link;
wire [5:0] sec_link;
wire [5:0] min_link;
//---------<内部信号定义>-----------------------------------------------------
key_debounce u_key_debounce(
.clk (clk),
.rst_n (rst_n),
.key_in (key_in),
.key_out (key_link)
);
time_count u_time_count(
.clk (clk),
.rst_n (rst_n),
.key (key_link),
.ms (ms_link),
.sec (sec_link),
.min (min_link)
);
seg_dirver u_seg_dirver(
.clk (clk),
.rst_n (rst_n),
.ms (ms_link),
.sec (sec_link),
.min (min_link),
.sel (sel),
.seg (seg)
);
endmodule