專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

FPGA按鍵去抖程序

作者:大鵬   來(lái)源:大鵬   點(diǎn)擊數(shù):  更新時(shí)間:2014年07月13日   【字體:

  代碼是特權(quán)同學(xué)的,我將其理解后加上了注釋。去抖的原理和單片機(jī)是一樣的,即通過(guò)延時(shí)來(lái)過(guò)濾掉按鍵抖動(dòng)產(chǎn)生的毛刺信號(hào)。不同的是判斷按鍵按下的條件不同,單片機(jī)通常是已知按鍵不按時(shí)IO口的電平(如高電平),當(dāng)IO口電平發(fā)生改變時(shí)(如低電平),則開啟定時(shí)器進(jìn)行延時(shí),延時(shí)20ms后再次讀取IO口的電平,若仍為低電平,則說(shuō)明有按鍵按下;若為高電平,則說(shuō)明是按鍵抖動(dòng),沒(méi)有按鍵按下。FPGA的采樣頻率很高,它可以在每個(gè)時(shí)鐘周期的上升沿到來(lái)時(shí)對(duì)IO口的電平進(jìn)行一次讀取。通過(guò)讀取相鄰2個(gè)時(shí)鐘周期內(nèi)IO口的電平值并進(jìn)行比較,若電平值發(fā)生改變(此代碼中是判斷電平從0變?yōu)?),則計(jì)數(shù)器清零,若持續(xù)20ms后電平值沒(méi)有發(fā)生改變,則讀取按鍵的鍵值,同時(shí)將這一鍵值存儲(chǔ)起來(lái),當(dāng)下一個(gè)20ms后再次讀取鍵值,將2次鍵值進(jìn)行比較,若鍵值發(fā)生改變,則說(shuō)明按鍵有動(dòng)作(要么按下,要么松手)。此代碼中是判斷鍵值從0變?yōu)?,即松手檢測(cè)。

 
 
// 按鍵去抖
// 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的三個(gè)按鍵分別控制三個(gè)發(fā)光二極管亮或暗的控制。
// 例如,按鍵1控制發(fā)光二極管1。上電初始發(fā)光二極管1不亮,
// 當(dāng)檢測(cè)到按鍵1被按下后,發(fā)光二極管1則點(diǎn)亮,
// 按鍵1再次被按下時(shí),發(fā)光二極管1則不亮,如此反復(fù)。
// 該實(shí)驗(yàn)需要把握好按鍵消抖檢測(cè)的設(shè)計(jì)技巧。
 
// 注:此代碼的按鍵操作是包括松手檢測(cè)的,
// 即按鍵按下后要等到松手才算一次按鍵操作
 
module key_debounce(
  clk,rst_n,
  key1_n,key2_n,key3_n,
  led1_n,led2_n,led3_n
  );
 
input clk;
input rst_n;
input key1_n,key2_n,key3_n;
output led1_n,led2_n,led3_n;
 
reg [2:0] key_rst;
 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_rst <= 3'b111;
 else
  key_rst <= {key3_n,key2_n,key1_n}; // 讀取當(dāng)前時(shí)刻的按鍵值
end
 
reg [2:0] key_rst_r;
 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_rst_r <= 3'b111;
 else
  key_rst_r <= key_rst;  // 將上一時(shí)刻的按鍵值進(jìn)行存儲(chǔ)
end
 
wire [2:0]key_an = key_rst_r & (~key_rst); // 當(dāng)鍵值從0到1時(shí)key_an改變
//wire [2:0]key_an = key_rst_r ^ key_rst;  // 注:也可以這樣寫
 
reg [19:0] cnt;  // 延時(shí)用計(jì)數(shù)器
 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  cnt <= 20'd0;
 else if(key_an)
   cnt <= 20'd0;
  else
   cnt <= cnt + 20'd1;
end
 
reg [2:0] key_value;
 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_value <= 3'b111;
 else if(cnt == 20'hfffff) // 2^20*1/(50MHZ)=20ms
   key_value <= {key3_n,key2_n,key1_n}; // 去抖20ms后讀取當(dāng)前時(shí)刻的按鍵值
end
 
reg [2:0] key_value_r;
 
always @(posedge clk or negedge rst_n)
begin
 if(!rst_n)
  key_value_r <= 3'b111;
 else
  key_value_r <= key_value; // 將去抖前一時(shí)刻的按鍵值進(jìn)行存儲(chǔ)
end
 
wire [2:0] key_ctrl = key_value_r & (~key_value); // 當(dāng)鍵值從0到1時(shí)key_ctrl改變
 
reg d1;
reg d2;
reg d3;
 
always @(posedge  clk or negedge rst_n)
begin
 if(!rst_n)
 begin  // 一個(gè)if內(nèi)有多條語(yǔ)句時(shí)不要忘了begin end
  d1 <= 0; 
  d2 <= 0;
  d3 <= 0;
 end
 else
 begin
  if(key_ctrl[0]) d1 <= ~d1;
  if(key_ctrl[1]) d2 <= ~d2;
  if(key_ctrl[2]) d3 <= ~d3;
 end
end
 
assign led1_n = d1? 1'b1:1'b0; // 此處只是為了將LED輸出進(jìn)行翻轉(zhuǎn),RTL級(jí)與下面注釋代碼無(wú)差別
assign led2_n = d2? 1'b1:1'b0;
assign led3_n = d3? 1'b1:1'b0;
 
endmodule
關(guān)閉窗口

相關(guān)文章