原创 CPLD笔记2 键盘消抖

2010-3-6 11:59 2340 10 12 分类: FPGA/CPLD

笔记键盘消抖<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


老实说,这个实验的开始之前和之后,都给我蛋疼了。时钟了解不到源码的思路,边看源码边睡着。醒来的时候既然 “惊” 一下,相通了......


 


module lesson02


 (


CLK, RST,


SW0, SW1, SW2,


LED0, LED1, LED2


);



input   CLK;


input   RST;


input  SW0, SW1, SW2;


output  LED0, LED1, LED2;


 


//---------------------------------------------------------------------------


//Detect the switch pressing



reg  [2:0] Press0;  


reg  [2:0] Press1; 


wire [2:0] isPress;



always @ (posedge CLK or negedge RST)


if(!RST)


   Press0 <= 3'b111;


else


   Press0 <= {SW0, SW1, SW2};  //read the pin result;


 


always @ (posedge CLK or negedge RST)


if(!RST) 


   Press1 <= 3'b111;


else


   Press1 <= Press0;  //read the previous Press0 result 


   


assign isPress = Press1 & (~Press0);   //detect the logic with bit is changed from logic 1 to 0



//---------------------------------------------------------------------------


//if pressing, counter start counting for 20ms


 


reg [19:0] Counter; //计数寄存器


 


always @ (posedge CLK or negedge RST)


if(!RST)


   Counter <= 20'd0;


else if(isPress)


   Counter <= 20'd0;


else


   Counter <= Counter + 1'b1; //increment for counter


 


//------------------------------------------------------------------------


//After 20ms read the key pin result



reg  [2:0] Press2;


reg  [2:0] Press3;


wire [2:0] Result;


 


always @ (posedge CLK or negedge RST)


if(!RST)


begin


   Press2 <= 3'b111;


   Press3 <= 3'b111;


end


