几乎没有哪一个系统没有输入输出设备,大到显示器,小到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;
[/mw_shl_code]
以下为按键抖动检测的代码,采用状态机的方式编写,总共有两个状态,按下消抖为状态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[/mw_shl_code]
一、 测试平台设计
本实验主要对按键检测的结果进行观察和分析,通过仿真,验证设计的正确性和合理性。按键消抖模块的testbench的代码如下:
`timescale 1ns/1ns
module 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
endtask
endmodule [/mw_shl_code]
testben中使用了一个任务(task),该任务模拟按键抖动的过程,给按键按下和释放时增加抖动,调用时只需要输入需要按下的按键编号,该任务便可自动完成按下抖动、稳定、松开抖动的过程。
整个工程的testbench与消抖模块的testbench一样,只需要在例化部分将消抖模块替换为顶层模块,同时将每个按键的任务由一次调用该为两次调用即可,详细请参考附件代码。
一、 仿真分析
由上图仿真结果可知,当有按键按下时,需要较长一段时间后,Key_Flag会有一个高电平脉冲,同时Key_Value更新为输入按键的反码。
为了确定消抖是成功的,这里再附上按键松开时的抖动细节图:
由图可知,松开按键时,该按键IO不断的检测到高电平和低电平,直到一段时间和,抖动方停止,稳定为按键没有按下时的状态
下图为整个工程的仿真结果,由图可知,每按下一次按键0(key_in[0]),led[0]的状态便翻转一次。
一、 下板验证
手头暂无开发板,板级验证略。
二、 总结
本文档对按键消抖的原理进行了分析,并对消抖核心模块的设计进行了仿真,通过modelsim仿真验证了消抖模块设计的正确性。
具体的控制模块这里因为篇幅和时间关系暂不介绍,也因为没有开发板,暂时无法录制演示视频,等录制视频时,我会对整个系统的架构设计,代码设计进行详细的分析和讲解。以前没有做过不知道,写了两三次后才发现,原来文档的编写和整理比编写代码要的时间要多的多。不过,我总还是会坚持做下去的,希望我能有足够的时间来做这些事。
|