Verilog中的按鍵消抖

Verilog 按鍵消抖模塊

遇到的問題:第一邊沿檢測以及對key_flag和key_state的作用瞭解耗費了我很多時間,經過不斷的分析後還是找出了錯誤
		  在解決問題的過程中讓我也對層次化設計FPGA更加了解,同時對阻塞賦值和非阻塞賦值也有了更深刻的瞭解,熟悉了
		  定時器的設計與應用

感悟:FPGA是個大坑啊,進去了就出不來了,哈哈哈!		
	希望自己在接下來的路上越搓越勇
設計代碼:
/*
 *NAME: Rayone
 *DATE: 2019/5/21
 *FUNC: key filter
 */
module key_filter(Clk,Rst_n,key_in,key_flag,key_state);
//端口定義
	input Clk;
	input Rst_n;
	input key_in;
	
	output reg key_flag; //濾波後如果檢測確實按下了將其置1
	output reg key_state;//按鍵在空閒穩定狀態下爲1 在按下穩定狀態爲0

//key_filter模塊設計
//——>利用兩級D觸發器降低亞穩態發生的可能
	reg key_in_tmpa,key_in_tmpb;
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)begin
			key_in_tmpa <= 1'b0;
			key_in_tmpb <= 1'b0;
		end
		else begin
			key_in_tmpa <= key_in;
			key_in_tmpb <= key_in_tmpa;
		end
		
//——>捕獲key_in變化信號
	wire pedge,nedge;//key_in的上升沿和下降沿
	reg key_in_a,key_in_b;
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		key_in_a <= 1'b0;
		key_in_b <= 1'b0;
	end
	else begin
		key_in_a <= key_in_tmpb;//key_in_tmpb是key_in經過二級D觸發器處理後的,這裏是非阻塞賦值
										//key_in_a在給key_in_b賦值的同時也給key_in_b賦值,處於時時更新的
		key_in_b <= key_in_a;
	end
	//now :key_in_a   ago :key_in_b
	assign pedge = ((!key_in_b) & key_in_a);	//key_in上升沿信號到來
	assign nedge = ( key_in_b & (!key_in_a));//key_in下降沿信號到來
	
	
	//前期設計已經準備好了,接下來就是狀態機的設計
	localparam  //one-hot code
		IDLE 	  = 4'b0001,
		FILTER0 = 4'b0010,
		DOWN 	  = 4'b0100,
		FILTER1 = 4'b1000;

	//用一個狀態寄存器保存狀態
		reg [3:0]state;
		
	//設計定時器
	reg [19:0]cnt;		//——>需要一個計數寄存器,因爲只用計數20ms,所以一個20位的寄存器已經足夠了
	reg en_cnt;   		//——>計數器開始計數使能信號
	reg cnt_full_flag;//——>計數器加滿標誌
	
	//狀態機設計
	always@(posedge Clk or negedge Rst_n)
		if(!Rst_n)begin
			state <= IDLE;
			en_cnt <= 1'b0;
			key_flag <= 1'b0;
			key_state <= 1'b1;
		end
		else begin 
			case(state)
				IDLE:begin
					key_flag <= 1'b0;
					if(nedge)begin//如果檢測到下降沿
						state <= FILTER0;//進入按下抖動濾波狀態
						en_cnt <= 1'b1;  //使能計數(此時需要計時20ms,前面我們沒有設計計數器,於是我們在最後設計一個計數器哦)
					end
					else 
						state <= IDLE;
				end	
				
				FILTER0:begin
					if(cnt_full_flag)begin
						state <= DOWN;
						en_cnt <= 1'b0;
						key_flag <= 1'b1;	//標誌按鍵此時確實按下
						key_state <= 1'b0;//此時按鍵處於按下穩定狀態
					end
						else if(pedge)begin
							en_cnt <= 1'b0;
							state <= IDLE;	
						end
					else 
						state <= FILTER0;	
				end
				
				DOWN:begin
					key_flag <= 1'b0;
					if(pedge)begin
						en_cnt <= 1'b1;
						state <= FILTER1;
					end	
					else
						state <= DOWN;
				end
				
				FILTER1:begin
					if(cnt_full_flag == 1'b1)begin
						state <= IDLE;		//計數滿標誌成功計數
						en_cnt <= 1'b0;	//關閉計數
//						key_flag <= 1'b1;	//標誌按鍵此時確實釋放
						key_state <= 1'b1;//空閒穩定狀態下爲1
					end
					else if(nedge)begin
							en_cnt <= 1'b0;
							state <= DOWN;	
					end
					else 
						state <= FILTER1;	
				end
				default:
					begin 
						state <= IDLE; 
						en_cnt <= 1'b0;		
						key_flag <= 1'b0;
						key_state <= 1'b1;
					end
					
			endcase
	end
		

//計數器加1操作	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)begin
		cnt <= 20'd0;
	end
	else if(en_cnt)
		cnt <= cnt + 1'b1;
	else 
		cnt <= 20'd0;

//產生計數20ms滿信號 cnt_full_flag	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		cnt_full_flag <= 1'b0;
	else if(cnt == 999_999)
		cnt_full_flag <= 1'b1;
	else 
		cnt_full_flag <= 1'b0;
		
endmodule

按鍵的狀態圖:
按鍵狀態轉換圖

仿真波形圖:
在這裏插入圖片描述

可以看到:當檢測到按鍵按下時key_flag置1表示按鍵已經按下,key_state用於判斷按下後是否處於穩定狀態,如果要將該模塊運用於其他模塊就可以通過檢測這兩個信號狀態來進行使用
module led_ctrl(Clk,Rst_n,key_flag0,key_flag1,key_state0,key_state1,led);

	input Clk;
	input Rst_n;
	input key_flag0,key_flag1;
	
	input key_state0,key_state1;
	
	output [3:0]led;
	
	reg [3:0]led_r;
	
	always@(posedge Clk or negedge Rst_n)
	if(!Rst_n)
		led_r <= 4'b0000;
	else if(key_flag0 && !key_state0)
		led_r <= led_r + 1'b1;
	else if(key_flag1 && !key_state1)
		led_r <= led_r - 1'b1;
	else
		led_r <= led_r;
		
	assign led = ~led_r;
		
endmodule
這裏就是通過檢測key_flag(按鍵是否按下)和key_state(是否處於穩定狀態)來使4個led表示的二進制數的加1和減1操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章