EDA实现电子时钟

电子时钟程序的实现 

摘要

        本文旨在实现一个基于电子时钟的程序,该程序通过7段数码管显示当前的时、分、秒,并利用4x4矩阵按键模拟调节时钟指令的输入。该电子时钟还具备整点报时功能,能够在整点时刻发出报时信号。实现过程中,首先通过7段数码管显示当前的时、分、秒,通过使用适当的控制电路和编程算法,将时间数据转换为相应的数码管段亮灭的信号,实现时间的可视化显示。其次,通过4x4矩阵按键来模拟调节时钟指令的输入。按键的功能包括时、分、秒的选择、复位、加时、减时等。通过编程算法,对按键输入进行检测和解析,从而实现对时钟的精确调节和控制。最后,为了增加时钟的实用性,设计了整点报时功能。当时钟显示的时间为整点时刻时,电子时钟会发出特定的报时信号。通过本课程设计,我们将深入了解电子时钟的原理和设计方法,掌握数字电路设计和编程的基本技巧,并能够将理论知识应用于实际项目中。此外,通过设计整点报时功能,我们还能够培养创新思维和实际应用能力。

关键词:电子时钟;7段数码管;4x4矩阵按键;整点报时功能;

1 绪论

        随着现代科技的不断进步,电子时钟已成为人们日常生活中常见的时间显示工具。其简洁、准确和可靠的特点,使其在家庭、学校、办公场所等各个领域得到广泛应用。在当今社会,人们对时间的准确性和精确度要求越来越高。传统的机械时钟存在精度低、操作不便等问题,而电子时钟则能够满足人们对时间显示的需求。通过数字电路和编程算法的结合,电子时钟能够实时、准确地显示时间,并提供丰富的功能和用户界面。因此,设计和实现一个功能完备的电子时钟程序具有重要的实际意义和研究价值。本文旨在设计和实现一个功能完备的电子时钟程序,通过数码管显示时、分、秒,利用矩阵按键模拟调节时钟指令的输入,并实现整点报时功能。通过本次设计,我们将能够掌握数字电路设计和编程的基本技巧,培养实际应用能力,并为电子时钟的改进和发展提供有益的参考。

1.1 本设计的目标及主要研究内容

        本文旨在介绍使用EDA(电子设计自动化)工具实现电子时钟程序的设计过程和结果。通过采用Verilog HDL语言编写代码,并借助EDA工具进行仿真、综合和布局布线,我们成功地设计和实现了一个功能齐全、稳定可靠的电子时钟,实现在7段数码管显示时、分、秒,使用4x4矩阵按键模拟调节时钟指令输入按键,并实现整点报时功能。按键功能包括但不限以下功能:选择、复位、+(时分秒加)、-(时分秒减)。本文将详细介绍电子时钟的功能需求分析、系统设计、EDA工具的使用以及硬件原型的验证。

1.2 系统的开发环境及运行环境

操作系统:Windows 10 跨平台操作系统。

开发工具: Quartus17.1 Lite,FPGA 黑金开发平台 AX301。

2 需求分析

        为了实现预计电子时钟的功能,我们将使用Quartus 17.1 Lite软件和FPGA黑金开发平台AX301进行设计和开发。下面是相关的需求分析,包括所需用到的器件和功能需求的描述。

2.1器件需求:

①FPGA黑金开发平台AX301:作为电子时钟的硬件平台,具备可编程逻辑单元和各种输入输出接口,提供灵活的数字电路设计和编程能力。

②7段数码管:用于显示时、分、秒的数字信息。

③4x4矩阵按键:用于模拟调节时钟指令的输入,包括选择时分秒、复位、加时、减时等功能。

2.2功能需求:

①显示时、分、秒:电子时钟需要能够实时获取当前的时、分、秒信息,并将其通过7段数码管进行显示。每个数码管应能够显示0-9的数字。

②4x4矩阵按键输入:电子时钟需要能够接收4x4矩阵按键的输入,并根据按键操作进行相应的功能调节。例如,选择时分秒、复位、加时、减时等功能。

③按键检测与解析:电子时钟需要能够检测按键的状态变化,并解析出相应的操作指令。通过编程算法,实现对时钟的精确调节和控制。

④整点报时功能:电子时钟需要能够判断当前时间是否为整点时刻,并在整点时刻触发报时信号。报时信号可以通过声音、LED灯等形式进行提示。