else if(Counter == 20'hfffff)


   Press2 <= {SW0, SW1, SW2}; //read the pin result after 20ms


else



    always @ (posedge CLK or negedge RST)


if(!RST)



else 


   Press3 <= Press2; //read the previous pin result


 


assign Result = Press3 & (~Press2);  //detect the changing bit from logic 1 to 0


 


//------------------------------------------------------------------------


//turn on led with pin result



reg D1;


reg D2;


reg D3;


 


always @ (posedge CLK or negedge RST)


if(!RST)


begin


   D1 <= 1'b0;


   D2 <= 1'b0;


   D3 <= 1'b0;


end


else 


begin


   if( Result[0] ) D1 <= ~D1;


   if( Result[1] ) D2 <= ~D2;


   if( Result[2] ) D3 <= ~D3;


end


 


assign LED0 = D1 ? 1'b1 : 1'b0;


assign LED1 = D2 ? 1'b1 : 1'b0;


assign LED2 = D3 ? 1'b1 : 1'b0;


  


endmodule



这个实验主要有计数器和边缘检查来实现按键消抖,按键功能。该代码新手打从一开始看会觉得很难(我也却是如此)。不过只要一部一部的分析就会觉得很容易。



程式定义了SW0~2RSTCLK为输入,而LED0~2为输出,他们默认均是wire型。


 


                                                                                                                                   reg  [2:0] Press0;  


reg  [2:0] Press1; 


wire [2:0] isPress;


 


always @ (posedge CLK or negedge RST)


if(!RST)


   Press0 <= 3'b111;


else


   Press0 <= {SW0, SW1, SW2};  //read the pin result;


 


always @ (posedge CLK or negedge RST)


if(!RST) 


   Press1 <= 3'b111;


else


   Press1 <= Press0;  //read the previous Press0 result 


   


assign isPress = Press1 & (~Press0);   //detect the logic with bit is changed from logic 1 to 0



 


这一段代码主要是用来检测 “按键是否被按下”。程式依然建立了两个寄存器 press 0 和 press 1 ,和一个wire型的isPress 。(注意,全部都是3位)。Press0 每次时钟的上升沿都会读取 SW0SW1,和SW2的电平(回顾原理图,该3各按键都带上拉电阻)。而Press1 分别每一个时钟的上升沿读取 寄存器 Press0上一个时钟周期的值。 (注意<=非阻塞赋值,代码执行过后才赋值)。 



29b2bdf2-11c5-4ee4-b8c4-094b670f9cff.jpg



如上图假设,我们有五个时间周期,SW0为例子,SW0Reg0Reg1初始电平都是高电平。SW0的电平初始是高电平,在时间01的时候,Reg0读取到1,而Reg1读取Reg0上一个周期的值(00周期的值),也是1。当SW002周期之前发生电平变化,由高到低,而在02周期的时候,Reg0读取SW0的值,是0Reg1则读取Reg0上一个周期的值(01周期的值),依然是1.



02时间边缘检查就可以发生作用了。如以上的代码,Reg0Press0,而Reg1Press1



assign isPress = Press1 & (~Press0); 



02周期的时候,如以上的组合逻辑,Reg0的值会被取反,然后与计算Reg1的值。结果会是1



虽然以上给出的例子有点勉强。但是思维是一样的。如果SW0的电平持续1,那么 isPress的结果会一直是0,相反的,如果SW0的电平持续0或者发生过变化,那么isPress的结果会是1.



话题扯远了,回归正题。接下来是计数寄存器。


 

reg [19:0] Counter; //计数寄存器


 


always @ (posedge CLK or negedge RST)


if(!RST)


   Counter <= 20'd0;


else if(isPress)


   Counter <= 20'd0;


else


   Counter <= Counter + 1'b1; //increment for counter



如上代码的意思,计数器在每个时间的上升沿都会递增,只有两个情况会3个情况会清零,


1-计数超过10485752-复位信号,3-当检查到SW0~2的电平发生变化。很显然选线3是重点。该计数器主要是利用于,当SW0~2电平发生变化,执行长达20ms的计数,为了是实现软件消抖。


 



always @ (posedge CLK or negedge RST)


if(!RST)


begin


   Press2 <= 3'b111;


   Press3 <= 3'b111;


end


else if(Counter == 20'hfffff)


   Press2 <= {SW0, SW1, SW2}; //read the pin result after 20ms


else



    always @ (posedge CLK or negedge RST)


if(!RST)



else 


    Press3 <= Press2; //read the previous pin result


 


assign Result = Press3 & (~Press2);  //detect the changing bit from logic 1 to 0



以上的代码和第一个的代码的功能基本相通,第一个代码是20ms秒之前读取SW0~2的电平,而这个代码是20ms秒之后读取SW0~2的电平,亦即是按键按下之前和按键按下之后。


同样也是实现边缘检查。


 


 


                                                                                                                            reg D1;


reg D2;


reg D3;


 


always @ (posedge CLK or negedge RST)


 

if(!RST) begin


   D1 <= 1'b0;


   D2 <= 1'b0;


   D3 <= 1'b0;


end


else  begin


   if( Result[0] ) D1 <= ~D1;


   if( Result[1] ) D2 <= ~D2;


   if( Result[2] ) D3 <= ~D3;


end


 


assign LED0 = D1 ? 1'b1 : 1'b0;


assign LED1 = D2 ? 1'b1 : 1'b0;


assign LED2 = D3 ? 1'b1 : 1'b0;



 


最后这一段代码比较简单。就是分别将Result寄存器的结果,经if判断取反D1~D3的值。


最后利用3目表达式对LED0~2进行赋值。


 



简单归纳:


嗯,这一章笔记基本上拖了一段时间。主要的问题是边缘检测和还未习惯于RTL的思维。


这篇笔记很有很多漏洞之处,有望多多包含。本人依然是新手上路,在任何角度上还要多多请教。希望这一章笔记可以帮到你。


 


补上:这里有一个问题点?为什么检测IO的电平需要使用边缘检测法呢?而不是使用更简单的直接检查电平?这个问题先保留先吧。

PARTNER CONTENT

文章评论2条评论)

登录后参与讨论

用户377235 2014-6-26 16:23

上面代码中关于20ms延时消抖,个人觉得你逻辑上有问题,你的注释是这样的:“1-计数超过1048575,2-复位信号,3-当检查到SW0~2的电平发生变化。很显然选线3是重点。该计数器主要是利用于,当SW0~2电平发生变化,执行长达20ms的计数,为了是实现软件消抖。” 按键没按下时,isPress信号为0;如果三按键中有任意按键按下,则isPress信号为非0,非0时应该启动20ms的延时计数器。以下是你的代码逻辑: always @ (posedge CLK or negedge RST) if(!RST) Counter <= 20'd0; else if(isPress) //has error!!! Counter <= 20'd0; else Counter <= Counter + 1'b1; //increment for counter 个人拜读了你写的《Verilog HDL——那些事儿 建模篇v5》第二章:低级建模- 基础知识--实验三:消抖模块之一。其中10ms的延时模块根本没有起到延时消抖的作用,应为你的代码逻辑中10ms延时后没有进行再采样,直接输出延时之前的采样结果。

用户1334174 2013-7-3 08:52

感谢!
相关推荐阅读
用户1609127 2011-10-22 18:26
Verilog的私私细语 - 时钟化和信号的长度
目录 第2章 时钟化和信号的长度 2.01 一个时钟一块数据的概念 2.02 信号时钟化 2.03 深入了解模块的沟通 2.04 电平检测模块的整合(即时事件在时序上的不和谐)      实验八:电...
用户1609127 2011-09-08 12:47
瞎搞Time Quest 和无责任的笔记 第二章
最近整合篇的第二章的构思和灵感都累积不少了,应该是时候开工了。恰好手头上还有一本笔记还没有写完,就是这本瞎搞TimeQuest的第二章。目录笔者也懒得贴了,看过第一章的同学多少也会猜到第二章的内容是什...
用户1609127 2011-08-29 18:21
瞎搞Time Quest 和无责任的笔记 第一章
哎呀 ... 潜水了都有一段时间了,这是最近研究的成果和目标。 话说TimeQuest这个东西真的很搞怪呀,做得笔者不得不从其他的方向去研究它。 好了还是切入正题,TimeQuest用作静态时序的工具...
用户1609127 2011-07-06 17:43
Verilog的私私细语 - 整合的概念
目录         02  第1章  整合的概念          1.01  源码上的整合                   实验一:字面上的整合          1.02  时钟和步骤的定...
用户1609127 2011-06-22 10:18
VerilogHDL那些事儿 - 建模篇v4 + 时序篇v1
VerilogHDL那些事儿 - 建模篇v4 ====== v4 ====== 主要是修改了大量用法上的BUG和极限的精简内容 https://docs.google.com/leaf?id=0B...
用户1609127 2011-06-10 13:19
Verilog HDL的礼物 - Verilog HDL扫盲文
目录 02第0章 Verilog HDL语言扫盲文 030.01 各种的HDL语言 030.02 HDL语言的层次 03 0.03 RTL级和组合逻辑级 040.04 Verilog HDL语言真的那...
EE直播间
更多
我要评论
2
10
关闭 站长推荐上一条 /3 下一条