分类:
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内被除去的。根据具体的按键和系统时钟,你可能需要修改计数器的宽度。
好了,该轮到你来实践了。