实际系统中常用的按键大部分都是轻触式按键,如下图所示。该按键内部由一个弹簧片和两个固定触点组成,当弹簧片被按下,则两个固定触点接通,按键闭合。弹簧片松开,两个触点断开,按键也就断开了。根据这种按键的机械特性,在按键按下时,会先有一段时间的不稳定期,在这期间,两个触点时而接通,时而断开,我们称之为抖动,当按键大约按下20ms后,两个触点才能处于稳定的闭合状态,按键松开时和闭合时情况类似。而我们的FPGA工作在很高的频率,按键接通或断开时任何一点小的抖动都能轻易的捕捉到,如果不加区分的将每一次闭合或断开都当做一次按键事件,那么势必一次按键动作会被FPGA识别为很多次按键操作,从而导致系统工作稳定性下降。
 轻触按键实物图
一次按键动作的大致波形如下图所示:
      因此,我们所需要做的工作,就是滤除按键按下和释放时各存在的20ms的不稳定波形。做法思路是:检测按键按下---》等待20Ms ----》检测此时按键键值,若为按下值则按下有效,否则按下无效(后面可以检测亦可以不检测,据具体情况而定----》检测到按键松开----》延迟20Ms ----》检测此时的键值,若为按下值则松开无效,否则按键松开)
硬件电路:    
        独立按键属于一种输入设备,其与FPGA连接的IO口被接上了10K的上拉电阻,在按键没有按下时,FPGA会检测到高电平;当按键按下后,FPGA的IO口上则将呈现低电平。因此,按键检测的实质就是读取FPGA的IO上的电平。




独立按键典型电路
 verilog 程序如下所示:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :key_shake.v
