4108|5

1

帖子

0

TA的资源

一粒金砂(初级)

楼主
 

Verilog按键消抖 [复制链接]

FPGA按键消抖程序剖析  
对于学习  FPGA  的爱好者来说  ,  在我们做的许许多多的系统和项目中都会用到
按键  ,  但是在我们所涉及的按键输入的数据  ,  是不能够直接利用的,而是要经过消
抖,唉,为了初级菜鸟,说简单点吧  ,  什么叫消抖?消抖说白点就是消除抖动引起的
按键不确定性!如果不消抖处理的话,就会按一次可能将相当于按几次出现的结果。相
信玩过单片机的人都清楚  ,  我们在读出按键的时候都会用到一个程序: 
If(!key) 
Delay(x); 
If(!key) 
{...} 
这个程序在此就不再延伸解释  ,  我所要说的是  ,  在  FPGA  里面按键的消抖与单片
机的消抖原理都是一样的!或许大家都自己写过或者在网上找到过许多消抖的程序  ,  但是
我所接触的当中  ,  我觉得特权前辈所写的那个是最为经典的  ,  一下就是我盗版特权前辈
的一个程序: 
module keyscan_module ( 
input clk, //  外部输入时钟,我选择  50M 
input reset, //  复位  ,  但是在此不建议使用此种复位 
//  形式,建议用异步复位同步释放 
input key_in, //  按键输入(  1bit  ) 
output key_ready); //  按键值输出 
/*********************************************/ 
reg key1,key2;  定义两个寄存器变量 
wire key_en;  定义一个线性变量 
always @ ( posedge clk or negedge reset) 
if ( !reset ) 
begin 
key1 <= 1'b1; 
key2 <= 1'b1; 
end 
else 
begin 
key1 <= key_in; //  学习过非阻塞式语句的同志,应该不能理解这里 
//  第一个时钟读取按键进入第一个寄存器  ,  第二个时钟 
key2 <= key1;  将第一个寄存器中的值赋给第二个寄存器 
end 
assign key_en = key2 & ( ~key1 ); //  当检测到下降沿的时候,  key_en  保持一个时钟的高 
电平 
/**********************************************/ 
reg[19:0] cnt;  定义一个计数器 
always @ ( posedge clk or negedge reset ) 
if ( !reset )cnt <= 20'd0; 
else if ( key_en ) cnt <= 20'd0; else cnt <= cnt + 1'b1; 
/********************************************/ 
reg key3,key4; 
always @ ( posedge clk or negedge reset ) 
if ( !reset ) key3 <= 1'b1; 
else if ( cnt == 20'hfffff) //  当计数到  20ms  的时候 
key3 <= key_in; 
always @ (posedge clk or negedge reset) //  下一个时钟把  key3  的按键值赋给  key4 
if(!reset)key4 <= 1'b1; 
else key4 <= key3;assign key_ready = key4 & ( ~key3); //  当有按键按下时  ,  输出有效  ,  保
持一个时钟周期  ; 
endmodule 
下面我们来好好研究下这个程序: 
整个程序的基本思路是这样的  :  系统上电后  ,  计数器就开始计数  , 
(注意啊  ,  不管有没有按键按下都在计数  ,  每次计数  20MS  )  于此同 
时,系统也在不断采集  key_in  的电平,假设在一个时钟上升沿的时 
候,检测到  key_in  为高电平  ,  (那么  key1<=1,key2<=1  )在下一个时 
钟上升沿的时候检测到低电平  (key1<=0;key2<=1)  那么执行这个语句 
assign key_en = key2 & ( ~key1 )  那么  key_en  就会得到一个高电 
平  ,  当第三个时钟上升沿的时候  (  key1<=0,key2<=0  )  由此可知  ,  key_e n 
只保持一个时钟周期的高电平  ,  而且仅是当检测到有下降沿的时候才 
会变为高电平  。  说到计数器  ,  前面也说了  ,  上电之后计数器就一直在 
计数,当时当  key_en  为高电平的时候,计数清零,也就是说,当前 
面检测到下降沿之后就重新开始计数, 
else if ( key_en ) cnt <= 20'd0; 
else cnt <= cnt + 1'b1; 
然后到这个程序 
else if ( cnt == 20'hfffff) //  当计数到  20ms  的时候 
key3 <= key_in; 
这个程序的作用是计数到了  20ms  之后再一次读取  key_in  的值  , 
换句话说,就是从前面检测到下降沿之后,  20ms  后再去检测!我们 
都知道,抖动所产生的毛刺都是在  us  级别的,哎呀,再怎么大也大 
不过  20ms  的,而我们按按键的话那肯定就不止  20ms  啦,再怎么快也要  500ms  以上吧!
再结合下段程序: 
always @ (posedge clk or negedge reset) //  下一个时钟把  key3  的按 
键值赋给  key4 
if(!reset)key4 <= 1'b1; 
else key4 <= key3; 
assign key_ready = key4 & ( ~key3); // 
假设第一次检测到的下降沿是由于抖动产生的毛刺  ,  那么  20M S 
后  ,  过  滤  掉  毛  刺  ,  检  测  到  的  应  该  是  一  个  高  电  平 
(  key3<=1,key4<=1,key_ready=0  )  ,  假如前面检测到的下降沿不是由于 
毛刺,而是按键按下的,那么  20MS  后,  key_in  肯定还会是低电平  , 
所以  (  key3<=0,key4<=1,key_ready=1  )  ,  而在  20MS  之后的下一个时钟 (  key3<=0,key4<=0,  那么  key_ready=0  )  key_read  只保持一个时钟周期的时间! 
为什么这个消抖程序被列为经典呢?大家神人研究后不妨从 
时序和资源方面去考虑一下,昨天我在一本黑金的教程上面弄了一 
个,一个按键就消耗  80  多个  lut  ,而这个只要  30  个,试想一下,  做 
16  个按键都上千呢,那其他模块怎么办?还有我觉得很好的一点就 
是它的输出  (  key_ready  )  只保持一个时钟周期  ,  有利于上层模块采集  ! 
哎呀  ,  没有对比  ,  看不出差距  ,  以下是我们老师教我写的一个消抖程 
序:他的思路是:把系统时钟(  50M  )分频  100K  (  10ms  )后再去读 
取按键值 
Always @ (posedge clk_100K or negedge reset) 
If(!reset)begin key1<=1;key2<=1; end 
Else begin key1<=key_in;key2<=key1;end 
Assign key_ready=key2 & !key1; 
大家看一下这个程序  ,  原理和特权的那个差不多  ,  看似更加简单  , 
的确,这个程序可以消抖,但是存在一个问题,就是  key_ready  也会 
保持  10MS  的一个高电平  ,  有些人会问  ,  高电平持续就一点不是方便 
上层采集吗?其实不然  ,  举个例子  :  我们用一个按键  ,  控制一个二极 
管,按一下亮,再按一下灭,程序大概这样:Always @ (posedge clk or negedge ) 
.........................................(  省略  ) 
If(key_ready) led<=~led 
我们使用的系统时钟是很高的,假如  key_ready  保持不是一个时 
钟周期  ,  而是  10ms,  那么我们按一次  ,  led  就会一亮一灭很多次  !  碰到 
这种情况,一般要用组合逻辑解决,比如 
Always @ (key_read or negedge) 
................... 
If(key_reaf)...... 
这样也可以实现按一次变化一次  ,  但是大家都知道  ,  组合逻辑往 
往会给我们带来许多时序上的问题,能用同步就同步  !  在此也特别提 
醒诸位,在  if(x)  判断电平的时候多考虑一下,免得出错! 
 
对于消抖程序忠告如下 
reg [3:0]key1_reg; 
reg [3:0]key2_reg; 
always @(posedge clk,negedge rest)begin 
  if(!rest)begin 
   key1_reg<=4'b1111; 
   key2_reg<=4’b1111; 
  end 
  else if(counter==5'hfffff)begin 
   key1_reg<=key; 
   key2_reg<=key1_reg 
end 
assign key_ctr=key2_reg&(~key1_reg); 
这段程序,20ms后采样 key的值给 key1_reg,而 key2_reg值为 key1_reg前一时刻的值,有按键按下时 key_ctr 为 1,可是程序中 key2_reg 值 20ms 才刷新一次,如果在做我们用一个按
键  ,  控制一个二极 
管,按一下亮,再按一下灭,程序大概这样:Always @ (posedge clk or negedge ) 
.........................................(  省略  ) 
If(key_ready) led<=~led 
这段 20ms 时间内每一晶振脉冲下 led 都翻转,这样灯就一闪一闪,不能达到要求,而我们
程序改成这样: 
reg [3:0]key1_reg; 
reg [3:0]key2_reg; 
always @(posedge clk,negedge rest)begin 
  if(!rest) 
   key1_reg<=4'b1111; 
  else if(counter==5'hfffff) 
   key1_reg<=key; 
end 
always @(posedge clk,negedge rest)begin 
  if(!rest) 
   key2_reg<=4'b1111; 
  else  
   key2_reg<=key1_reg; 
end 
assign key_ctr=key2_reg&(~key1_reg); 
Always @ (posedge clk or negedge rest ) 
.........................................(  省略  ) 
If(key_ready) led<=~led; 
这样 led 就只是翻转一次才能达到控制效果。 
 
 
此帖出自FPGA/CPLD论坛

最新回复

”假如前面检测到的下降沿不是由于毛刺,而是按键按下的,那么 20MS  后,key_in肯定还会是低电平  , 所以  (  key3  详情 回复 发表于 2014-12-2 23:05

点评

还没看内容,主题就坑人了。Verilog HDL:lol  详情 回复 发表于 2013-8-5 20:55

赞赏

1

查看全部赞赏

点赞 关注
 

回复
举报

648

帖子

2

TA的资源

纯净的硅(高级)

沙发
 

回复 楼主qwerqwerqwer 的帖子

还没看内容,主题就坑人了。Verilog HDL
此帖出自FPGA/CPLD论坛

点评

帮楼主调整了一下  详情 回复 发表于 2013-8-6 11:26
 
 

回复

2万

帖子

74

TA的资源

管理员

板凳
 
原帖由 philips_lu 于 2013-8-5 20:55 发表
还没看内容,主题就坑人了。Verilog HDL
帮楼主调整了一下
此帖出自FPGA/CPLD论坛
加EE小助手好友,
入技术交流群
EE服务号
精彩活动e手掌握
EE订阅号
热门资讯e网打尽
聚焦汽车电子软硬件开发
认真关注技术本身

点评

哈哈哈,不该犯的错误:)  详情 回复 发表于 2013-8-6 23:00
个人签名

加油!在电子行业默默贡献自己的力量!:)

 
 
 

回复

648

帖子

2

TA的资源

纯净的硅(高级)

4
 

回复 板凳soso 的帖子

哈哈哈,不该犯的错误
此帖出自FPGA/CPLD论坛
 
 
 

回复

128

帖子

0

TA的资源

一粒金砂(初级)

5
 
Great! Thank for sharing.
此帖出自FPGA/CPLD论坛
 
 
 

回复

4

帖子

0

TA的资源

一粒金砂(初级)

6
 
”假如前面检测到的下降沿不是由于毛刺,而是按键按下的,那么 20MS  后,key_in肯定还会是低电平  ,
所以  (  key3<=0,key4<=1,key_ready=1  )  ,  而在  20MS  之后的下一个时钟 (  key3<=0,key4<=0,  那么  key_ready=0  )  key_read  只保持一个时钟周期的时间! “楼主是默认20MS之后还在抖动吗?
此帖出自FPGA/CPLD论坛
 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表