FPGA|CPLD|ASICwilliam hill官网
直播中

梅雪松

12年用户 1236经验值
擅长:可编程逻辑 嵌入式技术 EDA/IC设计 控制/MCU
私信 关注
[资料]

小梅哥和你一起深入学习FPGA之独立按键检测

本帖最后由 小梅哥 于 2014-11-10 13:05 编辑

几乎没有哪一个系统没有输入输出设备,大到显示器,小到led灯,轻触按键。作为一个系统,要想稳定的工作,输入输出设备的性能占了很重要的角色。本实验,小梅哥就通过一个独立按键的检测实验,来正式步入基本外设驱动开发的大门。
一、        实验目的

实现4个独立按键的抖动检测实验,并通过4个独立按键控制4个led灯亮灭状态的翻转。

二、        实验原理

实际系统中常用的按键大部分都是轻触式按键,如图2-1所示。该按键内部由一个弹簧片和两个固定触点组成,当弹簧片被按下,则两个固定触点接通,按键闭合。弹簧片松开,两个触点断开,按键也就断开了。根据这种按键的机械特性,在按键按下时,会先有一段时间的不稳定期,在这期间,两个触点时而接通,时而断开,我们称之为抖动,当按键大约按下20ms后,两个触点才能处于稳定的闭合状态,按键松开时和闭合时情况类似。而我们的FPGA工作在很高的频率,按键接通或断开时任何一点小的抖动都能轻易的捕捉到,如果不加区分的将每一次闭合或断开都当做一次按键事件,那么势必一次按键动作会被FPGA识别为很多次按键操作,从而导致系统工作稳定性下降。


图2-1 轻触按键实物图

一次按键动作的大致波形如下图所示:


因此,我们所需要做的工作,就是滤除按键按下和释放时各存在的20ms的不稳定波形



三、        硬件设计

独立按键属于一种输入设备,其与FPGA连接的IO口被接上了10K的上拉电阻,在按键没有按下时,FPGA会检测到高电平;当按键按下后,FPGA的IO口上则将呈现低电平。因此,按键检测的实质就是读取FPGA的IO上的电平。


图3-1 独立按键典型威廉希尔官方网站


四、        架构设计

本实验由总共四个模块组成,分别为LED驱动模块、独立按键检测模块、控制模块和顶层模块,其架构如下:

图4-1 led实验模块组织结构图

由图可知本实验有n个输出端口,对应驱动了n个led灯。n+2个输入端口,对应了n个按键输入和一个时钟输入以及一个复位输入。详细端口名及其意义如下

端口说明
端口名
端口功能或意义
Rst_n
全局复位
Led
LED驱动输出Pin,驱动控制LED灯亮灭
Key_in
按键输入端口
Clk
系统时钟输入端口

表4-1 独立按键检测实验端口说明


因为存在模块间的连接,因此有部分内部信号,下表为内部信号的名称和功能说明

内部信号说明
内部信号名
内部信号功能或意义
Sig
LED控制输入,LED输出状态将于本信号的各位状态一致
Key_Value
按键检测结果输出
Key_Flag
按键检测成功标志信号

表4-2 独立按键检测实验内部信号说明





一、        代码组织方式

         本实验中,按键检测部分用到了状态机,该状态机包括两个状态:按下消抖状态和释放消抖状态,详细内容将在关键代码中进行讲解,此处不做细致讨论。

实验中还设计了一个控制器,该控制器主要进行按键事件与LED灯状态的控制,通过读取按键值,并根据按键信息翻转对应的LED的亮灭状态。这部分内容由于篇幅原因,不在文档中写出,请直接参看附件源代码即可。

LED的驱动采用实验一的模块,因此这里不进行过多的分析介绍。


二、        关键代码解读

以下为按键电平变化的检测代码,通过存储前一次时钟上升沿时的按键状态和当前时钟上升沿时的按键状态,并比较两次状态是否相同,即可获知是否有按键按下。

/*-------存储按键状态的上一个状态---------------*/
always @ (posedge Clk or negedge Rst_n)
begin
  if(!Rst_n)
   begin
    key_tmp <= 'd0;
    key_tmp1 <= 'd0;
   end
  else
   begin
    key_tmp <= Key_in;
    key_tmp1 <= key_tmp;  
   end
