可调数字时钟

前言

        前一章节我们讲述了基于FPGA的数字钟设计,整个工程只有外部时钟输入和复位按键,虽然实现了数字钟的逻辑,但是该项目是自动化的,不能像我们生活中调表一样去控制数字钟,本章节将介绍如何通过按键去调数字钟。

正文

一、 可调数字时钟

        1.项目需求

        在现有的设计基础上添加可以调时功能,使设计完成的数字钟能够进行调节小时,调节分钟,调节秒,实现可以对时功能。

        2.技术介绍

        利用三个独立按键实现调时功能,第一个独立按键实现控制调节模式选择功能,其对应的调节模式包含:调节小时十位,调节小时个位,调节分钟十位,调节分钟个位,调节秒十位,调节秒个位;利用另外两个按键实现加减功能。

       利用有限状态机进行设计描述,根据调节模式的设置,将对时功能实现设置为7个状态,分别为:

1:  正常显示状态,对加减按键进行操作时产生的标志信号无效。

2:  调节小时十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_hour1_add和flag_hour1_sub;产生一个小时十位数据闪烁使能信号bilnk_hour1_en,

3:  调节小时个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时个位数据加标志信号flag_hour0_add和flag_hour0_sub; 产生一个小时个位数据闪烁使能信号bilnk_hour0_en,

4:  调节分钟十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_min1_add和flag_min1_sub; 产生一个分钟十位数据闪烁使能信号bilnk_min1_en,

5:  调节分钟个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_min0_add和flag_min0_sub; 产生一个分钟个位数据闪烁使能信号bilnk_min0_en,

6:  调节秒十位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_sec1_add和flag_sec1_sub; 产生一个秒十位数据闪烁使能信号bilnk_sec1_en,

7:  调节秒个位数据状态,对加减按键进行操作时产生的标志信号有效,对应的就会产生小时十位数据加标志信号flag_sec0_add和flag_sec0_sub; 产生一个秒个位数据闪烁使能信号bilnk_sec0_en。

        需要在234567状态里产生1秒清零使能信号one_sec_en,该使能信号在正常显示状态时为低电平,当进入到调节时间状态时秒清零使能信号one_sec_clren拉高。

        3.顶层架构

        该项目是以自动数字钟系统为基础,在其上添加按键消抖和显示数据更改模块,整体框架:

按键消抖控制模块:

数字钟控制模块:接收到对应的信号后变化对应位的数据

        4.端口描述

clk时钟信号(50Mhz)
rst_n复位按键(低电平有效)
key_adjust模式切换按键
key_add加操作按键
key_sub减操作按键
[2:0] sel六位八段数码管位选信号
[7:0] seg六位八段数码管段选信号

二、代码验证

        blink_ctrl模块:当切换到调时间模式时,为区别当前选中位,使当前选中位数码管闪烁

module blink_ctrl(

	input clk,
	input rst_n,
	input blink_en,//模块使能信号(1有效)
	input [3:0] data,//当前位现在的数据(4位二进制数)
	
	output [3:0] show_data//闪烁输出数据
);

reg [24:0] cnt;
parameter MAX = 25'd25_000_000;//闪烁延时间隔计数器
reg selc_en;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		cnt <= 25'd0;
	else
		if(blink_en == 1)
			if(cnt < MAX - 1)//标准计数器,产生延时闪烁效果
				cnt <= cnt + 25'd1;
			else 
				cnt <= 25'd0;
		else
			cnt <= 25'd0;
end 

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		selc_en <= 1'b0;
	else
		if(blink_en == 1)
			if(cnt == MAX - 1)
				selc_en <= ~selc_en;
//每经过闪烁延时间隔计数器selc_en取反一次
//相当于产生一个新的时钟
			else 
				selc_en <= selc_en;
		else
			selc_en <= 1'b0;
end 

assign show_data = (selc_en == 0)?data:4'hf;
//在新时钟的低电平段传出数据,高电平段输出全部拉高,即在低电平显示数据,高电平段熄灭

endmodule 

        hour0_ctrl模块:小时个位控制模块,产生小时个位数据,并在调节小时个位模式下对加减按键操作进行对应的处理,产生新的小时个位数据并输出。