综上所述,为了实现电子时钟的功能,我们需要使用FPGA黑金开发平台AX301作为硬件平台,并连接7段数码管和4x4矩阵按键。在软件方面,使用Quartus 17.1 Lite进行数字电路设计和编程算法的开发。功能需求包括显示时、分、秒,按键输入检测与解析,以及整点报时功能的实现。通过合理设计和编程,将这些功能集成到电子时钟程序中,实现一个完整且可靠的电子时钟系统。

3 电子时钟实现程序逻辑结构及流程

3.1 程序逻辑结构:

图3.1程序逻辑结构

Figure 3.1 Program Logic Structure

3.2 流程图:

图3.2 电子时钟实现流程图

Figure 3.2 Realization flow chart of Electric clock

 4 电子时钟实现结果展示

时钟初始化,见图4.1:

图4.1 时钟初始化

Figure 4.1 Clock initialization

时钟时间调整见图4.2:

图4.2 时间调整

Figure 4.2 Time Adjustment

时钟整点可报时提醒:

时钟时间可重置见图4.3:

图4.3 时间重置

Figure 4.3 Time Reset

5 总结与展望

5.1总结

        经过两个周的准备,通过查阅资料,学习原理,编写程序,撰写报告,我完成了本次考查内容,成果由本报告呈现。

        本次设计旨在实现一个功能完备的电子时钟程序,并利用4x4矩阵按键模拟调节时钟指令的输入,实现时、分、秒的显示和调节功能,并添加整点报时功能。通过这个设计,我们深入学习了数字电路、编程算法等相关知识,并将其应用于实际项目中。

        在设计和实现过程中,我们首先对需求进行了详细的分析和讨论,明确了所需的器件和功能需求。我们选择了Quartus 17.1 Lite作为开发软件,FPGA黑金开发平台AX301作为硬件平台,以支持我们的电子时钟实现。

        通过分析需求,我们设计了主程序和几个子程序的逻辑结构,并使用流程图清晰地展示了程序的执行流程。主程序负责控制整个程序的执行流程,而子程序负责时钟显示、按键检测和整点报时等具体功能的实现。

        在具体的编程实现中,我们使用了合适的编程语言和算法,通过数字电路设计和编程技巧,实现了时、分、秒的显示和调节功能,并实现了整点报时的提示功能。我们还充分考虑了硬件平台的特点和限制,并进行了相应的适配和优化。

        通过本次设计,我们不仅加深了对数字电路和编程的理解,还培养了实际应用能力和创新思维。我们在项目实施中遇到了一些挑战,例如硬件和软件的兼容性、调试和优化的困难等,但通过不断的学习和努力,我们克服了这些问题,并最终成功地完成了电子时钟的设计和实现。

        总的来说,本次设计对我们的学习和成长具有重要意义。通过实践项目,我们不仅掌握了相关技术和工具的使用,还培养了解决问题的能力。我们对于所学知识的理解更加深入,并在实践中获得了宝贵的经验。我相信这次设计将为我们未来的学习和职业发展奠定坚实的基础。本系统基本实现了关键的功能模块,在功能上基本满足了用户的需求,但是由于时间较紧,有些模块以及整个系统还有许多不完善的地方,如操作性不太友好等。

        本系统开发的过程中遇到了许多问题,虽然旅途艰辛,但是经过自己不断的翻阅书本以及同学的相互交流,这些问题终于被克服了。这有助于我们提高自己的学习和独立工作的能力。

5.2展望

        本次系统开发结束以后,虽然自己在运用基础、专业知识解决问题、检索信息的能力得到了很大的提升,但是也发现了自己所欠缺的知识还很多。这次的题目属于计算机领域常用问题,因此,我认为可行性与实用性还是非常强的。经过这次的系统开发设计,我认识到系统开发还有很多可以提升完善的地方,简而言之,本次设计为我们未来在硬件和软件领域的学习和发展提供了坚实的基础。我们期待在未来的学习和实践中,不断挑战自我,探索更广阔的技术领域,实现更多有意义的项目和创新成果。这次的系统开发设计使我受益匪浅。

Use 4x4 matrix and LCD1602 liquid crystal display to realize the conversion of each base

