使设计的多功能计时器包括数字钟和电子秒表的功能。数字钟电路部分具有时、分、秒显示功能,以24小时循环计时;具有调节小时、分钟功能。电子秒表部分计时部分具有清零、启动计时、暂停计时功能,板子是黑金的AX301
源代码
顶层模块
module top (
input clk ,//系统时钟输入50M
input rst_n ,//复位,低电平有效
output [5:0] sel,
output [7:0] seg,
input key1 ,
input key2 ,
input key0
);
wire [23:0] hex_data;
wire _2_flag;
wire _1_flag;
wire _0_flag;
xiaodou xiaodou2(.clk(clk),.rst_n(rst_n),.key1(key2),.key_out1(_2_flag));
xiaodou xiaodou1(.clk(clk),.rst_n(rst_n),.key1(key1),.key_out1(_1_flag));
xiaodou xiaodou0(.clk(clk),.rst_n(rst_n),.key1(key0),.key_out1(_0_flag));
counter counter(
.clk (clk) ,//系统时钟输入50M
.rst_n(rst_n) ,//复位,低电平有效
.key_time(_2_flag),
.key_add(_1_flag) ,
.key_sub(_0_flag) ,
.hex_data(hex_data)
);
hex hex(
.clk (clk) ,//系统时钟输入50M
.rst_n(rst_n) ,//复位,低电平有效
.data_in(hex_data), //待显示数据
.point(6'b010100), //待显示数据
.sel(sel),
.seg(seg)
);
endmodule
计数秒表模块
module counter (
input clk ,//系统时钟输入50M
input rst_n ,//复位,低电平有效
input key_time ,
input key_add ,
input key_sub ,
output reg [23:0] hex_data
);
reg [25:0] cnt_ns ;
reg [6:0] cnt_s ;
reg [6:0] cnt_min ;
reg [6:0] cnt_h ;
reg [19:0] cnt_ns1 ;
reg [3:0] cnt_10ms;
reg [3:0] cnt_100ms;
reg [5:0] cnt_s1 ;
reg [5:0] cnt_min1 ;
//生成修改时钟时的闪烁效果
//时钟分频,生成0.5s的时钟
reg [25:0] cnt_ns_half ;
reg flag_half ;
reg out_flag;
reg [3:0] m_c_10ms;
reg [3:0] m_c_100ms;
reg [5:0] m_c_s1 ;
reg [5:0] m_c_min1;
reg [3:0] c_10ms;
reg [3:0] c_100ms;
reg [5:0] c_s1 ;
reg [5:0] c_min1;
wire[3:0] cnt_s_bcd1=cnt_s/10%10;
wire[3:0] cnt_min_bcd1=cnt_min/10%10;
wire[3:0] cnt_h_bcd1=cnt_h/10%10;
wire[3:0] cnt_s_bcd2=cnt_s%10;
wire[3:0] cnt_min_bcd2=cnt_min%10;
wire[3:0] cnt_h_bcd2=cnt_h%10;
wire[3:0] c_s1_bcd1=c_s1/10%10;
wire[3:0] c_s1_bcd2=c_s1%10;
wire[3:0] c_min1_bcd1=c_min1/10%10;
wire[3:0] c_min1_bcd2=c_min1%10;
localparam S_IDLE= 7'b0000_0001;
localparam S_TIME= 7'b0000_0010;
localparam S_HOUR= 7'b0000_0100;
localparam S_MIN= 7'b0000_1000;
localparam S_CNT= 7'b0001_0000;
localparam S_CNT_RUN= 7'b0010_0000;
localparam S_CNT_STOP= 7'b0100_0000;
reg [6:0]state;
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
state<=S_IDLE;
else
case(state)
S_IDLE:state<=S_TIME;
S_TIME:
if(key_time)
state<=S_HOUR;
else if(key_sub)
state<=S_CNT;
else
state<=S_TIME;
S_HOUR:
if(key_time)
state<=S_MIN;
else
state<=S_HOUR;
S_MIN:
if(key_time)
state<=S_TIME;
else
state<=S_MIN;
S_CNT:
if(key_sub)
state<=S_TIME;
else if(key_time)
state<=S_CNT_RUN;
else
state<=S_CNT;
S_CNT_RUN:
if(key_time)//清零
state<=S_CNT;
else if(key_add)
state<=S_CNT_STOP;
else if(key_sub)
state<=S_TIME;
else
state<=S_CNT_RUN;
S_CNT_STOP:
if(key_add)
state<=S_CNT_RUN;
else if(key_time)
state<=S_CNT;
else if(key_sub)
state<=S_TIME;
else
state<=S_CNT_STOP;
default:state<=S_IDLE;
endcase
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
hex_data<=24'h0;
else
case(state)
S_IDLE:hex_data<=24'h0;
S_TIME:hex_data<={cnt_h_bcd1,cnt_h_bcd2,cnt_min_bcd1,cnt_min_bcd2,cnt_s_bcd1,cnt_s_bcd2};
S_HOUR:
if(flag_half)
hex_data<={cnt_h_bcd1,cnt_h_bcd2,cnt_min_bcd1,cnt_min_bcd2,cnt_s_bcd1,cnt_s_bcd2};
else
hex_data<={8'hbb,cnt_min_bcd1,cnt_min_bcd2,cnt_s_bcd1,cnt_s_bcd2};
S_MIN:
if(flag_half)
hex_data<={cnt_h_bcd1,cnt_h_bcd2,cnt_min_bcd1,cnt_min_bcd2,cnt_s_bcd1,cnt_s_bcd2};
else
hex_data<={cnt_h_bcd1,cnt_h_bcd2,8'hbb,cnt_s_bcd1,cnt_s_bcd2};
S_CNT:hex_data<=24'h0;
S_CNT_RUN,S_CNT_STOP:hex_data<={c_min1_bcd1,c_min1_bcd2,c_s1_bcd1,c_s1_bcd2,c_100ms,c_10ms};
default:hex_data<=24'h0;
endcase
end
parameter HALFS_MAX=25_000_000-1;//0.5s
parameter NS_MAX=50_000_000-1;//1s
parameter S_MAX=60-1;//1min
parameter MIN_MAX=60-1;//1h
parameter H_MAX=24-1;//1day
//生成修改时钟时的闪烁效果
//时钟分频,生成0.5s的时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_ns_half<=0;
else if(state==S_MIN||state==S_HOUR)
begin
if(cnt_ns_half==HALFS_MAX)
cnt_ns_half<=0;
else
cnt_ns_half<=cnt_ns_half+1;
end
else
cnt_ns_half<=0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
flag_half<=0;
else if(cnt_ns_half==HALFS_MAX && (state==S_MIN||state==S_HOUR))
flag_half<=~flag_half;
end
//时钟分频,生成1s的时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_ns<=0;
else if(cnt_ns==NS_MAX)
cnt_ns<=0;
else
cnt_ns<=cnt_ns+1;
end
//秒计时
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_s<=0;
else if(cnt_s==S_MAX && cnt_ns==NS_MAX)
cnt_s<=0;
else if(cnt_ns==NS_MAX)
cnt_s<=cnt_s+1;
end
wire flag_1_min=cnt_s==S_MAX && cnt_ns==NS_MAX;
//分钟计时
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_min<=57;
else if(state==S_MIN)
begin
if(key_add && cnt_min<59)
cnt_min<=cnt_min+1;
else if(key_sub && cnt_min>0)
cnt_min<=cnt_min-1;
end
else if(cnt_min==MIN_MAX && flag_1_min)
cnt_min<=0;
else if(flag_1_min)
cnt_min<=cnt_min+1;
end
//小时计时
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_h<=5'd0;
else if(state==S_HOUR)
begin
if(key_add && cnt_h<5'd23)
cnt_h<=cnt_h+1;
else if(key_sub && cnt_h>5'd0)
cnt_h<=cnt_h-1;
end
else if(cnt_h==H_MAX && cnt_min==MIN_MAX && flag_1_min)
cnt_h<=5'd0;
else if(cnt_min==MIN_MAX && flag_1_min)
cnt_h<=cnt_h+1'b1;
end
//秒表
//毫秒数码管有2位,可以精确到10ms
// parameter MS_10MAX=5-1;//10ms
parameter MS_10MAX=500000-1;//10ms
//时钟分频,生成10ms的时钟
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_ns1<=20'd0;
else if(state==S_CNT)
cnt_ns1<=20'd0;
else if(state==S_CNT_RUN || state==S_CNT_STOP)
begin
if(cnt_ns1==MS_10MAX)
cnt_ns1<=20'd0;
else
cnt_ns1<=cnt_ns1+1;
end
else
cnt_ns1<=20'd0;
end
//10ms计时
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_10ms<=4'd0;
else if(state==S_CNT)
cnt_10ms<=5'd0;
else if(state==S_CNT_RUN || state==S_CNT_STOP)
begin
if(cnt_10ms==4'd9 && cnt_ns1==MS_10MAX)
cnt_10ms<=4'd0;
else if(cnt_ns1==MS_10MAX)
cnt_10ms<=cnt_10ms+1'b1;
end
else
cnt_10ms<=4'd0;
end
//100ms计时
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_100ms<=4'd0;
else if(state==S_CNT)
cnt_100ms<=4'd0;
else if(state==S_CNT_RUN || state==S_CNT_STOP)
begin
if(cnt_100ms==4'd9 && cnt_10ms==4'd9 && cnt_ns1==MS_10MAX)
cnt_100ms<=4'd0;
else if(cnt_10ms==4'd9 && cnt_ns1==MS_10MAX)
cnt_100ms<=cnt_100ms+1'b1;
end
else
cnt_100ms<=4'd0;
end
wire flag_1_s=(cnt_100ms==4'd9 && cnt_10ms==4'd9 && cnt_ns1==MS_10MAX);
//秒
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_s1<=6'd0;
else if(state==S_CNT)
cnt_s1<=4'd0;
else if(state==S_CNT_RUN || state==S_CNT_STOP)
begin
if(flag_1_s && cnt_s1==6'd59)
cnt_s1<=6'd0;
else if(flag_1_s)
cnt_s1<=cnt_s1+1'b1;
end
else
cnt_s1<=6'd0;
end
//分
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cnt_min1<=6'd0;
else if(state==S_CNT)
cnt_min1<=4'd0;
else if(state==S_CNT_RUN || state==S_CNT_STOP)
begin
if(flag_1_s && cnt_min1==6'd59 && cnt_s1==6'd59)
cnt_min1<=6'd0;
else if(flag_1_s && cnt_s1==6'd59)
cnt_min1<=cnt_min1+1'b1;
end
else
cnt_min1<=6'd0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
out_flag <=1'd0;
else if(state==S_CNT)
out_flag <=1'd0;
else if((state==S_CNT_RUN || state==S_CNT_STOP) && key_add)
out_flag<=~out_flag;
end
//输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
m_c_10ms <=4'd0;
m_c_100ms <=4'd0;
m_c_s1 <=4'd0;
m_c_min1 <=4'd0;
end
else if(state==S_CNT_RUN && key_add)
begin
m_c_10ms <=cnt_10ms ;
m_c_100ms <=cnt_100ms ;
m_c_s1 <=cnt_s1 ;
m_c_min1 <=cnt_min1 ;
end
end
//输出
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
begin
c_10ms <=4'd0;
c_100ms <=4'd0;
c_s1 <=4'd0;
c_min1 <=4'd0;
end
else if(out_flag)
begin
c_10ms <=m_c_10ms ;
c_100ms <=m_c_100ms ;
c_s1 <=m_c_s1 ;
c_min1 <=m_c_min1 ;
end
else
begin
c_10ms <=cnt_10ms ;
c_100ms <=cnt_100ms ;
c_s1 <=cnt_s1 ;
c_min1 <=cnt_min1 ;
end
end
endmodule
数码管驱动模块
module hex(
clk, //系统输入时钟
rst_n, //复位输入,低电平复位
data_in, //待显示数据
point, //待显示数据
sel,
seg
);
//----------------模块输入端口----------------
input clk; //系统输入时钟
input rst_n;
input [23:0]data_in; //待显示数据
input [5:0]point; //待显示数据
//----------------模块输出端口----------------
output [5:0]sel;
output [7:0]seg;
parameter CNT_MAX=50000;
reg [15:0]cnt;
reg flag;
reg nisp;
always@(posedge clk or negedge rst_n)
if(!rst_n)
cnt<=16'd0;
else if(cnt==CNT_MAX)
cnt<=16'd0;
else
cnt<=cnt+1'b1;
always@(posedge clk or negedge rst_n)
if(!rst_n)
flag<=1'd0;
else if(cnt==CNT_MAX)
flag<=1'd1;
else
flag<=1'd0;
reg [5:0]hex_sel;
reg [7:0]hex_seg;
always@(posedge clk or negedge rst_n)
if(!rst_n)
hex_sel<=4'h1;
else if(flag)
hex_sel<={hex_sel[4:0],hex_sel[5]};
reg [3:0]disp;
always@(*)
case(hex_sel)
6'b100000:begin nisp <=~point[5]; disp=data_in[23:20];end
6'b010000:begin nisp <=~point[4]; disp=data_in[19:16];end
6'b001000:begin nisp <=~point[3]; disp=data_in[15:12];end
6'b000100:begin nisp <=~point[2]; disp=data_in[11:8];end
6'b000010:begin nisp <=~point[1]; disp=data_in[7:4];end
6'b000001:begin nisp <=~point[0]; disp=data_in[3:0];end
default:begin nisp <=1'b1;disp=4'h0;end
endcase
always @ (disp) begin
case (disp)
4'h0 : hex_seg = {nisp,7'b1000000}; //显示数字 0
4'h1 : hex_seg = {nisp,7'b1111001}; //显示数字 1
4'h2 : hex_seg = {nisp,7'b0100100}; //显示数字 2
4'h3 : hex_seg = {nisp,7'b0110000}; //显示数字 3
4'h4 : hex_seg = {nisp,7'b0011001}; //显示数字 4
4'h5 : hex_seg = {nisp,7'b0010010}; //显示数字 5
4'h6 : hex_seg = {nisp,7'b0000010}; //显示数字 6
4'h7 : hex_seg = {nisp,7'b1111000}; //显示数字 7
4'h8 : hex_seg = {nisp,7'b0000000}; //显示数字 8
4'h9 : hex_seg = {nisp,7'b0010000}; //显示数字 9
4'ha : hex_seg = {nisp,7'b0111111}; //显示-
4'hb : hex_seg = {nisp,7'b1111111}; //不显示
// 4'ha : hex_seg = {nisp,7'b0001000}; //显示数字 9
// 4'hb : hex_seg = {nisp,7'b0000011}; //显示数字 9
// 4'hc : hex_seg = {nisp,7'b1000110}; //显示数字 9
// 4'hd : hex_seg = {nisp,7'b0100001}; //显示数字 9
// 4'he : hex_seg = {nisp,7'b0000110}; //显示数字 9
// 4'hf : hex_seg = {nisp,7'b0001110}; //显示数字 9
// 4'hb : seg = 8'b10111111; //显示负号(-)
default:hex_seg = {nisp,7'b1000000};
endcase
end
assign sel=~hex_sel;
assign seg=hex_seg;
endmodule
按键消抖模块
//--消抖模块
module xiaodou(
input clk,//系统时钟输入
input rst_n,//系统复位输入
input key1,//外部按键输入
output reg key_out1//消抖后的波形输出
);
///
reg [31:0] cnt1;
parameter CNT_MAX=32'd2000000;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
cnt1 <= 0;
else
begin
if(key1) //--当按键为1,即高电平的时候,说明没有按键按下(硬件特性),把计数寄存器清零,geo1的按键按下去时高电平,平时为低电平,因此输入时需要取反
cnt1 <=0;
else if(cnt1==CNT_MAX)//--如果上述条件不满足,即按键为低电平,说明有按键按下,此时我们开始计数,
cnt1 <=cnt1; //--因为毛刺的存在,按键低电平状态必须保持一段时间才能视为有效按下,几十毫秒是个可以接受的值
else //--系统时钟是100MHz,即10ns一周期,则2000000*10ns=20000000ns=20ms
cnt1 <=cnt1+1; //--此段进程的目的就是,按键过程中,如果存在毛刺,则把计数寄存器清零,直到按键电平稳定为低,
end //--有效计数时间能达到ms级别,我们认为按键有效
end
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
key_out1 <= 1'b0;
else
begin
if(cnt1==CNT_MAX-1)//--接上面的进程,按键电平稳定,计数时间能达到50ms的时候,我们产生按键脉冲,用于后续程序。
key_out1 <= 1'b1;
else
key_out1 <= 1'b0;
end
end
endmodule
演示视频