module hour0_ctrl(

	input clk,
	input rst_n,
	input [3:0]hour1,//当前小时十位数据
	input flag,//分钟十位产生的进位信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] hour0,//产生的新的小时个位数据
	output reg flag_10hour//小时个位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		hour0 <= 4'd0;
	else
		if(hour1 < 2)//小时十位数据小于2的时候,个位最大是9
			begin
				if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
					if(hour0 < 9)//最大到9,在加后跳转到0
						hour0 <= hour0 + 4'd1;//有进位或加按键操作时,数据加1
					else
						hour0 <= 4'd0;
				else
					if(flag_sub == 1)
						if(hour0 == 0)//最小到0,再减后跳转到9
							hour0 <= 4'd9;
						else
							hour0 <= hour0 - 4'd1;//有减按键操作时,数据减1
					else
						hour0 <= hour0;
			end 
		else//等于2时,小时个位数据最大是3
			begin
				if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
					if(hour0 < 3)//最大到3,在加后跳转到0
						hour0 <= hour0 + 4'd1;//有进位或加按键操作时,数据加1
					else
						hour0 <= 4'd0;
				else
					if(flag_sub == 1)
						if(hour0 == 0)//最小到0,再减后跳转到3
							hour0 <= 4'd3;
						else
							hour0 <= hour0 - 4'd1;//有减按键操作时,数据减1
					else
						hour0 <= hour0;
			end 			
end 

always @(*) begin//组合逻辑,产生进位信号
	if(rst_n == 0)
		flag_10hour <= 1'b0;
	else
		if(hour1 < 2)//小时十位数据小于2的时候,个位最大是9
			if((hour0 == 9)&&(flag == 1))
				flag_10hour <= 1'b1;
			else
				flag_10hour <= 1'b0;
		else//等于2时,小时个位数据最大是3
			if((hour0 == 3)&&(flag == 1))
				flag_10hour <= 1'b1;
			else
				flag_10hour <= 1'b0;			
end 

endmodule 

        hour1_ctrl模块:小时十位数据控制模块,产生小时十位数据,并在调节小时十位模式下对加减按键操作进行对应的处理,产生新的小时十位数据并输出。

module hour1_ctrl(

	input clk,
	input rst_n,
	input flag,//小时个位产生的进位信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] hour1//小时十位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		hour1 <= 4'd0;
	else
		if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
			if(hour1 < 2)//小时十位数据小于2的时,有进位或加按键操作时,数据加1
				hour1 <= hour1 + 4'd1;
			else
				hour1 <= 4'd0;//超过2直接到0
		else
			if(flag_sub == 1)
				if(hour1 == 0)
					hour1 <= 4'd2;//减操作到0后,再减到2
				else
					hour1 <= hour1 - 4'd1;
			else
				hour1 <= hour1;
end 

endmodule 

        min0_ctrl模块:分钟个位数据控制模块,产生分钟个位数据,并在调节分钟个位模式下对加减按键操作进行对应的处理,产生新的分钟个位数据并输出。

module min0_ctrl(

	input clk,
	input rst_n,
	input flag,//秒十位进位信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] min0,//产生的新的分钟个位数据
	output flag_10min//分钟个位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		min0 <= 4'd0;
	else
		if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
			if(min0 < 9)//最大到9,在加后跳转到0
				min0 <= min0 + 4'd1;//有进位或加按键操作时,数据加1
			else
				min0 <= 4'd0;
		else
			if(flag_sub == 1)
				if(min0 == 0)//最小到0,再减后跳转到9
					min0 <= 4'd9;
				else
					min0 <= min0 - 4'd1;//有减按键操作时,数据减1
			else
				min0 <= min0;
end 

assign flag_10min = ((min0 == 9)&&(flag == 1))?1'b1:1'b0;
//进位信号是秒十位进位信号到来并且当前分钟个位为9时产生一次进位

endmodule 

        min1_ctrl模块:分钟十位数据控制模块,产生分钟十位数据,并在调节分钟十位模式下对加减按键操作进行对应的处理,产生新的分钟十位数据并输出。

module min1_ctrl(

	input clk,
	input rst_n,
	input flag,//分钟个位产生的进位信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] min1,//产生的新的分钟十位数据
	output flag_hour//分钟十位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		min1 <= 4'd0;
	else
		if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
			if(min1 < 5)//最大到5,在加后跳转到0
				min1 <= min1 + 4'd1;//有进位或加按键操作时,数据加1
			else
				min1 <= 4'd0;
		else
			if(flag_sub == 1)
				if(min1 == 0)//最小到0,再减后跳转到5
					min1 <= 4'd5;
				else
					min1 <= min1 - 4'd1;//有减按键操作时,数据减1
			else
				min1 <= min1;
end 

assign flag_hour = ((min1 == 5)&&(flag == 1))?1'b1:1'b0;
//进位信号是分钟个位位进位信号到来并且当前分钟十位为9时产生一次进位

endmodule 

        one_sec模块: 一秒信号产生模块,产生1S的时钟信号,精确度纳秒级

module one_sec(

	input clk,
	input rst_n,
	input clr_en,//1s信号使能信号
//(按键产生的有效信号,1时1s信号停止产生,0时产生1s信号)
	
	output flag//1s信号
);

reg [25:0] cnt;
parameter MAX = 26'd50_000_000;

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		cnt <= 26'd0;
	else
		if(clr_en == 1)
			cnt <= 26'd0;
		else
			if(cnt < MAX - 1)
				cnt <= cnt + 26'd1;
			else
				cnt <= 26'd0;
end 

assign flag = ((cnt == MAX - 1)&&(clr_en == 0))?1'b1:1'b0;
//计数器记到最大值,下一个时钟信号到来时产生1s信号

endmodule 

        sec0_ctrl模块:秒个位数据控制模块,产生秒个位数据,并在调节秒个位模式下对加减按键操作进行对应的处理,产生新的秒个位数据并输出。

module sec0_ctrl(

	input clk,
	input rst_n,
	input flag,//1s信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] sec0,//产生的新的秒个位数据
	output flag_10sec//秒个位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		sec0 <= 4'd0;
	else
		if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
			if(sec0 < 9)//最大到9,在加后跳转到0
				sec0 <= sec0 + 4'd1;//有进位或加按键操作时,数据加1
			else
				sec0 <= 4'd0;
		else
			if(flag_sub == 1)
				if(sec0 == 0)//最小到0,再减后跳转到9
					sec0 <= 4'd9;
				else
					sec0 <= sec0 - 4'd1;//有减按键操作时,数据减1
			else
				sec0 <= sec0;
end 

assign flag_10sec = ((sec0 == 9)&&(flag == 1))?1'b1:1'b0;
//进位信号是1s信号到来并且当前秒个位为9时产生一次进位

endmodule 

        sec1_ctrl模块:秒十位数据控制模块,产生秒十位数据,并在调节秒十位模式下对加减按键操作进行对应的处理,产生新的秒十位数据并输出。

module sec1_ctrl(

	input clk,
	input rst_n,
	input flag,//秒十位产生的进位信号
	input flag_add,//加按键产生的有效信号
	input flag_sub,//减按键产生的有效信号
	
	output reg [3:0] sec1,//产生的新的秒十位数据
	output flag_min//秒十位进位信号
);

always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		sec1 <= 4'd0;
	else
		if((flag == 1)||(flag_add == 1))//或操作,假设所有情况,避免数据出错
			if(sec1 < 5)//最大到5,在加后跳转到0
				sec1 <= sec1 + 4'd1;//有进位或加按键操作时,数据加1
			else
				sec1 <= 4'd0;
		else
			if(flag_sub == 1)
				if(sec1 == 0)//最小到0,再减后跳转到5
					sec1 <= 4'd5;
				else
					sec1 <= sec1 - 4'd1;//有减按键操作时,数据减1
			else
				sec1 <= sec1;
end 

assign flag_min = ((sec1 == 5)&&(flag == 1))?1'b1:1'b0;
//进位信号是秒个位进位信号到来并且当前秒十位为5时产生一次进位

endmodule 

        time_ctrl模块:数字钟数据连接顶层,连线作用

module time_ctrl(
	
	input clk,
	input rst_n,
	input one_sec_clren,
	input flag_sec0_add,
	input flag_sec0_sub,
	input blink_sec0_en,
	input flag_sec1_add,
	input flag_sec1_sub,
	input blink_sec1_en,
	input flag_min0_add,
	input flag_min0_sub,
	input blink_min0_en,
	input flag_min1_add,
	input flag_min1_sub,
	input blink_min1_en,
	input flag_hour0_add,
	input flag_hour0_sub,
	input blink_hour0_en,
	input flag_hour1_add,
	input flag_hour1_sub,
	input blink_hour1_en,
	
	output [23:0] data_out
);

wire flag;
wire [3:0] sec0;
wire flag_10sec;
wire [3:0] sec1;
wire flag_min;
wire [3:0] min0;
wire flag_10min;
wire [3:0] min1;
wire flag_hour;
wire [3:0] hour1;
wire [3:0] hour0;
wire flag_10hour;
wire [3:0] show_sec0;
wire [3:0] show_sec1;
wire [3:0] show_min0;
wire [3:0] show_min1;
wire [3:0] show_hour0;
wire [3:0] show_hour1;

one_sec one_sec_inst(

			.clk(clk),
			.rst_n(rst_n),
			.clr_en(one_sec_clren),
			
			.flag(flag)
);

sec0_ctrl sec0_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag(flag),
			.flag_add(flag_sec0_add),
			.flag_sub(flag_sec0_sub),
			
			.sec0(sec0),
			.flag_10sec(flag_10sec)
);

blink_ctrl  blink_ctrl_inst1(//数码管秒个位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(sec0),
			.blink_en(blink_sec0_en),
			
			.show_data(show_sec0)
);


sec1_ctrl sec1_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag(flag_10sec),
			.flag_add(flag_sec1_add),
			.flag_sub(flag_sec1_sub),
			
			.sec1(sec1),
			.flag_min(flag_min)
);

blink_ctrl  blink_ctrl_inst2(//数码管秒十位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(sec1),
			.blink_en(blink_sec1_en),
			
			.show_data(show_sec1)
);

min0_ctrl min0_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag(flag_min),
			.flag_add(flag_min0_add),
			.flag_sub(flag_min0_sub),
			
			.min0(min0),
			.flag_10min(flag_10min)
);

blink_ctrl  blink_ctrl_inst3(//数码管分钟个位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(min0),
			.blink_en(blink_min0_en),
			
			.show_data(show_min0)
);

min1_ctrl min1_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag(flag_10min),
			.flag_add(flag_min1_add),
			.flag_sub(flag_min1_sub),
			
			.min1(min1),
			.flag_hour(flag_hour)
);

blink_ctrl  blink_ctrl_inst4(//数码管分钟十位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(min1),
			.blink_en(blink_min1_en),
			
			.show_data(show_min1)
);

hour0_ctrl hour0_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.hour1(hour1),
			.flag(flag_hour),
			.flag_add(flag_hour0_add),
			.flag_sub(flag_hour0_sub),
			
			.hour0(hour0),
			.flag_10hour(flag_10hour)
);

