新闻  |   论坛  |   博客  |   在线研讨会
笔记2 键盘消抖
0750long | 2010-03-10 21:19:07    阅读:15121   发布文章

笔记键盘消抖

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

 

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上一个时钟周期的值。 (注意<=非阻塞赋值,代码执行过后才赋值)。 

如上图假设,我们有五个时间周期,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的电平需要使用边缘检测法呢?而不是使用更简单的直接检查电平?这个问题先保留先吧。

*博客内容为网友个人发布,仅代表博主个人观点,如有侵权请联系工作人员删除。

参与讨论
登录后参与讨论
推荐文章
最近访客