专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

FPGA按键去抖程序

作者:大鹏   来源:大鹏   点击数:  更新时间:2014年07月13日   【字体:

  代码是特权同学的,我将其理解后加上了注释。去抖的原理和单片机是一样的,即通过延时来过滤掉按键抖动产生的毛刺信号。不同的是判断按键按下的条件不同,单片机通常是已知按键不按时IO口的电平(如高电平),当IO口电平发生改变时(如低电平),则开启定时器进行延时,延时20ms后再次读取IO口的电平,若仍为低电平,则说明有按键按下;若为高电平,则说明是按键抖动,没有按键按下。FPGA的采样频率很高,它可以在每个时钟周期的上升沿到来时对IO口的电平进行一次读取。通过读取相邻2个时钟周期内IO口的电平值并进行比较,若电平值发生改变(此代码中是判断电平从0变为1),则计数器清零,若持续20ms后电平值没有发生改变,则读取按键的键值,同时将这一键值存储起来,当下一个20ms后再次读取键值,将2次键值进行比较,若键值发生改变,则说明按键有动作(要么按下,要么松手)。此代码中是判断键值从0变为1,即松手检测。

 
 
// 按键去抖
// 实现一个简单的三个按键分别控制三个发光二极管亮或暗的控制。
// 例如,按键1控制发光二极管1。上电初始发光二极管1不亮,
// 当检测到按键1被按下后,发光二极管1则点亮,
// 按键1再次被按下时,发光二极管1则不亮,如此反复。
// 该实验需要把握好按键消抖检测的设计技巧。
 
// 注:此代码的按键操作是包括松手检测的,
// 即按键按下后要等到松手才算一次按键操作
 
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}; // 读取当前时刻的按键值
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;  // 将上一时刻的按键值进行存储
end
 
wire [2:0]key_an = key_rst_r & (~key_rst); // 当键值从0到1时key_an改变
//wire [2:0]key_an = key_rst_r ^ key_rst;  // 注:也可以这样写
 
reg [19:0] cnt;  // 延时用计数器
 
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后读取当前时刻的按键值
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; // 将去抖前一时刻的按键值进行存储
end
 
wire [2:0] key_ctrl = key_value_r & (~key_value); // 当键值从0到1时key_ctrl改变
 
reg d1;
reg d2;
reg d3;
 
always @(posedge  clk or negedge rst_n)
begin
 if(!rst_n)
 begin  // 一个if内有多条语句时不要忘了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输出进行翻转,RTL级与下面注释代码无差别
assign led2_n = d2? 1'b1:1'b0;
assign led3_n = d3? 1'b1:1'b0;
 
endmodule
关闭窗口

相关文章