blink_ctrl  blink_ctrl_inst5(//数码管小时个位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(hour0),
			.blink_en(blink_hour0_en),
			
			.show_data(show_hour0)
);

hour1_ctrl hour1_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag(flag_10hour),
			.flag_add(flag_hour1_add),
			.flag_sub(flag_hour1_sub),
			
			.hour1(hour1)
);

blink_ctrl  blink_ctrl_inst6(//数码管小时十位数据闪烁输出

			.clk(clk),
			.rst_n(rst_n),
			.data(hour1),
			.blink_en(blink_hour1_en),
			
			.show_data(show_hour1)
);

assign data_out = {show_hour1,show_hour0,show_min1,show_min0,show_sec1,show_sec0};
//时间数据融合

endmodule 

        seg_driver模块:数码管驱动模块,接收时间数据并按固定方式输出到数码管上。

module seg_driver(
 
	input clk,
	input rst_n,
	input [23:0] data_in,//接收数字时钟信号
	
	output reg [2:0] sel,//位选信号
	output reg [7:0] seg//段选信号
);
 
reg [18:0] cnt;//1MS
parameter MAX = 19'd50_000;
//20ns记50000次
//产生1ms的延时
//位选信号1ms变换一次
reg [3:0] temp;
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		cnt <= 19'd0;
	else
		if(cnt < MAX - 1)//产生1ms的延时
			cnt <= cnt + 19'd1;
		else
			cnt <= 19'd0;