end/*---通过比较按键上一个状态和此时刻状态来获知按键状态是否改变---*/
assign level_change = (key_tmp == key_tmp1)?1'b0:1'b1;

   
以下为按键抖动检测的代码,采用状态机的方式编写,总共有两个状态,按下消抖为状态0,释放消抖为状态1。具体的消抖流程代码中的注释已经写的比较清楚,但如果全部用文字解释出来还是有一定的复杂性。这也是实地讲解和网上文档的一点点差距吧,希望我后期的视频里面能讲清楚。其实抖动消除的核心思路就是对按键状态的变化进行计时,若两次电平变化之间时间小于20ms,则视为抖动,若低电平稳定时间超过20ms,则表明检测到了稳定的按键状态。释放时的消抖过程与按下时的消抖过程类似。

always @ (posedge Clk or negedge Rst_n)
if(!Rst_n)
begin
  cnt1 <= 20'd0;
  state <= 1'b0;
  Key_Value <= 4'b0000;
  Key_Flag <= 1'b0;
end
else
begin
  case(state)
  0: /*按下检测*/
   //没有电平变化,且按键输入状态不全为1
   if(!level_change & key_tmp1 != {KEY_WIDTH{1'b1}})
    begin
     if(cnt1 == cnt1_TOP)/*计数满消抖所需时间*/
      begin
       Key_Value <= ~Key_in;
       Key_Flag <= 1;
       cnt1 <= 0;
       state <= 1;
      end
     else
      cnt1 <= cnt1 + 1'b1;
    end
   else
    begin
     cnt1 <= 0;
     Key_Flag <= 0;
     state <= 0;
    end
  
  1:/*释放检测*/
   begin
    Key_Flag <= 0;
    /*没有电平变化,且按键输入状态全为1*/
    if(!level_change & key_tmp1 == {KEY_WIDTH{1'b1}})
     begin
      if(cnt1 == cnt1_TOP)/*计数满消抖所需时间*/
       begin
        cnt1 <= 0;
        state <= 0;
       end
      else
       cnt1 <= cnt1 + 1'b1;
     end
    else
     begin
      cnt1 <= 0;
      state <= 1;
     end      
   end
  endcase
end


一、        测试平台设计

本实验主要对按键检测的结果进行观察和分析,通过仿真,验证设计的正确性和合理性。按键消抖模块的testbench的代码如下:
`timescale 1ns/1nsmodule normal_keys_detect_tb; reg Clk;
reg Rst_n;
reg [3:0]Key_in;

wire Key_Flag;
wire [3:0]Key_Value;

normal_keys_detect
#(
  .KEY_WIDTH(4)
)
normal_keys_detect_inst1(
  .Clk(Clk),
  .Rst_n(Rst_n),
  .Key_in(Key_in),
  .Key_Flag(Key_Flag),
  .Key_Value(Key_Value)
);

initial begin
  Clk = 1;
  Rst_n = 0;
  Key_in = 4'b1111;
  #100;
  Rst_n = 1;
  press_key(0);
  #30000000;
  press_key(1);
  #30000000;
  press_key(2);
  #30000000;
  press_key(3);
  #30000000;
  $stop;
end

always #10 Clk = ~Clk;

task press_key;
  input [1:0]Key;
  begin
   Key_in = 4'b1111;
   
   /*按下抖动*/
   #100 Key_in[Key] = 0;
   #200 Key_in[Key] = 1;
   #300 Key_in[Key] = 0;
   #400 Key_in[Key] = 1;
   #500 Key_in[Key] = 0;
   #600 Key_in[Key] = 1;
   #700 Key_in[Key] = 0;
   #800 Key_in[Key] = 1;
   #900 Key_in[Key] = 0;
   
   /*稳定期*/
   #22000000;
   
   /*释放抖动*/
   #100 Key_in[Key] = 1;
   #200 Key_in[Key] = 0;
   #300 Key_in[Key] = 1;
   #400 Key_in[Key] = 0;
   #500 Key_in[Key] = 1;
   #600 Key_in[Key] = 0;
   #700 Key_in[Key] = 1;
   #800 Key_in[Key] = 0;
   #900 Key_in[Key] = 1;
  end
endtaskendmodule


testben中使用了一个任务(task),该任务模拟按键抖动的过程,给按键按下和释放时增加抖动,调用时只需要输入需要按下的按键编号,该任务便可自动完成按下抖动、稳定、松开抖动的过程。

整个工程的testbench与消抖模块的testbench一样,只需要在例化部分将消抖模块替换为顶层模块,同时将每个按键的任务由一次调用该为两次调用即可,详细请参考附件代码。

一、        仿真分析


  
由上图仿真结果可知,当有按键按下时,需要较长一段时间后,Key_Flag会有一个高电平脉冲,同时Key_Value更新为输入按键的反码。

为了确定消抖是成功的,这里再附上按键松开时的抖动细节图:
由图可知,松开按键时,该按键IO不断的检测到高电平和低电平,直到一段时间和,抖动方停止,稳定为按键没有按下时的状态
下图为整个工程的仿真结果,由图可知,每按下一次按键0(key_in[0]),led[0]的状态便翻转一次。

  

一、        下板验证

手头暂无开发板,板级验证略。



二、        总结

本文档对按键消抖的原理进行了分析,并对消抖核心模块的设计进行了仿真,通过modelsim仿真验证了消抖模块设计的正确性。

具体的控制模块这里因为篇幅和时间关系暂不介绍,也因为没有开发板,暂时无法录制演示视频,等录制视频时,我会对整个系统的架构设计,代码设计进行详细的分析和讲解。以前没有做过不知道,写了两三次后才发现,原来文档的编写和整理比编写代码要的时间要多的多。不过,我总还是会坚持做下去的,希望我能有足够的时间来做这些事。

回帖(18)

张号

2014-11-17 15:47:17
总结的很好,学习学习
举报

新疆切糕

2014-11-18 09:59:59
支持小梅哥,学习学习!
举报

梅雪松

2014-11-18 10:14:10
各位,由于小梅哥最近工作了,公司安排给了很多任务,可能更新的计划暂时要搁浅一到两个月。当然大家也不要觉得可惜,因为当小梅哥阶段性工作完成后,工作中的成果都会发布到博客里面来,那时候,相信进过了公司资深工程师的严格检验,小梅哥写的东西一定能够比现在成熟和优秀
举报

小白报道

2014-11-28 20:43:49
怒给一赞.。。。。。。。。。。。。。。。
举报

skinge

2015-1-14 15:42:29
写的很好,给个赞。还有我没看到源程序的附件。
举报

梅雪松

2015-1-14 22:58:12
引用: skinge 发表于 2015-1-14 15:42
写的很好,给个赞。还有我没看到源程序的附件。

这里,目前主要是讲原理,方便需呀学习的筒子。其实核心代码都给出来了,只差端口和信号定义了。自己稍微捣鼓一下就能出来。源代码可能需要果断时间后一起发布
举报

xiekaboy

2015-1-21 11:27:54
受益了,感谢分享!!!
举报

jinmaoshu

2015-1-22 23:55:17
不错,写的很详细,可以留下联系方式多交流交流
举报

梅雪松

2015-1-23 16:18:58
引用: jinmaoshu 发表于 2015-1-22 23:55
不错,写的很详细,可以留下联系方式多交流交流

528369266  QQ
举报

zyhsyl

2015-1-25 23:16:34
感谢分享,谢谢了。。。
举报

赵志杰

2015-3-9 21:57:01
写的真心不错,希望楼主再出更多如此详细的技术分享
举报

枫林_tp

2015-3-11 11:37:45
收藏了谢谢楼主!!
举报

meng19901003

2015-3-16 17:12:20
学习了!!!!!
举报

唐建

2015-3-20 09:23:18
怒赞一个,总结的很好,学习了
举报

yulizi001

2015-11-24 16:05:01
谢谢,小梅哥的分享,结合你的视频教程看这个william hill官网 就更加清晰了,if(!level_change & key_tmp1 == {KEY_WIDTH{1'b1}})
这个中括号中的是什么意思啊
举报

梅雪松

2015-11-24 16:55:48
引用: yulizi001 发表于 2015-11-24 16:05
谢谢,小梅哥的分享,结合你的视频教程看这个william hill官网 就更加清晰了,if(!level_change & key_tmp1 == {KEY_WIDTH{1'b1}})
这个中括号中的是什么意思啊

加技术支持群一起讨论学习
举报

李耀

2015-12-7 14:16:34
内容支持小梅哥,谢谢小梅哥分享
举报

格古落

2016-1-19 16:44:49
小梅哥的资料是杠杠的,在下又学习到新东西了~
举报

更多回帖

发帖
×
20
完善资料,
赚取积分