Abstract: This paper aims to implement a program based on Electric clock, which displays the current hour, minute and second through seven Nixie tube, and uses 4x4 matrix keys to simulate and adjust the input of clock instructions. The Electric clock also has the function of hourly time reporting, which can send out the time signal at the hourly time. In the process of implementation, the current hour, minute and second are displayed through 7 segments of Nixie tube, and the time data is converted into the corresponding Nixie tube segment on/off signal by using appropriate control circuit and programming algorithm to realize the visual display of time. Secondly, simulate and adjust the input of clock instructions through 4x4 matrix buttons. The functions of the buttons include selecting hours, minutes, and seconds, resetting, adding time, and subtracting time. By programming algorithms, key inputs are detected and analyzed to achieve precise adjustment and control of the clock. Finally, in order to increase the practicality of the clock, an hourly time reporting function was designed. When the time displayed by the clock is the hour, the Electric clock will send a specific time signal. Through this course design, we will deeply understand the principle and design method of Electric clock, master the basic skills of digital circuit design and programming, and be able to apply theoretical knowledge to practical projects. In addition, by designing the hourly reporting function, we can also cultivate innovative thinking and practical application abilities.

Keywords: Electric clock; 7 sections of Nixie tube; 4x4 matrix buttons; Hour reporting function;

附源码如下:

top_count_clk.v:

module top_count_clk(
	input clk,
	input rst_n, 
	input  [3:0] key_in_y,
	output [3:0] key_out_x,
	output [5:0] sel,
	output [7:0] seg,
	output beep
);
wire flag1;
wire box;
wire ok;
wire flag9;
wire flag10;
 
count u_count(
.clk(clk),
.rst_n(rst_n),
.flag1(flag1),
.ok(ok)
);
 
count2 u_count2(
.clk(clk),
.rst_n(rst_n),
.box(box)
);
 
count_clk u_count_clk(
.clk(clk),
.rst_n(rst_n),
.sel(sel),
.seg(seg),
.flag1(flag1),
.box(box),//位选信号例化
.flag9(flag9),
.flag10(flag10),
.ok(ok),
.beep(beep)
);
 
key_debounce u_key_debounce(
.clk(clk),
.rst_n(rst_n),
.key_in_y(key_in_y),
.key_out_x(key_out_x),
.ok(ok),
.flag9(flag9),
.flag10(flag10)
);
 
endmodule

count.v:

module count(
	input wire clk,
	input wire rst_n,
	input wire ok,
	output reg flag1
);
 