end 
 
//数码管位选信号控制逻辑
always @(posedge clk,negedge rst_n)
begin
	if(rst_n == 0)
		sel <= 3'd0;
	else
		if(cnt == MAX - 1)//位选信号6位,1ms的延时到变化一次
			if(sel < 5)
				sel <= sel + 3'd1;
			else 
				sel <= 3'd0;
		else 
			sel <= sel;	
end 
always @(*) begin
	if(rst_n == 0)
		temp <= 4'd0;
	else
		case(sel)//位选信号到将数据输出到对应输出位上
			3'd0	:	temp <= data_in[23:20];
			3'd1	:	temp <= data_in[19:16];
			3'd2	:	temp <= data_in[15:12];
			3'd3	:	temp <= data_in[11:8];
			3'd4	:	temp <= data_in[7:4];
			3'd5	:	temp <= data_in[3:0];
			default	:	temp <= 4'd0;
		endcase
end 
always @(*) begin
	if(rst_n == 0)
		seg <= 8'hff;
	else
		case(temp)//段选信号,对应的数码管信号
			4'd0	:	seg <= 8'b1100_0000;//0
			4'd1	:	seg <= 8'b1111_1001;//1
			4'd2	:	seg <= 8'b1010_0100;//2
			4'd3	:	seg <= 8'b1011_0000;//3
			4'd4	:	seg <= 8'b1001_1001;//4
			4'd5	:	seg <= 8'b1001_0010;//5
			4'd6	:	seg <= 8'b1000_0010;//6
			4'd7	:	seg <= 8'b1111_1000;//7
			4'd8	:	seg <= 8'b1000_0000;//8
			4'd9	:	seg <= 8'b1001_0000;//9
			4'd10	:	seg <= 8'b1000_1000;//A
			4'd11	:	seg <= 8'b1000_0011;//b
			4'd12	:	seg <= 8'b1100_0110;//C
			4'd13	:	seg <= 8'b1010_0001;//d
			4'd14	:	seg <= 8'b1000_0110;//E
			4'd15	:	seg <= 8'b1000_1110;//F
			default	:	seg <= 8'hff;
		endcase 
