一、設計需求
設計一個按鍵控制led的功能模塊,即一個按鍵控制一個led。隨著每一次按鍵的按下,led就按照亮、滅交替顯示。
二、設計思路
本次使用的按鍵為機械彈性開關按鍵,但由于機械觸點的彈性作用,按鍵閉合時并不能馬上接通,同樣按鍵斷開時也不能馬上斷開,故在按鍵閉合和斷開的瞬間均伴隨著一連串的抖動,如圖1所示。而抖動的時間由按鍵的機械特性決定,一般為5ms~10ms。為了避免由于按鍵抖動而引起的誤操作,需要對按鍵進行消抖。
圖1 按鍵抖動
按鍵消抖包括硬件消抖和軟件消抖。當按鍵數目較少時,可采用硬件消抖如RS觸發器,否則將占大面積的PCB;當按鍵數目較多時,采用軟件消抖,利用程序避開按鍵的抖動期。
本設計采用軟件消抖的方案,即檢測按鍵是否按下,當檢測到按鍵按下時,延時10ms,再檢測按鍵是否真的按下了,若不是真的按下則返回繼續檢測按鍵是否按下,若是真的按下了則進行按鍵按下處理,接著檢測按鍵是否松開,當檢測到按鍵松開時,延時10ms,再檢測按鍵是否真的松開了,若不是真的松開則返回繼續檢測按鍵是否松開,若是真的松開了則進行按鍵松開處理,接著再返回檢測按鍵是否按下,就這樣不斷的判斷按鍵按下與否和松開與否。根據這種思路,給出按鍵檢測和消抖流程圖,如圖2所示。
圖2 按鍵檢測和消抖流程圖
圖3所示為紅色颶風E45開發板上按鍵的電路原理圖。從原理圖可以看出,按鍵按下時為低電平,按鍵斷開時為高電平。
圖3 E45中按鍵電路原理圖
圖4所示為本設計按鍵控制led的總體框架,由三個模塊組成,分別為子模塊KEY_Decounce、led_ctrl和頂層模塊key_ctrl_led。這里重點介紹按鍵檢測和消抖。圖5所示為KEY_Decounce模塊中所用到的按鍵檢測與消抖狀態機。狀態機中有四個狀態,分別為IDLE、JITTER1、DEAL和JITTER2。在IDLE狀態中檢測按鍵是否按下,若按下即data!=1則跳轉到JITTER1狀態中延時10ms進行消抖后再判斷按鍵是否真的按下了,若為真即data!=1則跳轉到DELA狀態中進行按鍵按下處理,否則即data==1則返回IDLE狀態中繼續檢測按鍵是否按下,在DEAL狀態中除了按鍵按下處理外還要檢測按鍵是否松開,若檢測到按鍵松開即data==1則跳轉到JITTER2狀態中延時10ms進行消抖后再判斷按鍵是否真的松開了,若為真即data==1則跳轉到IDLE狀態繼續檢測按鍵是否按下,否則返回DEAL狀態中繼續判斷按鍵是否松開。
圖4 按鍵控制led
圖5 按鍵檢測與消抖狀態機
三、設計實現
key_ctrl_led頂層模塊:
/**********************************************版權申明*************************************************
** 電子技術應用網站, CrazyBird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: key_ctrl_led.v
** 創建者: CrazyBird
** 創建日期: 2015-7-12
** 版本號: v1.0
** 功能描述: 頂層模塊將按鍵消抖模塊和led控制模塊連接起來,完成按鍵控制led的功能
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module key_ctrl_led(
rst_n,
clk,
key_data,
led_data
);
//******************************************************************************
// 參數定義
//******************************************************************************
// 修改以下參數以滿足需求
parameter CLK_CYCLE = 20; // 時鐘周期,單位ns
// 修改以上參數以滿足需求
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號,低電平有效
input clk; // 全局時鐘信號,50MHz
input key_data; // 鍵值輸入
output led_data; // led狀態
//******************************************************************************
// 變量定義
//******************************************************************************
wire key_flag;
//******************************************************************************
// 模塊的連接
//******************************************************************************
// 例化KEY_Debounce模塊
KEY_Debounce #(
.CLK_CYCLE(CLK_CYCLE)
)
u_KEY_Debounce (
.rst_n ( rst_n ),
.clk ( clk ),
.key_data ( key_data ),
.key_flag ( key_flag )
);
// 例化led_ctrl模塊
led_ctrl u_led_ctrl (
.rst_n ( rst_n ),
.clk ( clk ),
.led_en ( key_flag ),
.led_data ( led_data )
);
//******************************************************************************
endmodule
//*********************************************文件結束*****************************************************KEY_Debounce模塊:
/**********************************************版權申明*************************************************
** 電子技術應用網站, CrazyBird
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: KEY_Debounce.v
** 創建者: CrazyBird
** 創建日期: 2015-7-12
** 版本號: v1.0
** 功能描述: 該模塊主要負責完成按鍵的檢測和消抖
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module KEY_Debounce(
rst_n,
clk,
key_data,
key_flag
);
//******************************************************************************
// 參數定義
//******************************************************************************
// 修改以下參數以滿足需求
parameter CLK_CYCLE = 20; // 時鐘周期,單位ns
parameter T0 = 10_000_000; // 10ms,按鍵消抖時間
// parameter T0 = 1000; // 測試用
parameter DELAY = 19; // 10ms計數器位寬
// 修改以上參數以滿足需求
// 以下參數不要修改
parameter T0_VAL = (T0/CLK_CYCLE)-1; // 10ms,按鍵消抖時間
parameter IDLE = 2'b00,
JITTER1= 2'b01,
DEAL = 2'b10,
JITTER2= 2'b11;
// 以上參數不要修改
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號,低電平有效
input clk; // 全局時鐘信號,50MHz
input key_data; // 鍵值輸入
output reg key_flag; // 按鍵按下標志位
//******************************************************************************
// 變量定義
//******************************************************************************
wire delay_cnt_done;
//******************************************************************************
// 實現按鍵檢測和消抖的狀態機
//******************************************************************************
reg [1:0] state;
reg delay_cnt_en;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
begin
state <= IDLE;
delay_cnt_en <= 1'b0;
key_flag <= 1'b0;
end
else
begin
case(state)
IDLE :
begin
if(key_data!=1'b1)
begin
state <= JITTER1;
delay_cnt_en <= 1'b1;
end
else
state <= IDLE;
end
JITTER1 :
begin
if(delay_cnt_done==1'b1)
begin
delay_cnt_en <= 1'b0;
if(key_data!=1'b1)
begin
state <= DEAL;
key_flag <= 1'b1;
end
else
state <= IDLE;
end
else
state <= JITTER1;
end
DEAL :
begin
key_flag <= 1'b0;
if(key_data==1'b1)
begin
state <= JITTER2;
delay_cnt_en <= 1'b1;
end
else
state <= DEAL;
end
JITTER2 :
begin
if(delay_cnt_done==1'b1)
begin
delay_cnt_en <= 1'b0;
if(key_data==1'b1)
state <= IDLE;
else
state <= DEAL;
end
else
state <= JITTER2;
end
endcase
end
end
//******************************************************************************
// 計數器的實現
//******************************************************************************
reg [DELAY-1:0] delay_cnt;
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
delay_cnt <= {(DELAY){1'b0}};
else if(delay_cnt_en==1'b1)
begin
if(delay_cnt_done==1'b1)
delay_cnt <= {(DELAY){1'b0}};
else
delay_cnt <= delay_cnt + 1'b1;
end
else
delay_cnt <= {(DELAY){1'b0}};
end
assign delay_cnt_done = (delay_cnt==T0_VAL);
//******************************************************************************
endmodule
//*********************************************文件結束*****************************************************led_ctrl模塊:
/**********************************************版權申明*************************************************
**
**--------------------------------------------文件信息--------------------------------------------------
** 文件名: led_ctrl.v
** 創建者: CrazyBird
** 創建日期: 2015-7-12
** 版本號: v1.0
** 功能描述: 該模塊根據按鍵每次按下時將led的狀態取反
**
********************************************************************************************************/
// synopsys translate_off
`timescale 1 ns / 1 ps
// synopsys translate_on
module led_ctrl(
rst_n,
clk,
led_en,
led_data
);
//******************************************************************************
// 輸入/輸出端口定義
//******************************************************************************
input rst_n; // 全局復位信號,低電平有效
input clk; // 全局時鐘信號,50MHz
input led_en; // led狀態取反使能信號
output reg led_data; // led狀態
//******************************************************************************
// led狀態控制
//******************************************************************************
always @(posedge clk or negedge rst_n)
begin
if(rst_n==1'b0)
led_data <= 1'b0;
else if(led_en==1'b1)
led_data <= ~led_data;
else
led_data <= led_data;
end
//******************************************************************************
endmodule
//*********************************************文件結束*****************************************************modelsim仿真結果:
從仿真結果看,本設計已實現了按鍵控制led的功能,同時也達到了按鍵消抖的效果。
最后對設計進行綜合、布局布線、生成bit流文件、下載在紅色颶風板子上,然后通過控制led燈的亮與滅。