笔记2 键盘消抖<?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~2,RST,CLK为输入,而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 每次时钟的上升沿都会读取 SW0,SW1,和SW2的电平(回顾原理图,该3各按键都带上拉电阻)。而Press1 分别每一个时钟的上升沿读取 寄存器 Press0上一个时钟周期的值。 (注意<=非阻塞赋值,代码执行过后才赋值)。
如上图假设,我们有五个时间周期,SW0为例子,SW0,Reg0,Reg1初始电平都是高电平。SW0的电平初始是高电平,在时间01的时候,Reg0读取到1,而Reg1读取Reg0上一个周期的值(00周期的值),也是1。当SW0在02周期之前发生电平变化,由高到低,而在02周期的时候,Reg0读取SW0的值,是0,Reg1则读取Reg0上一个周期的值(01周期的值),依然是1.
在02时间边缘检查就可以发生作用了。如以上的代码,Reg0是Press0,而Reg1是Press1。
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-计数超过1048575,2-复位信号,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的电平需要使用边缘检测法呢?而不是使用更简单的直接检查电平?这个问题先保留先吧。
用户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