end 
endmodule 

        jitter_ctrl模块:按键消抖模块,产生按键有效信号。具体参考独立按键消抖

module jitter_ctrl(

	input clk,
	input rst_n,
	input key,
	
	output reg flag
);

reg [3:0] n_state;
reg [3:0] c_state;

parameter s0 = 4'b0001;//空闲状态
parameter s1 = 4'b0010;//下抖动状态
parameter s2 = 4'b0100;//稳定状态
parameter s3 = 4'b1000;//上抖动状态

reg [18:0] cnt;
parameter MAX = 19'd500_000;
//FSM1
always @(posedge clk,negedge rst_n)//状态传递
begin
	if(rst_n == 0)
		c_state <= s0;
	else
		c_state <= n_state;
end 

//FSM2
always @(*)//描述状态转移
begin
	if(rst_n == 0)
		n_state <= s0;
	else
		case(c_state)
			s0	:	begin
						if(key == 0)
							n_state <= s1;
						else
							n_state <= s0;
					end 
			s1	:	begin
						if(cnt == MAX - 1)//下抖动延时
							n_state <= s2;
						else
							n_state <= s1;
					end 
			s2	:	begin
						if(key == 1)
							n_state <= s3;
						else
							n_state <= s2;
					end 					
			s3	:	begin
						if(cnt == MAX - 1)//上抖动延时
							n_state <= s0;
						else
							n_state <= s3;
					end 		
			default	:	n_state <= s0;
		endcase
end 

//FSM3
always @(posedge clk,negedge rst_n)//描述状态输出
begin
	if(rst_n == 0)
		begin
			cnt <= 19'd0;
			flag <= 1'b0;
			//key_en <= 1'b0;
		end
	else
		case(c_state)
			s0	:	begin 
						cnt <= 19'd0;
						flag <= 1'b0;
						//key_en <= 1'b0; 
					end 
			s1	:	begin
						if(cnt < MAX - 1)
							cnt <= cnt + 19'd1;
						else
							cnt <= 19'd0;
							
						if(cnt == MAX - 1)
							begin
								flag <= 1'b1;
								//key_en <= 1'b1; 
							end 
						else
							begin
								//key_en <= 1'b0; 
								flag <= 1'b0;
							end 
					end 
			s2	:	begin
						flag <= 1'b0;
						//key_en <= 1'b1;
						cnt <= 19'd0;
					end 
			s3	:	begin
						flag <= 1'b0;
					//key_en <= 1'b0;
						if(cnt < MAX - 1)
							cnt <= cnt + 19'd1;
						else
							cnt <= 19'd0;
					end 
			default	:	begin
								cnt <= 19'd0;
								flag <= 1'b0;
								//key_en <= 1'b0;
							end 
		endcase 	
