【数电实验6】Verilog—按键防抖动设计

【2022.05西南交大数电实验】

【本代码及波形已通过老师验收。仅供参考。

【参考博客:Verilog实现独立按键消抖(状态机)_ty_xiumud的博客-CSDN博客_verilog按键消抖】

【参考视频(强推这个up主):[录播] 数字电子技术实验_哔哩哔哩_bilibili】 

【2022.05.11更新:目前仅有一人反映,拨动开关(不是按钮开关)关闭时计数的,有助教判为一验不过。该情况则是需要改为在高电平时计数。修改建议:在s1高电平稳定时的key_flag取1,s2计数满后认定为低电平稳定的key_flag取为0:

S1:	
						begin
							key_flag   <= 1'b1;	
							key_state  <= 1'b1;	
							en_counter <= 1'b0;	
S2:	
						if(cnt_full)	//计数满,说明达到稳定状态,关闭计数器,进入下一个状态
							begin
								state      <= S3;
								en_counter <= 1'b0;
								key_flag   <= 1'b0;
								key_state  <= 1'b0;

这样跑出来的波形是这样:

【数电实验6】Verilog—按键防抖动设计_第1张图片

因大部分人反映用正文代码验收无问题(老师验收的学生也过了),就不再修改正文代码。

目录

1 Quartus代码编写

2 test代码生成

3 ModelSim仿真

4 实验记录

5 其他

5.1 显示单位的切换

5.2 缩放界面


1 Quartus代码编写

module yck_1716_6_1(clk, rst_n, key_in, key_flag, key_state, codeout, Q, CO);

input clk;        //时钟50MHz
input rst_n;      //复位信号
input key_in;     //按键输入


output key_state;		//按键状态,高电平为未按下,低电平为按下状态
output key_flag;		//经消抖后可确认的按下动作

output CO;		//进位信号
output reg [6: 0] codeout;
output reg [3: 0] Q;	


parameter   S1 = 2'b00,		//松开稳定时
            S2 = 2'b01,		//按下毛刺时
            S3 = 2'b10,		//按下稳定时
            S4 = 2'b11;		//松开毛刺时
				


/*===============================================================20ms计数器=============================================================*/
reg en_counter;	//计数使能
reg [19:0] cnt;	//计数
reg cnt_full;

//只有当计数使能为高电平的时候,计数器才会计数
always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			cnt <= 0;
		else if(en_counter)
			cnt <= cnt + 1'b1;
		else 
			cnt <= 0;
	end
	
