FPGA学习之按键去抖
按键仿真的例程,仿照黑金开发板03_key_detect_1例程,分为两个模块,分别是detect模块、delay模块,detect模块检测输入key_in信号的变化,delay模块负责延时去抖;
两个模块并不复杂,但是在test bench上仿真花费相当多的时间,波形图一直不是我想要的输出类型,折腾了一天,决定先下载到开发板上看看效果,结果效果却是我想要的输出类型,这就更加让我郁闷了,怎么仿真都不行呢?
按说当时应该可以明确的一点就是程序本省应该没有多大的问题,但是当时并没有意识到这里,这点以后还是要加强啊。随后我又多次查看的程序本身,并单独对detect模块进行仿真测试,波形是正确的,delay模块的波形始终有问题,经过分析,我将原来的test bench文件又看了看,并对应波形比较了比较,发现问题出现仿真的周期上。看下test bench文件,
`timescale 10ns / 1ps
module key_tf;
// Inputs
reg clk;
reg rst;
reg key_in;
parameter T = 5'd20;
parameter TMS = 21'd1500_000; //15ms
// Outputs
wire LED;
// Instantiate the Unit Under Test (UUT)
key uut (
.clk(clk),
.rst(rst),
.key_in(key_in),
.LED(LED)
);
always begin
clk = 1;
# (T/2);
clk = 0;
# (T/2);
end
always begin
key_in = 1;
#(TMS);
key_in = 0;
#(TMS*10);
end
initial begin
// Initialize Inputs
clk = 0;
rst = 1;
key_in = 0;
// Wait 100 ns for global reset to finish
#100;
rst = 0;
// Add stimulus here
end
endmodule
其实这里没什么东西,很简单的语句,就是自己在clk这里犯了浑,搞错了,而detect模块中基本上算是没有用到clk,所以delay模块出现问题时没有及时的找到问题的根源,我们知道test bench文件中已经设定clk的周期,这是我们通过always文件自己定义的,现在仿真的周期是10ns,clk的周期是我们设定的T = 20*10 = 200ns,而我却把10ns作为了我delay模块的时钟周期,所以造成了波形时钟延后很长一段时间,造成逻辑因此出现错误,在这个仿真周期上我算是栽了一个跟头,今后一定要引以为戒。把周期调整之后,波形就正确了,但是有一定的延时,估计是布线或者其他原因引起的问题,我暂时还没有学习到这一块,加油,继续FPGA之旅。
看了许久的按键消除抖动的例程,特权的、黑金的,对于按键的消抖有了一些了解,首先先说说我们单片机通常的消抖是怎样做的,read按键值,如果按键被按下,延时10ms,再次读取按键值,如果按键确实被按下,则确认按键动作执行。这种按键去抖的方式有时候并不是特别管用,可以采用按键按下,然后抬起作为一次按键操作,执行流程是如下程序,
uint8_t keydata = 0;
static uint8_t keyup = 0;
if(keyup && (keydata ^ 0xf0))
{
keyup = 0;
//延时10ms
Delay(10);
//读取按键值
keydata = GPIO_ReadInputPin(GPIOG, (GPIO_Pin_TypeDef)(HR1|HR2|HR3));
if(keydata ^ 0xf0)
{
return keydata;
}
}
//无按键按下时至keyup标志位
else if((keydata ^ 0xf0) == 0)
{
keyup = 1;
return 0;
}
这个程序是我用的最多的按键去抖程序,他的优点很明显就是能够防止按键误动作,不会出现按一次按键出现多次按键值的情况,缺点就是按键按一次只能有一次按键值,不能做连续的按键值输出,比如说有需要按键按下按键值能够连续相加的情况。
FPGA的按键去抖与单片机的去抖就会有些不同,这里也需要对按键进行去抖,当然消抖必然会进行延时,毕竟这些抖动信号是我们不需要的,黑金的例程中按键消抖处理感觉有点小问题,这里先看按键去抖的例程:
去抖程序分为两个模块,detect_module.v为电平检查模块
CLK, RSTn, Pin_In, H2L_Sig, L2H_Sig
/**********************************/
parameter T100US = 11'd4_999;//DB4CE15开发板使用的晶振为50MHz,50M*0.0001-1=4_999
/**********************************/
always @ ( posedge CLK or negedge RSTn )
/********************************************/
always @ ( posedge CLK or negedge RSTn )
/***********************************/
assign H2L_Sig = isEn ? ( H2L_F2 & !H2L_F1 ) : 1'b0;
assign L2H_Sig = isEn ? ( !L2H_F2 & L2H_F1 ) : 1'b0;
/***********************************/
CLK, RSTn, H2L_Sig, L2H_Sig, Pin_Out
/****************************************/
parameter T1MS = 16'd49_999;//DB4CE15开发板使用的晶振为50MHz,50M*0.001-1=49_999
/***************************************/
always @ ( posedge CLK or negedge RSTn )
else if( isCount && Count1 == T1MS )
/****************************************/
always @ ( posedge CLK or negedge RSTn )
else if( isCount && Count1 == T1MS )
/******************************************/
always @ ( posedge CLK or negedge RSTn )
if( Count_MS == 4'd10 ) begin isCount <= 1'b0; rPin_Out <= 1'b1;
if( Count_MS == 4'd10 ) begin isCount <= 1'b0; rPin_Out <= 1'b0;
/********************************************/
/********************************************/
延时程序在接收到H2L_Sig和L2H_Sig时会跳转到相应的case条件,如果是H2L_Sig高到低信号产生,首先延时10ms,然后Pin_Out输出高电平,当接收到L2H_Sig低到高信号产生,同样首先延时10ms,然后Pin_Out输出低电平,这样程序就会在按键按下时点亮LED灯,按键抬起后熄灭LED灯,延时10ms达到消除抖动的目的。
黑金的按键去抖程序总体而言是非常清晰,容易理解,也比较实用,接下来看看特权的按键消抖程序。
/说明:当三个独立按键的某一个被按下后,相应的LED被点亮;
input sw1_n,sw2_n,sw3_n; //三个独立按键,低表示按下
output led_d1,led_d2,led_d3; //发光二极管,分别由按键控制
//---------------------------------------------------------------------------
always @(posedge clk or negedge rst_n)
if (!rst_n) key_rst <= 3'b111;
else key_rst <= {sw3_n,sw2_n,sw1_n};
always @ ( posedge clk or negedge rst_n )
if (!rst_n) key_rst_r <= 3'b111;
//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期
wire[2:0] key_an = key_rst_r & ( ~key_rst);
//wire [2:0]key_an = key_rst_r ^ key_rst; // 注:也可以这样写
//---------------------------------------------------------------------------
always @ (posedge clk or negedge rst_n)
if (!rst_n) cnt <= 20'd0; //异步复位
always @(posedge clk or negedge rst_n)
else if (cnt == 20'hfffff) //满20ms,将按键值锁存到寄存器low_sw,cnt == 20'hfffff
low_sw <= {sw3_n,sw2_n,sw1_n};
//---------------------------------------------------------------------------
reg [2:0] low_sw_r; //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中
always @ ( posedge clk or negedge rst_n )
if (!rst_n) low_sw_r <= 3'b111;
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期
wire[2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);
always @ (posedge clk or negedge rst_n)
else begin //某个按键值变化时,LED将做亮灭翻转
assign led_d3 = d1 ? 1'b1 : 1'b0; //LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;
wire[2:0] key_an = key_rst_r & ( ~key_rst);
这里明显是1到0时key_an为高,这里与单个信号是操作不同的是key_rst用的是取反而不是取非操作,状态由1到0是按键刚按下的时刻,key_an输出为高才能进行下面的操作。
wire[2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);
按键去抖是一个简单的小程序,但是有时候一点改变影响的效果确实是不一样的,就像用C语言写的按键去抖,一个抬起有效的按键去抖方式就能很好解决按键抖动的问题,FPGA也一样。