end 

endmodule 

        key_top模块:按键顶层连线模块,读取按键状态并传递给按键消抖模块,产生按键有效信号。

module key_top(

	input clk,
	input rst_n,
	input key_adjust,
	input key_add,
	input key_sub,
	
	output flag_adjust,
	output flag_add,
	output flag_sub
);

jitter_ctrl  jitter_ctrl_inst1(//模式切换按键

			.clk(clk),
			.rst_n(rst_n),
			.key(key_adjust),
			
			.flag(flag_adjust)
);

jitter_ctrl  jitter_ctrl_inst2(//数据加按键

			.clk(clk),
			.rst_n(rst_n),
			.key(key_add),
			
			.flag(flag_add)
);

jitter_ctrl  jitter_ctrl_inst3(//数据减按键

			.clk(clk),
			.rst_n(rst_n),
			.key(key_sub),
			
			.flag(flag_sub)
);

endmodule 

        adjust_ctrl模块:模式切换模块,有限状态机,对目标位进行使能并将按键操作有效信号传递给对应模块。

module adjust_ctrl(

	input clk,
	input rst_n,
	input flag_adjust,
	input flag_add,
	input flag_sub,
	
	output reg one_sec_clren,
	output reg flag_sec0_add,
	output reg flag_sec0_sub,
	output reg blink_sec0_en,
	output reg flag_sec1_add,
	output reg flag_sec1_sub,
	output reg blink_sec1_en,
	output reg flag_min0_add,
	output reg flag_min0_sub,
	output reg blink_min0_en,
	output reg flag_min1_add,
	output reg flag_min1_sub,
	output reg blink_min1_en,
	output reg flag_hour0_add,
	output reg flag_hour0_sub,
	output reg blink_hour0_en,
	output reg flag_hour1_add,
	output reg flag_hour1_sub,
	output reg blink_hour1_en
);

reg [2:0] c_state;
reg [2:0] n_state;
parameter s0 = 3'd0;//正常显示状态
parameter s1 = 3'd1;//调节小时十位数据状态
parameter s2 = 3'd2;//调节小时个位数据状态
parameter s3 = 3'd3;//调节分钟十位数据状态
parameter s4 = 3'd4;//调节分钟个位数据状态
parameter s5 = 3'd5;//调节秒十位数据状态
parameter s6 = 3'd6;//调节秒个位数据状态

always @(posedge clk,negedge rst_n)//状态传递
begin
	if(rst_n == 0)
		c_state <= s0;
	else
		c_state <= n_state;
end 

always @(*) begin//描述状态转移
	if(rst_n == 0)
		n_state <= s0;
	else
		case(c_state)
			s0	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s1;
						else
							n_state <= s0;
					end 
			s1	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s2;
						else
							n_state <= s1;
					end 
			s2	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s3;
						else
							n_state <= s2;
					end
			s3	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s4;
						else
							n_state <= s3;
					end
			s4	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s5;
						else
							n_state <= s4;
					end 
			s5	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s6;
						else
							n_state <= s5;
					end 
			s6	:	begin
						if(flag_adjust == 1)//模式切换按键操作
							n_state <= s0;
						else
							n_state <= s6;
					end   					
			default	:	n_state <= s0;
		endcase
end 