//计数满信号(数数到1000000计数满时间到。1000000是1M,当基于clk信号频率进行计数时,cnt_full走过(1/50M)s*1M的时间,即20ms)
always@(posedge clk or negedge rst_n)	//当clk接50MHz的信号时,相当于clk在1s内进行了50M次计数,相邻上升沿相间(1/50M)s
begin
	if(!rst_n)
		cnt_full <= 1'b0;
	else if(cnt == 20'd1000000)	//1000000的二进制数有20位(2^20=1048576)
		cnt_full <= 1'b1;
	else 
		cnt_full <= 1'b0;
end



/*==============================================================判断边沿模块=============================================================*/
reg key_tmp0,key_tmp1;

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			begin
				key_tmp0 <= 1'b1;
				key_tmp1 <= 1'b1;
			end
			
		else
			begin
				key_tmp0 <= key_in;		//key_in按键输入
				key_tmp1 <= key_tmp0;	
			end
			
	end

wire pedge,nedge;
assign nedge = (!key_tmp0) & key_tmp1;        //下降沿(下一clk时为0,上一clk时为1)
assign pedge = key_tmp0 & (!key_tmp1);        //上升沿(下一clk时为1,上一clk时为0)



/*=============================================================状态机模块================================================================*/
reg [1:0] state;
reg key_flag;		//经消抖后可确认的按下动作
reg key_state;		//按键状态,高电平为未按下,低电平为按下状态

always@(posedge clk or negedge rst_n)
	begin
		
		if(!rst_n)
			begin
				state      <= S1; 
				en_counter <= 1'b0;	//计数器归零
				key_state  <= 1'b1;	//按键未按下
				key_flag   <= 1'b0;
			end
		
		else 
			begin
				case(state)
					
					S1:	//高电平(松开稳定)
						begin
							key_flag   <= 1'b0;	//按键未按下,不计
							key_state  <= 1'b1;	//按键松开状态
							en_counter <= 1'b0;	//计数器归零
							
							if(nedge)	//检测到下降沿,进入下一个状态同时打开计数器
								begin
									state      <= S2;
									en_counter <= 1'b1;	//计数器使能
								end
							
							else 
								state <= state;	//保持目前状态    
						end
					
					S2:	//下降沿抖动(按下毛刺)
						if(cnt_full)	//计数满,说明达到稳定状态,关闭计数器,进入下一个状态
							begin
								state      <= S3;
								en_counter <= 1'b0;	//计数器归零
								key_flag   <= 1'b1;	//按键可确认已按下
								key_state  <= 1'b0;	//按键按下状态
							end
						
						else if(pedge)	//检测到上升沿(毛刺),跳回S1状态同时关闭计数器
							begin
								en_counter <= 1'b0;	//计数器归零
								state      <= S1;
							end
						
						else 
							state <= state;	//保持目前状态
							
					S3:	//低电平(按下稳定)
						begin
							key_flag <= 1'b0;	//一个按键周期测到一次就行,现在可清零了(后面代码编写只在意其posedge)
							
							if(pedge)	//检测到上升沿,进入下一个状态同时打开计数器
								begin
									state      <= S4;
									en_counter <= 1'b1;	//计数器使能
								end
							
							else 
								state <= state;	//保持目前状态
						end
					
					S4:	//上升沿抖动(松开毛刺)
						if(cnt_full)
							begin
								state     <= S1;
								key_state <= 1'b1;
							end
						
						else 
							if(nedge)
								begin
									en_counter <= 1'b0;	//计数器归零
									state      <= S3;					 		
								end 
							else 
								state <= state;	//保持目前状态
					
					default:
						state <= S1;
						
					endcase
	
			end
	
	end

	

/*===========================================================计数值输出与译码器模块========================================================*/
always@(posedge key_flag,negedge rst_n)	//key_flag:经消抖后可确认的按下动作
	if(!rst_n)
		Q <= 0;
	else
		begin
			if(Q < 4'd9)
				Q <= Q + 1'b1;
			else
				Q <= 4'b0;
		end
	
	assign CO = (rst_n & Q == 4'd9);
	
always@(Q)
	begin
		case(Q)
			4'd0:codeout <= 7'b1111110;
			4'd1:codeout <= 7'b0110000;
			4'd2:codeout <= 7'b1101101;
			4'd3:codeout <= 7'b1111001;
			4'd4:codeout <= 7'b0110011;
			4'd5:codeout <= 7'b1011011;
			4'd6:codeout <= 7'b1011111;
			4'd7:codeout <= 7'b1110000;
			4'd8:codeout <= 7'b1111111;
			4'd9:codeout <= 7'b1111011;
		default:codeout <= 7'bx;
	endcase
end



endmodule

2 test代码生成

         对 yck_1716_6_1.v 进行 Start Compilation 7cc783098aa44b3ca6f757265faa9ef3.png,而后自动生成test bench代码(如果没有Start Compilation 直接生成test bench代码会报错的)

【注:若出现Can't generate test bench files -- select a valid simulation tool,则通过菜单栏Assignments—Settings弹出窗口中EDA Tool Settings—Simulation设置为ModelSim,重新编译即可解决:166b2a186b3e4c4987bba1d718fa8a65.png

        得到测试v文件的步骤见上一博客【数电实验5】Verilog—可控分频器设计 & ModelSim的使用_kokoのadventure的博客-CSDN博客_可控分频器设计

        测试v文件的代码如下:

`timescale 1 ns/ 1 ns
module yck_1716_6_1_vlg_tst();
// constants                                           
// general purpose registers

// test vector input registers
reg clk;
reg key_in;
reg rst_n;
// wires                                               
wire CO;
wire [3:0]  Q;
wire [6:0]  codeout;
wire key_flag;
wire key_state;

// assign statements (if any)                          
yck_1716_6_1 i1 (
// port map - connection between master ports and signals/registers   
	.CO(CO),
	.Q(Q),
	.clk(clk),
	.codeout(codeout),
	.key_flag(key_flag),
	.key_in(key_in),
	.key_state(key_state),
	.rst_n(rst_n)
);
initial                                                
begin                                                  
// code that executes only once                        
// insert code here --> begin                          
		clk    = 1'b0;
		rst_n  = 1'b0;
		key_in = 1'b1;
		// Wait 100 ns for global reset to finish
		#200
      rst_n = 1'b1;
// --> end                                             
$display("Running testbench");                       
end

always
	begin

		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#(50*1000000);
		 
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#1000000;
		 key_in = 1'b0;#1000000;
		 key_in = 1'b1;#(50*1000000);
	end
                                                    
always                                                 
// optional sensitivity list                           
// @(event1 or event2 or .... eventn)                  
begin                                                  
// code executes for every event on sensitivity list   
// insert code here --> begin                          
		 #10 clk = ~clk;		// (1/50M)/2 ns = 10 ns												 
// --> end                                             
end                                                    
endmodule

3 ModelSim仿真

        相关步骤见上一博客【数电实验5】Verilog—可控分频器设计 & ModelSim的使用_kokoのadventure的博客-CSDN博客_可控分频器设计

         测试波形如下:

        10ms毛刺区: 

【数电实验6】Verilog—按键防抖动设计_第2张图片

 【数电实验6】Verilog—按键防抖动设计_第3张图片

4 实验记录

【数电实验6】Verilog—按键防抖动设计_第4张图片

Top-level Entity name

Family

Device

yck_1716_5_1

Cyclone IV E

EP4CE6E22C8

Total logic elements

Total registers

Total pins

59 / 6,272 ( < 1 % )

34

17 / 92 ( 18 % )

Total memory bits

Embedded Multiplier 9-bit elements

Total PLLs

0 / 276,480 ( 0 % )

0 / 30 ( 0 % )

0 / 2 ( 0 % )

信号名

主板器件

PIN

信号名

主板器件

PIN

clk

50MHz

PIN_90

Q[3]

IO3/LED3

PIN_54

rst_n

Key0/SW0/LED8

PIN_24

Q[2]

IO2/LED2

PIN_52

key_in

Key1/SW1/LED9

PIN_31

Q[1]

IO1/LED1

PIN_50

codeout[6]

a

PIN_112

Q[0]

IO0/LED0

PIN_46

codeout[5]

b

PIN_100

codeout[4]

c

PIN_104

codeout[3]

d

PIN_111

codeout[2]

e

PIN_106

codeout[1]

f

PIN_110

codeout[0]

g

PIN_103

5 其他

5.1 显示单位的切换

        Wave—WavePreference 

【数电实验6】Verilog—按键防抖动设计_第5张图片

         显示ms:

【数电实验6】Verilog—按键防抖动设计_第6张图片

         显示Hz:

【数电实验6】Verilog—按键防抖动设计_第7张图片

5.2 缩放界面

        ctrl + 滚轮缩放界面(慢一点,modelsim加载很慢)

你可能感兴趣的:(数电实验,fpga开发,学习)