** CreateDate :2015.03
** Funtions : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,之后输出按键输出.
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved[F].
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。
*******************************************************************************/ module key_shake (
clk_100M,
rst_n, key_in,
key_out
);
input clk_100M; //100Mhz
input rst_n; input key_in;
output key_out; //--------------------------------------
//在复位之后的100us内,不响应按键的操作
localparam t_100us = 'd9999;
localparam t1ms = 'd99999; //定时1ms
localparam t_20ms = 'd20; reg [:] cnt;
reg key_en; //复位之后允许按键输入标志
always @(posedge clk_100M or negedge rst_n)
begin
if(!rst_n)
begin
cnt <= ;
key_en <=;
end
else
begin
if(cnt == t_100us)
begin
key_en <= ;
end
else
begin
key_en <= ;
cnt <= cnt + ;
end
end
end //--------------------------------------------------
wire HtoL_flag; //下降沿标志
wire LtoH_flag; //上升沿标志
reg [:] key_reg;
always @(posedge clk_100M or negedge rst_n)
begin
if(!rst_n)
begin
key_reg <= 'b111; //默认没按下状态为高,按下之后为低.反之则为3'b000;
end
else
begin
key_reg <= {key_reg[:],key_in};
end
end assign HtoL_flag = key_en?(key_reg[:] == 'b10):0; //下降沿检测,一个时钟的高电平
assign LtoH_flag = key_en?(key_reg[:] == 'b01):0; //上升沿检测,一个时钟的高电平
//---------------------------------------------
reg cnt_en; //计数使能标志 reg [:] cnt2;
always @(posedge clk_100M or negedge rst_n)
begin
if(!rst_n)
begin
cnt2 <= 'd0;
end
else if((cnt_en)&&(cnt2 == t1ms))
begin
cnt2 <= 'd0;
end
else if(cnt_en)
begin
cnt2 <= cnt2 + 'd1;
end
else
cnt2 <= 'd0;
end reg [:] cnt3;
always @(posedge clk_100M or negedge rst_n)
begin
if(!rst_n)
begin
cnt3 <= 'd0;
end
else if((cnt_en)&&(cnt2 == t1ms))
begin
if(cnt3 == t_20ms )
cnt3 <= t_20ms;
else
cnt3 <= cnt3 + ;
end
else if(!cnt_en)
cnt3 <= 'd0;
end //----------------------------------
//按键状态机
reg [:] i;
reg key_down; //按键按下标志
reg key_up; //按键释放标志
always @(posedge clk_100M or negedge rst_n)
begin
if(!rst_n)
begin
key_down <= ;
key_up <= ;
i <= ;
cnt_en <= ;
end
else
begin
case(i)
'd0:
begin
key_down <= ;
key_up <= ;
if(HtoL_flag) i <= 'd1; //检测到按下
else if(LtoH_flag) i <= 'd2; //检测到释放按键
else i <= 'd0;
end
'd1:
begin
if(cnt3 == t_20ms )
begin
if(!key_in) //检测到按键依然被按下
begin
key_down <= ; //按键按下成功
i <= 'd3;
cnt_en <= ;
end
else
begin
key_down <= ;
i <= 'd0;
cnt_en <= ;
end
end
else
cnt_en <= ;
end
'd2:
begin
if(cnt3 == t_20ms )
begin
if(key_in) //检测到按键被释放
begin
key_up <= ; //按键释放成功
i <= 'd3;
cnt_en <= ;
end
else
begin
key_up <= ;
i <= 'd0;
cnt_en <= ;
end
end
else
cnt_en <= ;
end
'd3:
begin
key_up <= ;
key_down <= ;
i <= 'd0;
end
default:i <= 'd0;
endcase
end
end assign key_out = key_down; //当按键被按下有效时
// assign key_out = key_up; //当按键被释放后才有效时
endmodule

测试代码如下:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :key_testbench.v
** CreateDate :2015.03
** Funtions :按键消抖的测试文件
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved.
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:
*******************************************************************************/ module key_testbench; reg clk;
reg rst_n;
reg key_in;
wire key_out; key_shake key_shake_1(
.clk,
.rst_n, .key_in,
.key_out
); localparam tck = ;
localparam t = /tck; always #(t/) clk = ~clk; task key_in_down;
begin
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
end
endtask task key_in_up;
begin
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
#(*t) key_in = ;
end
endtask initial
begin
clk = ;
rst_n = ;
key_in = ; #(*t) rst_n = ; #(*t);
#(*t) key_in_down;
#(*t);
#(*t) key_in_up; #(*t);
#(*t) repeat() key_in_down; //按下时抖动 #(*t); //按下时间 #(*t) repeat() key_in_up; //释放时抖动 end endmodule

仿真结果:

1、在复位之后100us之前按下按键时,不响应。

2、抖动(按下后20ms之内释放)。

 

  3、按下之后检测以及释放之后的检测。
  

如果将按键按下有效时刻、按键释放有效时刻和按键所处状态全部表现出来,则代码稍作修改即可:

/********************************Copyright**************************************
**----------------------------File information--------------------------
** File name :key_shake.v
** CreateDate :2015.03
** Funtions : 按键的消抖操作:在复位之后的100us内,不响应按键的操作,在之后有按键按下后,有20ms的延迟,检测,然后松开时也有20ms的检测,之后输出按键输出.
** Operate on :M5C06N3L114C7
** Copyright :All rights reserved[F].
** Version :V1.0
**---------------------------Modify the file information----------------
** Modified by :
** Modified data :
** Modify Content:V1.1:clk-->clk_100M, 常数声明放到一起,便于修改。
*******************************************************************************/ module key_shake (
clk,
rst_n, key_in,
key_down_out,
key_up_out,
key_in_out
);
input clk; //24Mhz
input rst_n; input key_in;
output key_down_out; //按下输出
output key_up_out; //释放输出
output key_in_out; //跟随输入输出 //--------------------------------------
//在复位之后的100us内,不响应按键的操作
parameter t_20ms = 'd20; `define CLK_20M
// `define CLK_24M
// `define CLK_50M `ifdef CLK_20M
parameter t_100us = 'd1999;
parameter t1ms = 'd19999; //定时1ms
`endif `ifdef CLK_20M
parameter t_100us = 'd2399;
parameter t1ms = 'd23999; //定时1ms
`endif `ifdef CLK_20M
parameter t_100us = 'd4999;
parameter t1ms = 'd49999; //定时1ms
`endif reg [:] cnt;
reg key_en; //复位之后允许按键输入标志
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt <= ;
key_en <=;
end
else
begin
if(cnt == t_100us)
begin
key_en <= ;
end
else
begin
key_en <= ;
cnt <= cnt + ;
end
end
end //--------------------------------------------------
wire HtoL_flag; //下降沿标志
wire LtoH_flag; //上升沿标志
reg [:] key_reg;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_reg <= 'b111; //默认没按下状态为高,按下之后为低.反之则为3'b000;
end
else
begin
key_reg <= {key_reg[:],key_in};
end
end assign HtoL_flag = key_en?(key_reg[:] == 'b10):0; //下降沿检测,一个时钟的高电平
assign LtoH_flag = key_en?(key_reg[:] == 'b01):0; //上升沿检测,一个时钟的高电平
//---------------------------------------------
reg cnt_en; //计数使能标志
reg [:] cnt2;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt2 <= 'd0;
end
else if((cnt_en)&&(cnt2 == t1ms))
begin
cnt2 <= 'd0;
end
else if(cnt_en)
begin
cnt2 <= cnt2 + 'd1;
end
else
cnt2 <= 'd0;
end reg [:] cnt3;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
cnt3 <= 'd0;
end
else if((cnt_en)&&(cnt2 == t1ms))
begin
if(cnt3 == t_20ms )
cnt3 <= t_20ms;
else
cnt3 <= cnt3 + ;
end
else if(!cnt_en)
cnt3 <= 'd0;
end //----------------------------------
//按键状态机
reg [:] i;
reg key_down; //按键按下标志
reg key_up; //按键释放标志
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_down <= ;
key_up <= ;
i <= ;
cnt_en <= ;
end
else
begin
case(i)
'd0:
begin
key_down <= ;
key_up <= ;
if(HtoL_flag) i <= 'd1; //检测到按下
else if(LtoH_flag) i <= 'd2; //检测到释放按键
else i <= 'd0;
end
'd1:
begin
if(cnt3 == t_20ms )
begin
if(!key_in) //检测到按键依然被按下
begin
key_down <= ; //按键按下成功
i <= 'd3;
cnt_en <= ;
end
else
begin
key_down <= ;
i <= 'd0;
cnt_en <= ;
end
end
else
cnt_en <= ;
end
'd2:
begin
if(cnt3 == t_20ms )
begin
if(key_in) //检测到按键被释放
begin
key_up <= ; //按键释放成功
i <= 'd3;
cnt_en <= ;
end
else
begin
key_up <= ;
i <= 'd0;
cnt_en <= ;
end
end
else
cnt_en <= ;
end
'd3:
begin
key_up <= ;
key_down <= ;
i <= 'd0;
end
default:i <= 'd0;
endcase
end
end //---------------------------
reg key_out;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
key_out <= ;
end
else
begin
if(key_down)
key_out <= ; //按下为低
else if(key_up)
key_out <= ; //释放为高
else
key_out <= key_out; //否则保持
end
end assign key_down_out = key_down; //当按键被按下有效时
assign key_up_out = key_up; //当按键被释放后才有效时
assign key_in_out = key_out;
endmodule

仿真:

最新文章

  1. [zz]如何在C语言程序中处理汉字
  2. Java基础-JDK动态代理
  3. uva 816 Abbott的复仇
  4. BOM(Bill of Material)详解
  5. getComputedStyle(and currentStyle)
  6. __attribute__((unused))
  7. a标签的href=&quot;javascript:void(0)&quot;和href=&quot;#&quot;的区别
  8. 记一次SQL联合查询注入工具的编写
  9. java课程设计(Calculator) 201521123027 陈龙
  10. setContentType与setCharacterEncoding的区别
  11. 一条查询sql的执行流程和底层原理
  12. 【Android】android文件的写入与读取---简单的文本读写context.openFileInput() context.openFileOutput()
  13. Logstash安装和使用
  14. [CodeForces 471A] MUH and Sticks
  15. 【做题】51NOD1518 稳定多米诺覆盖——容斥&amp;dp
  16. 算法题思路总结和leecode继续历程
  17. 查看pc ip地址
  18. IDEA+Maven+多个SpringBoot子模块(创建多模块整合项目)
  19. 第3次Scrum冲刺
  20. 51nod 循环数组最大子段和(动态规划)

热门文章

  1. 学号160809224姓名黄家帅c语言程序设计实验2 选择结构程序设计
  2. 一起入门python4之字典
  3. Tip
  4. ajax初探01
  5. Python自动化之配置Python模块国内镜像
  6. 7.5---两个正方形分成对半的直线(CC150)
  7. discuz论坛移植修改数据库配置
  8. linux创建子进程--fork()方法
  9. 【leetcode】Populating Next Right Pointers in Each Node II
  10. mysql性能优化学习笔记-参数介绍及优化建议