always @(posedge clk,negedge rst_n)//描述状态输出
begin
	if(rst_n == 0)
		begin
			one_sec_clren <= 1'b0;
			flag_sec0_add <= 1'b0;
			flag_sec0_sub <= 1'b0;
			blink_sec0_en <= 1'b0;
			flag_sec1_add <= 1'b0;
			flag_sec1_sub <= 1'b0;
			blink_sec1_en <= 1'b0;
			flag_min0_add <= 1'b0;
			flag_min0_sub <= 1'b0;
			blink_min0_en <= 1'b0;
			flag_min1_add <= 1'b0;
			flag_min1_sub <= 1'b0;
			blink_min1_en <= 1'b0;
			flag_hour0_add <= 1'b0;
			flag_hour0_sub <= 1'b0;
			blink_hour0_en <= 1'b0;
			flag_hour1_add <= 1'b0;
			flag_hour1_sub <= 1'b0;
			blink_hour1_en <= 1'b0;		
		end 
	else
		case(c_state)
			s0	:	begin
						one_sec_clren <= 1'b0;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;
						blink_hour1_en <= 1'b0;				
					end 
		s1	:	begin
						one_sec_clren <= 1'b1;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						blink_hour1_en <= 1'b1;
						flag_hour1_add <= flag_add;
						flag_hour1_sub <= flag_sub;				
					end 
		s2	:	begin
						one_sec_clren <= 1'b1;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;
						blink_hour1_en <= 1'b0;
						blink_hour0_en <= 1'b1;
						flag_hour0_add <= flag_add;
						flag_hour0_sub <= flag_sub;				
					end 
		s3	:	begin
						one_sec_clren <= 1'b1;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= flag_add;
						flag_min1_sub <= flag_sub;
						blink_min1_en <= 1'b1;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						blink_hour1_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;				
					end 
		s4	:	begin
						one_sec_clren <= 1'b1;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_min0_add <= flag_add;
						flag_min0_sub <= flag_sub;
						blink_min0_en <= 1'b1;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						blink_hour1_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;				
					end 
		s5	:	begin
						one_sec_clren <= 1'b1;
						flag_sec0_add <= 1'b0;
						flag_sec0_sub <= 1'b0;
						blink_sec0_en <= 1'b0;
						flag_sec1_add <= flag_add;
						flag_sec1_sub <= flag_sub;
						blink_sec1_en <= 1'b1;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						blink_hour1_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;				
					end
		s6	:	begin
						one_sec_clren <= 1'b1;
						flag_sec1_add <= 1'b0;
						flag_sec1_sub <= 1'b0;
						blink_sec1_en <= 1'b0;
						flag_sec0_add <= flag_add;
						flag_sec0_sub <= flag_sub;
						blink_sec0_en <= 1'b1;
						flag_min0_add <= 1'b0;
						flag_min0_sub <= 1'b0;
						blink_min0_en <= 1'b0;
						flag_min1_add <= 1'b0;
						flag_min1_sub <= 1'b0;
						blink_min1_en <= 1'b0;
						flag_hour0_add <= 1'b0;
						flag_hour0_sub <= 1'b0;
						blink_hour0_en <= 1'b0;
						blink_hour1_en <= 1'b0;
						flag_hour1_add <= 1'b0;
						flag_hour1_sub <= 1'b0;				
					end 											
			default	:	begin
								one_sec_clren <= 1'b0;
								flag_sec0_add <= 1'b0;
								flag_sec0_sub <= 1'b0;
								blink_sec0_en <= 1'b0;
								flag_sec1_add <= 1'b0;
								flag_sec1_sub <= 1'b0;
								blink_sec1_en <= 1'b0;
								flag_min0_add <= 1'b0;
								flag_min0_sub <= 1'b0;
								blink_min0_en <= 1'b0;
								flag_min1_add <= 1'b0;
								flag_min1_sub <= 1'b0;
								blink_min1_en <= 1'b0;
								flag_hour0_add <= 1'b0;
								flag_hour0_sub <= 1'b0;
								blink_hour0_en <= 1'b0;
								flag_hour1_add <= 1'b0;
								flag_hour1_sub <= 1'b0;
								blink_hour1_en <= 1'b0;									
							end 				
		endcase
end 

endmodule 

        digital_clock模块:顶层连线

module digital_clock(

	input clk,
	input rst_n,
	input key_adjust,
	input key_add,
	input key_sub,
	
	output [2:0] sel,
	output [7:0] seg
);

wire flag_adjust;//连线信号
wire flag_add;
wire flag_sub;
wire one_sec_clren;
wire flag_sec0_add;
wire flag_sec0_sub;
wire blink_sec0_en;
wire flag_sec1_add;
wire flag_sec1_sub;
wire blink_sec1_en;
wire flag_min0_add;
wire flag_min0_sub;
wire blink_min0_en;
wire flag_min1_add;
wire flag_min1_sub;
wire blink_min1_en;
wire flag_hour0_add;
wire flag_hour0_sub;
wire blink_hour0_en;
wire flag_hour1_add;
wire flag_hour1_sub;
wire blink_hour1_en;
wire [23:0] data_out;

key_top key_top_inst(

			.clk(clk),
			.rst_n(rst_n),
			.key_adjust(key_adjust),
			.key_add(key_add),
			.key_sub(key_sub),
			
			.flag_adjust(flag_adjust),
			.flag_add(flag_add),
			.flag_sub(flag_sub)
);

