Chinaunix首页 | 论坛 | 博客
  • 博客访问: 974433
  • 博文数量: 214
  • 博客积分: 10173
  • 博客等级: 上将
  • 技术积分: 1867
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-18 13:48
文章分类

全部博文(214)

文章存档

2012年(1)

2010年(13)

2009年(5)

2008年(98)

2007年(97)

分类:

2008-05-08 15:04:44

如果你连接了一个机械键盘到FPGA,那么你可能会碰到一些问题。这里我们按下键盘10次,希望LED显示00000010,但最后的结果如下...
 

按键防抖项目
这个项目包括两个部分:

  • 第一部分: 问题描述
  • 第二部分: 解决办法

链接

    *

按键防抖 - 问题描述
假设我们需要连接一个键盘到FPGA,硬件上,我们可能这样实现:
 
但是,机械按键存在这样的问题:抖动!
当你按下按键,可能很幸运能得到如下的理想波形:
 
但更多的时候,得到的波形是这样的:
 
 

FPGA计数器
现在,假设我们在FPGA内部设计了一个计数器,并添加了一个显示器件来观察它是怎样工作的。
上电,看起来一切正常.
 
连续按下键盘十次,结果...

并不像我们想象的那样从0加到10.

按键防抖 - 解决办法
一种办法是在硬件上添加一个R/C滤波器,并且使用一个施密特触发器,但是我们有更简单的解决办法。

FPGA滤波器
FPGAs在简单的数字代数方面功能很强大. 让我们在FPGA内部使用一个计数器来计算按键被按下或松开多长时间。一旦计数器溢出,我们就认为按键的状态改变了。
 
PB 是按键的信号输入(这个例子中低有效).可能包含若干个毛刺, 并且不与任何时钟信号同步,没有任何规律可言。通过设计,我们希望将其与一个始终信号同步(这个例子中使用的时钟频率为20MHz),并产生3个无毛刺 的,与时钟同步的按键输出,所有输出均为高有效,从任何一个信号都能得到按键的状态改变信息。

module PushButton_Debouncer(clk, PB, PB_state, PB_up, PB_down);
input clk;  // "clk" 时钟信号
input PB;  // "PB" 有毛刺的、异步的、低有效的按键信号

output PB_state;  // 当按键被按下时输出1
output PB_down;  // 按键被按下的瞬间输出一个高电平脉冲
output PB_up;  // 按键被松开的瞬间输出一个高电平脉冲
// 首先使用两个触发器来同步PB信号
reg PB_sync_0;  always @(posedge clk) PB_sync_0 <= ~PB;  // 翻转PB,使之高有效
reg PB_sync_1;  always @(posedge clk) PB_sync_1 <= PB_sync_0;
// 声明一个16位的计数器
reg [15:0] PB_cnt;
// 当按键被按下或松开时,计数器计数
// 当计数器计数溢出时,便认为按键的状态的确已经改变
reg PB_state;  // 按键状态 (0:松开, 1:按下)
wire PB_idle = (PB_state==PB_sync_1);
wire PB_cnt_max = &PB_cnt; //当B_cn为全1时,输出为真
always @(posedge clk)
if(PB_idle)
    PB_cnt <= 0;  // 没发生任何事情
else
begin
    PB_cnt <= PB_cnt + 1;  // 按键被按下或者松开, 增加计数器的值r
    if(PB_cnt_max) PB_state <= ~PB_state;  // 如果计数器溢出,改变PB_state的值
end
wire PB_down = ~PB_state & ~PB_idle & PB_cnt_max;  // 当按键被按下时,有效一个时钟周期
wire PB_up   =  PB_state & ~PB_idle & PB_cnt_max;  // 当按键被松开时,有效一个时钟周期
endmodule

使用16位的计数器、20MHz的时钟,需要3ms的时间才能使计数器溢出。在用户眼里,3ms的时间很短,甚至来不及反应,但是毛刺却正是在这3ms内被除去的。根据具体的按键和系统时钟,你可能需要修改计数器的宽度。

好了,该轮到你来实践了。

阅读(1559) | 评论(0) | 转发(2) |
0

上一篇:altsyncram Megafunction

下一篇:rtorrent/ARM

给主人留下些什么吧!~~