reg[25:0] cnt;
//parameter MAX=24'd31250;//飞驰版54秒24小时 (上板时也可以使用这个,可以看到数码管显示很快)
parameter MAX=26'd50_000_000;//1s   (上板时使用这个)
//parameter MAX=20'd1000_000;//测试    (modelsim时使用这个)
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			cnt<=26'd0;
			flag1<=1'd0;
		end
	else if(cnt==MAX-1'd1)
		begin
			cnt<=26'd0;
			flag1<=1'd1;
		end
	else if(ok)	
		begin
			cnt<=cnt;
			flag1<=1'd0;
		end
	else
		begin
			cnt<=cnt+1'd1;
			flag1<=1'd0;
		end
end
endmodule
	

count2.v:

module count2(
	input wire clk,
	input wire rst_n,
	output reg box
);
 
reg[19:0] cnt;
parameter MAX=20'd1_000_0;//20ms
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			cnt<=20'd0;
			box<=1'd0;
		end
	else if(cnt==MAX-1'd1)
		begin
			cnt<=20'd0;
			box<=1'd1;
		end
	else
		begin
			cnt<=cnt+1'd1;
			box<=1'd0;
		end
end
 
endmodule
 

count_clk.v:

module count_clk(
	input wire clk,
	input wire rst_n,
	input wire flag1,//计数满1s
	//input wire key,//为1时,表示无效,没被按下
	input wire box,//控制位选跳转(流水灯)
	input wire flag9,
	input wire flag10,
	input wire ok,
	output reg[5:0] sel,
	output reg[7:0] seg,
	output beep
	
);
reg [2:0] sum=3'd0;//计数控制调哪个位置的数字
reg [4:0] s_a;//秒的个位
reg [3:0] s_b;//秒的十位
reg [4:0] m_a;//分的个位
reg [3:0] m_b;//分的十位
reg [4:0] t_a;//时的个位
reg [2:0] t_b;//时的十位
reg [0:0] flag2 = 0;//秒的个位进位标识符
reg [0:0] flag3 = 0;//秒的十位进位标识符
reg [0:0] flag4 = 0;//分的个位进位标识符
reg [0:0] flag5 = 0;//分的十位进位标识符
reg [0:0] flag6 = 0;//时的个位进位标识符
reg [0:0] flag7 = 0;//时的十位进位标识符
reg [0:0] clear =0;//计数满了清零符
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		sum<=3'd0;
	else if(flag10)
	begin
		if(sum<3'd5)
			begin
				sum<=sum+1'd1;
			end
		else 
			begin
				sum<=3'd0;
			end
	end
	else
		sum<=sum;
end
//秒的个位计数(暂停采用的止住flag进位,而不是采用禁用另一个计数模块的clk)	(已经改成clk暂停了)	
always@(posedge clk or negedge rst_n )
begin
	if(!rst_n)
		begin
			s_a<=5'd0;
			flag2<=1'd0;
		end
	else if(flag1)
		begin
			if(s_a<5'd9)
				begin
					s_a<=s_a+1'd1;
					flag2<=1'd0;
				end
			else
				begin
					s_a<=5'd0;
					flag2<=1'd1;
				end
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd0)
				begin
					if(s_a<5'd9)
						begin
							s_a<=s_a+1'd1;
						end
					else
						begin
							s_a<=5'd0;
						end
				end
		end
	else
		begin
			s_a<=s_a;
			flag2<=1'd0;//因为改成clk触发了,触发频率变大,如果不是设置置零,则会在下一个always语句中,多次触发flag2为1 的情况,疯狂计数(最开始写法为flagx触发时候,没有考虑到这种情况,现在改为clk,则需要考虑到不进位时clk上升沿来了触发的情况)
		end
end
 
//秒的十位计数
always@(posedge clk or negedge rst_n )
begin
	if(!rst_n)
		begin
			s_b<=3'd0;
			flag3<=1'd0;
		end
	else if(flag2)
		begin
			if(s_b<3'd5)
				begin
					s_b<=s_b+1'd1;
					flag3<=1'd0;
				end
			else
				begin
					s_b<=3'd0;
					flag3<=1'd1;
				end
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd1)
				begin
					if(s_b<3'd5)
						begin
							s_b<=s_b+1'd1;
						end
					else
						begin
							s_b<=3'd0;
						end
				end
		end
	else
		begin
			flag3<=1'd0;
			s_b<=s_b;
		end
end
//分的个位计数
always@(posedge clk or negedge rst_n )
begin
	if(!rst_n)
		begin
			m_a<=4'd0;
			flag4<=1'd0;
		end
	else if(flag3)
		begin
			if(m_a<4'd9)
				begin
					m_a<=m_a+1'd1;
					flag4<=1'd0;
				end
			else
				begin
				   m_a<=4'd0;
					flag4<=1'd1;
				end
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd2)
				begin
					if(m_a<4'd9)
						begin
							m_a<=m_a+1'd1;
						end
					else
						begin
							m_a<=4'd0;
						end
				end
		end
	else
		begin
			m_a<=m_a;
			flag4<=1'd0;
		end
end
 
 
//分的十位计数
always@(posedge clk or negedge rst_n )
begin
	if(!rst_n)
		begin
			m_b<=3'd0;
			flag5<=1'd0;
		end
	else if(flag4)
		begin
			if(m_b<3'd5)
				begin
					m_b<=m_b+1'd1;
					flag5<=1'd0;
				end
			else
				begin
				   m_b<=3'd0;
					flag5<=1'd1;
				end
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd3)
				begin
					if(m_b<3'd5)
						begin
							m_b<=m_b+1'd1;
						end
					else
						begin
							m_b<=3'd0;
						end
				end
		end
	else
		begin
			m_b<=m_b;
			flag5<=1'd0;
		end
end
//时的个位计数
always@(posedge clk or negedge rst_n )
begin
	if(!rst_n)
		begin
			t_a<=4'd0;
			flag6<=1'd0;
		end
	else if(flag5)
		begin
			if(t_a==4'd9)
				begin
					t_a<=4'd0;
					flag6<=1'd1;
				end
			else if(t_a==4'd3&&t_b==2'd2)
				begin
					t_a<=4'd0;
					clear<=1'd1;//传递给下一位使其判断清零的符号。
					flag6<=1'd1;
				end
			else
				begin
					t_a<=t_a+1'd1;
					flag6<=1'd0;
					clear<=1'd0; 
				end
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd4)
				begin
					if(t_a<4'd9&&t_b!=2'd2)
						begin
							t_a<=t_a+1'd1;
						end
					else if(t_b==2'd2)
						begin
							if(t_a<4'd3)
								t_a<=t_a+1'd1;
							else
								t_a<=4'd0;
						end
					else
						begin
							t_a<=4'd0;
						end
				end
		end
	else
		begin
			t_a<=t_a;
			flag6<=1'd0;
		end
end
//时的十位计数(已设置满23:59:59自动清零)  
always@(posedge clk  or negedge rst_n )//?不能加第二个(or posedge clear)加了会导致个位跟十位显示相同,小数点都相同
begin
	if(!rst_n)
		begin
			t_b<=2'd0;
		end
	else if(flag6)//flag6表示进位,也可以代表清零。
		begin
			if(clear)
				begin
					t_b<=2'd0;
				end
			else
				t_b<=t_b+1'd1;
		end
	else if(flag9)
		begin
			if(ok==1'd1&&sum==3'd5)
				begin
					if(t_b<2'd2)
						begin
							t_b<=t_b+1'd1;
						end
					else
						begin
							t_b<=2'd0;
						end
				end
		end
	else
		begin
			t_b<=t_b;
		end
end
//位选流水灯(实现快速闪烁每一个数码管形成残影,造成时钟现象)
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			sel<=6'b111_110;
		end
	else if(box)
		begin
			sel<={sel[4:0],sel[5]};
		end
	else
		begin
			sel<=sel;
		end
end
 
//译码模块
always@(sel)
begin
	if(sel==6'b111110)
			begin
				case(s_a)
				4'd0:seg=8'b11000000;
				4'd1:seg=8'b11111001;
				4'd2:seg=8'b10100100;
				4'd3:seg=8'b10110000;
				4'd4:seg=8'b10011001;
				4'd5:seg=8'b10010010;
				4'd6:seg=8'b10000010;
				4'd7:seg=8'b11111000;
				4'd8:seg=8'b10000000;
				4'd9:seg=8'b10010000;
				default:;
				endcase
			end
	else if(sel==6'b111101)
			begin
				case(s_b)
				3'd0:seg=8'b11000000;
				3'd1:seg=8'b11111001;
				3'd2:seg=8'b10100100;
				3'd3:seg=8'b10110000;
				3'd4:seg=8'b10011001;
				3'd5:seg=8'b10010010;
				default:;
				endcase
			end
	else if(sel==6'b111011)
			begin
				case(m_a)
				4'd0:seg=8'b01000000;
				4'd1:seg=8'b01111001;
				4'd2:seg=8'b00100100;
				4'd3:seg=8'b00110000;
				4'd4:seg=8'b00011001;
				4'd5:seg=8'b00010010;
				4'd6:seg=8'b00000010;
				4'd7:seg=8'b01111000;
				4'd8:seg=8'b00000000;
				4'd9:seg=8'b00010000;
				default:;
				endcase
			end
	else if(sel==6'b110111)
			begin
				case(m_b)
				3'd0:seg=8'b11000000;
				3'd1:seg=8'b11111001;
				3'd2:seg=8'b10100100;
				3'd3:seg=8'b10110000;
				3'd4:seg=8'b10011001;
				3'd5:seg=8'b10010010;
				default:;
				endcase
			end
	else if(sel==6'b101111)
			begin
				case(t_a)
				4'd0:seg=8'b01000000;
				4'd1:seg=8'b01111001;
				4'd2:seg=8'b00100100;
				4'd3:seg=8'b00110000;
				4'd4:seg=8'b00011001;
				4'd5:seg=8'b00010010;
				4'd6:seg=8'b00000010;
				4'd7:seg=8'b01111000;
				4'd8:seg=8'b00000000;
				4'd9:seg=8'b00010000;
				default:;
				endcase
			end
	else if(sel==6'b011111)
			begin
				case(t_b)
				2'd0:seg=8'b11000000;
				2'd1:seg=8'b11111001;
				2'd2:seg=8'b10100100;
				default:;
				endcase
			end
		
end


//音乐模块
reg flag_en;
 reg [7:0] state;
    always@(posedge clk, negedge rst_n) begin
        if(!rst_n)
            flag_en <= 1'b0;
        else if(flag5)  //闹钟时间到
            flag_en <= 1'b1;
        else if(state == 8'd63)  //一首音乐结束
            flag_en <= 1'b0;
        else
            flag_en <= flag_en;
    end 

reg beep_r;    
    reg [16:0] count, count_end;
    reg [23:0] count1;

    localparam  L_3 = 17'd75850,
                L_5 = 17'd63776,
                L_6 = 17'd56818,
                L_7 = 17'd50618,
                M_1 = 17'd47774,
                M_2 = 17'd42568,
                M_3 = 17'd37919,
                M_5 = 17'd31888,
                M_6 = 17'd28409,
                H_1 = 17'd23889;

    localparam TIME = 12000000;  //每个音的长短(250ms)     
    assign beep = beep_r;
    always@(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            count <= 17'h0;
            beep_r <= 1'b1;
        end 
        else if(flag_en) begin
            count <= count + 1'b1;
            if(count == count_end) begin
                count <= 17'h0;
                beep_r <= !beep_r;
            end 
        end
        else begin
            count <= 17'h0;
            beep_r <= 1'b1;            
        end 
    end 
    
    always@(posedge clk, negedge rst_n) begin
        if(!rst_n) begin
            count1 <= 24'd0;
            state <= 8'd0;
        end
        else if(flag_en) begin
            if(count1 < TIME) 
                count1 <= count1 + 1'b1;
            else begin
                count1 <= 24'd0;
                if(state == 8'd63)
                    state <= 8'd0;
                else
                    state <= state + 1'b1;        
            end         
        end 
        else begin
            count1 <= 24'd0;
            state <= 8'd0;
        end            
    end 
    
    always@(state) begin
        case(state)
            8'd0, 8'd1, 8'd2, 8'd3:      count_end  = L_3;
            8'd4, 8'd5, 8'd6:            count_end  = L_5;
            8'd7:                        count_end  = L_6;
            
            8'd8, 8'd9, 8'd10:           count_end  = M_1;
            8'd11:                       count_end  = M_2;
            8'd12:                       count_end  = L_6;
            8'd13:                       count_end  = M_1;
            8'd14, 8'd15:                count_end  = L_5;
            
            8'd16, 8'd17, 8'd18:         count_end  = M_5;
            8'd19:                       count_end  = H_1;
            8'd20:                       count_end  = M_6;
            8'd21:                       count_end  = M_5;
            8'd22:                       count_end  = M_3;
            8'd23:                       count_end  = M_5;
            
            8'd24, 8'd25, 8'd26, 8'd27, 
            8'd28, 8'd29, 8'd30, 8'd31:  count_end  = M_2;
            
            8'd32, 8'd33, 8'd34:         count_end  = M_2;
            8'd35:                       count_end  = M_3;
            8'd36, 8'd37:                count_end  = L_7;
            8'd38, 8'd39:                count_end  = L_6;
            
            8'd40, 8'd41, 8'd42:         count_end  = L_5;
            8'd43:                       count_end  = L_6;
            8'd44, 8'd45:                count_end  = M_1;
            8'd46, 8'd47:                count_end  = M_2;
            8'd48, 8'd49:                count_end  = L_3;
            
            8'd50, 8'd51:                count_end  = M_1;
            8'd52:                       count_end  = L_6;
            8'd53:                       count_end  = L_5;
            8'd54:                       count_end  = L_5;
            8'd55:                       count_end  = M_1;
            
            8'd56, 8'd57, 8'd58, 8'd59, 
            8'd60, 8'd61, 8'd62, 8'd63:  count_end  = L_5;  
            default:                     count_end  = 17'hxxxxx; 
        endcase     
    end 

 
endmodule	

 key_debounce.v:

//======================================================
// Module name: key4x4_test.v
// 描述: 检测开发板上的四乘四的矩阵键盘K1~K16
//======================================================
`timescale 1ns / 1ps
module key_debounce  (
input        clk,
input        rst_n,
input  [3:0] key_in_y,
output reg [3:0] key_out_x,
output reg ok,
output reg flag9,//改变暂停时数字
output reg flag10//控制数码管调整数字的位置			
							
						);

//========================================================
// PORT declarations
//========================================================						


//寄存器定义
reg [19:0] count;

//==============================================
// 输出矩阵键盘的行信号,20ms扫描矩阵键盘一次,采样频率小于按键毛刺频率,相当于滤除掉了高频毛刺信号。
//==============================================
always @(posedge clk or negedge rst_n)     //检测时钟的上升沿和复位的下降沿
begin
   if(!rst_n) begin               //复位信号低有效
      count <= 20'd0;        //计数器清0
      key_out_x <= 4'b1111;  
   end		
   else begin
	       if(count == 20'd0)           //0ms扫描第一行矩阵键盘
            begin
               key_out_x <= 4'b1110;   //开始扫描第一行矩阵键盘,第一行输出0
					count <= count + 20'b1; //计数器加1
            end
         else if(count == 20'd249_999) //5ms扫描第二行矩阵键盘,5ms计数(50M/200-1=249_999)
            begin
               key_out_x <= 4'b1101;   //开始扫描第二行矩阵键盘,第二行输出0
					count <= count + 20'b1; //计数器加1
            end				
			else if(count ==20'd499_999)   //10ms扫描第三行矩阵键盘,10ms计数(50M/100-1=499_999)
            begin
               key_out_x <= 4'b1011;   //扫描第三行矩阵键盘,第三行输出0
					count <= count + 20'b1; //计数器加1
            end	
			else if(count ==20'd749_999)   //15ms扫描第四行矩阵键盘,15ms计数(50M/67.7-1=749_999)
            begin
               key_out_x <= 4'b0111;   //扫描第四行矩阵键盘,第四行输出0
					count <= count + 20'b1; //计数器加1
            end				
         else if(count ==20'd999_999)  //20ms计数(50M/50-1=999_999)
			   begin
               count <= 0;             //计数器为0
            end	
	      else
				count <= count + 20'b1;    //计数器加1
			
     end
end
//====================================================
// 采样列的按键信号
//====================================================
reg [3:0] key_h1_scan;    //第一行按键扫描值KEY
reg [3:0] key_h1_scan_r;  //第一行按键扫描值寄存器KEY
reg [3:0] key_h2_scan;    //第二行按键扫描值KEY
reg [3:0] key_h2_scan_r;  //第二行按键扫描值寄存器KEY
reg [3:0] key_h3_scan;    //第三行按键扫描值KEY
reg [3:0] key_h3_scan_r;  //第三行按键扫描值寄存器KEY
reg [3:0] key_h4_scan;    //第四行按键扫描值KEY
reg [3:0] key_h4_scan_r;  //第四行按键扫描值寄存器KEY
always @(posedge clk)
	begin
		if(!rst_n) begin               //复位信号低有效
			key_h1_scan <= 4'b1111;     
			key_h2_scan <= 4'b1111;          
			key_h3_scan <= 4'b1111;          
			key_h4_scan <= 4'b1111;        
		end		
		else begin
		  if(count == 20'd124_999)           //2.5ms扫描第一行矩阵键盘值
			   key_h1_scan<=key_in_y;         //扫描第一行的矩阵键盘值
		  else if(count == 20'd374_999)      //7.5ms扫描第二行矩阵键盘值
			   key_h2_scan<=key_in_y;         //扫描第二行的矩阵键盘值
		  else if(count == 20'd624_999)      //12.5ms扫描第三行矩阵键盘值
			   key_h3_scan<=key_in_y;         //扫描第三行的矩阵键盘值
		  else if(count == 20'd874_999)      //17.5ms扫描第四行矩阵键盘值
			   key_h4_scan<=key_in_y;         //扫描第四行的矩阵键盘值 
		end
end

//====================================================
// 按键信号锁存一个时钟节拍
//====================================================
always @(posedge clk)
   begin
		 key_h1_scan_r <= key_h1_scan;   	
		 key_h2_scan_r <= key_h2_scan; 
		 key_h3_scan_r <= key_h3_scan; 
		 key_h4_scan_r <= key_h4_scan;  
	end 
   
wire [3:0] flag_h1_key = key_h1_scan_r[3:0] & (~key_h1_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效 
wire [3:0] flag_h2_key = key_h2_scan_r[3:0] & (~key_h2_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效 
wire [3:0] flag_h3_key = key_h3_scan_r[3:0] & (~key_h3_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效 
wire [3:0] flag_h4_key = key_h4_scan_r[3:0] & (~key_h4_scan[3:0]);  //当检测到按键有下降沿变化时,代表该按键被按下,按键有效 



//按键标识赋值
parameter MAX_TEN=21'd1000_000;//20ms
reg [20:0] delay_cnt;
reg [20:0] delay_cnt2;
reg [20:0] delay_cnt3;
reg value_key;
reg value_key2;
reg value_key3;
reg[0:0] flag8;
//reg[0:0] flag9;
//消抖模块
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			value_key<=1'd1;
			delay_cnt<=MAX_TEN;
		end
	else
		begin
		value_key<=flag_h1_key[0];//取得一瞬间key值
		if(value_key==!flag_h1_key[0])//如果抖动
			begin
				delay_cnt<=MAX_TEN;
			end
		else if(delay_cnt>1'd0&&value_key==1'd0)//当没有抖动时候,开始计数
			begin
				delay_cnt<=delay_cnt-1'd1;
			end
		else
			begin
				delay_cnt<=21'd0;
			end
		end
end
 
//消抖符号的处理
always@(posedge clk or negedge rst_n)
begin	
	if(!rst_n)
		begin
			flag8<=1'd0;
		end
	else if(delay_cnt==21'd1&&value_key==1'd0)
		begin
			flag8<=1'd1;
		end
	else 
		begin
			flag8<=1'd0;		
		end
end
//暂停标识符的转化
always@(posedge flag8)
begin	
	if(flag8 && !value_key)//按下消除抖动后才置1(没有松开)
		begin
			ok<=~ok;
		end
end
//消抖模块(按键三的消抖)
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			value_key2<=1'd1;
			delay_cnt2<=MAX_TEN;
		end
	else
		begin
		value_key2<=flag_h1_key[1];//取得一瞬间key值(寄存器存储key值)
		if(value_key2==!flag_h1_key[1])//如果抖动
			begin
				delay_cnt2<=MAX_TEN;
			end
		else if(delay_cnt2>1'd0&&value_key2==1'd0)//当没有抖动时候,开始计数
			begin
				delay_cnt2<=delay_cnt2-1'd1;
			end
		else
			begin
				delay_cnt2<=21'd0;
			end
		end
end
 
//消抖符号的处理
always@(posedge clk or negedge rst_n)
begin	
	if(!rst_n)
		begin
			flag9<=1'd0;
		end
	else if(delay_cnt2==21'd1&&value_key2==1'd0)
		begin
			flag9<=1'd1;//表示按键按下了一次并未松开,且抖动已经消除了
		end
	else 
		begin
			flag9<=1'd0;		
		end
end
//消抖模块(按键四的消抖)
always@(posedge clk or negedge rst_n)
begin
	if(!rst_n)
		begin
			value_key3<=1'd1;
			delay_cnt3<=MAX_TEN;//这里其实可以去掉,就不用给后面加东西了
		end
	else
		begin
		value_key3<=flag_h1_key[2];//取得一瞬间key值
		if(value_key3==!flag_h1_key[2])//如果抖动
			begin
				delay_cnt3<=MAX_TEN;
			end
		else if(delay_cnt3>1'd0&&value_key3==1'd0)//当没有抖动时候,开始计数
			begin
				delay_cnt3<=delay_cnt3-1'd1;
			end
		else
			begin
				delay_cnt3<=21'd0;
			end
		end
end
 
//消抖符号的处理
always@(posedge clk or negedge rst_n)
begin	
	if(!rst_n)
		begin
			flag10<=1'd0;
		end
		else if(delay_cnt3==21'd1&&value_key3==1'd0)//注意(当复位后,置了MAX值,所以会导致计数一次(此时没有抖动),会造成flag10跟flag9置1一次,进而导致暂停时候复位,会让调数的位置默认加一和数字默认加一)
		begin
			flag10<=1'd1;//表示按键按下了一次并松开,且抖动已经消除了
		end
	else 
		begin
			flag10<=1'd0;		
		end
end
	 
endmodule

以上内容为本人练习,仅供参考,若读者发现内容有误请私信指正,谢谢~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

你代码有bug!

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

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

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

打赏作者

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

抵扣说明:

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

余额充值