adjust_ctrl adjust_ctrl_inst(

			.clk(clk),
			.rst_n(rst_n),
			.flag_adjust(flag_adjust),
			.flag_add(flag_add),
			.flag_sub(flag_sub),
			
			.one_sec_clren(one_sec_clren),
			.flag_sec0_add(flag_sec0_add),
			.flag_sec0_sub(flag_sec0_sub),
			.blink_sec0_en(blink_sec0_en),
			.flag_sec1_add(flag_sec1_add),
			.flag_sec1_sub(flag_sec1_sub),
			.blink_sec1_en(blink_sec1_en),
			.flag_min0_add(flag_min0_add),
			.flag_min0_sub(flag_min0_sub),
			.blink_min0_en(blink_min0_en),
			.flag_min1_add(flag_min1_add),
			.flag_min1_sub(flag_min1_sub),
			.blink_min1_en(blink_min1_en),
			.flag_hour0_add(flag_hour0_add),
			.flag_hour0_sub(flag_hour0_sub),
			.blink_hour0_en(blink_hour0_en),
			.flag_hour1_add(flag_hour1_add),
			.flag_hour1_sub(flag_hour1_sub),
			.blink_hour1_en(blink_hour1_en)
);

time_ctrl time_ctrl_inst(
	
			.clk(clk),
			.rst_n(rst_n),
			.one_sec_clren(one_sec_clren),
			.flag_sec0_add(flag_sec0_add),
			.flag_sec0_sub(flag_sec0_sub),
			.blink_sec0_en(blink_sec0_en),
			.flag_sec1_add(flag_sec1_add),
			.flag_sec1_sub(flag_sec1_sub),
			.blink_sec1_en(blink_sec1_en),
			.flag_min0_add(flag_min0_add),
			.flag_min0_sub(flag_min0_sub),
			.blink_min0_en(blink_min0_en),
			.flag_min1_add(flag_min1_add),
			.flag_min1_sub(flag_min1_sub),
			.blink_min1_en(blink_min1_en),
			.flag_hour0_add(flag_hour0_add),
			.flag_hour0_sub(flag_hour0_sub),
			.blink_hour0_en(blink_hour0_en),
			.flag_hour1_add(flag_hour1_add),
			.flag_hour1_sub(flag_hour1_sub),
			.blink_hour1_en(blink_hour1_en),
			
			.data_out(data_out)
);

seg_driver seg_driver_inst(

			.clk(clk),
			.rst_n(rst_n),
			.data_in(data_out),
			
			.sel(sel),
			.seg(seg)
);

endmodule 

        仿真文件,对相应延时计数器处理后开始仿真

`timescale 1ns/1ps
module digital_clock_tb;

	reg clk;
	reg rst_n;
	reg key_adjust;
	reg key_add;
	reg key_sub;
	
	wire [2:0] sel;
	wire [7:0] seg;

digital_clock digital_clock_inst(

			.clk(clk),
			.rst_n(rst_n),
			.key_adjust(key_adjust),
			.key_add(key_add),
			.key_sub(key_sub),
			
			.sel(sel),
			.seg(seg)
);

initial clk = 1;
always #10 clk = ~clk;

initial begin
	rst_n = 0;
	key_adjust = 1;
	key_add = 1;
	key_sub = 1;
	#200
	rst_n = 1;
	#1000
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#1000	
	key_add = 0;
	#200
	key_add = 1;
	#1000
	key_sub = 0;
	#200
	key_sub = 1;
	#1000
	key_adjust = 0;
	#200
	key_adjust = 1;
	#10000
	$stop;
end 

endmodule 

三、仿真验证

        仿真文件

按下模式切换,观察sec0信号,1s信号产生停止,数据一直保持在1。时钟停止等待按键操作。

按下一次模式切换按键,调整小时十位数据,观察hour1信号,add一次后由0变1,sub两次后变到2,满足设计。下图为放大后的图像。

再按下一次模式切换按键,当前小时十位为2,调整小时个位数据,观察hour0信号,add一次后由0变1,sub两次后变到3,满足设计。

切换回自动模式

当前时刻为23:59:28,下一秒时间为23:59:29,

观察上图,23:59:59到00:00:00,自动模式下时间数据也是正确的。

参考资料

独立按键消抖

简易数字钟

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张明阳.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值