一、按键消抖
按键消抖只是针对机械弹性开关按键,打开闭合时由于机械触点的弹性作用,一个按键在闭合后不会马上达到稳定状态,因此在闭合瞬间伴随一连串的抖动。必须对这部分抖动进行消抖处理。
按键消抖可以分为硬件消抖和软件消抖。
硬件消抖:主要用RS触发器和电容等方法消抖。(硬件消抖比较受限)
软件消抖:对按键信号延时5ms-20ms采样,也可以检测到按键稳定状态后采样。如图
1、1
软件消抖:这里按键稳定时间为20ms,系统晶振为50MHz,周期20ns,20ms/20ns=1000000,所以计数器最大值为999999。当检测到按键为低电平时计数器开始计数,计数到最大值时此时按键状态稳定,当系统检测到高电平时,对计数器清零,当计数器到最大值时,让计数器保持值不变,在定义一个标志信号对按键消抖的稳定状态进行采集,当计数器计数到最大值时,说明按键处于稳定状态。
module key_shake
#(
parameter CNT_MAX = 20'd999_999//计数最大值,20ms
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_in ,
output reg key_flag
);
reg [19:0] cnt;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt <= 20'd0;
else if(key_in == 1'b1)
cnt <= 20'd0;
else if(cnt == CNT_MAX)
cnt <= CNT_MAX;
else
cnt <= cnt + 1'b1;
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
key_flag <= 1'b0;
else if(cnt == CNT_MAX)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
endmodule
testbench
`timescale 1ns/1ns
module tb_key_shake();
reg sys_clk;
reg sys_rst_n;
reg key_in;
reg [7:0] tb_cnt;
wire key_flag;
initial
begin
sys_clk = 1'b0;
sys_rst_n <= 1'b0;
key_in <= 1'b1;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//把按键消抖过程压缩至250个系统周期
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tb_cnt <= 8'd0;
else if(tb_cnt == 8'd249)
tb_cnt <= 8'd0;
else
tb_cnt <= tb_cnt + 1'b1;
//模拟按键抖动
always@(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
tb_cnt <= 8'd0;
else if(tb_cnt <= 8'd24 || tb_cnt >= 8'd224)
key_in <= 1'b1;
else if((tb_cnt <= 8'd74 && tb_cnt > 8'd24)||(tb_cnt <= 8'd224 && tb_cnt >= 8'd174))
key_in <= {$random}%2;
else
key_in <= 1'b0;
key_shake
#(
.CNT_MAX(70)
)
key_shake_inst
(
.sys_clk (sys_clk),
.sys_rst_n (sys_rst_n),
.key_in (key_in),
.key_flag (key_flag)
);
endmodule
仿真结果把按键按下时长缩短到250个系统时钟周期。定义一个计数器tb_cnt,计数最大值为249,然后模拟按键被按下的过程。过程如下图所示。
仿真结果
由仿真结果得,每一次模拟按键的稳定状态都被标志信号key_flag采集到。
1、2
还有一种软件消抖写法,同样计数时间为20ms,对按键保存当前的值进寄存器,判断下一个周期的值是否不等于上一个周期,如果不等于,对计数器赋值,下一个周期如果相等计数器递减,如果一直相等,计数器一直递减到1,输出按键有效标志位。
module key_debounce(
input sys_clk, //外部 50M 时钟
input sys_rst_n, //外部复位信号,低有效
input key, //外部按键输入
output reg key_flag, //按键数据有效信号
output reg key_value //按键消抖后的数据
);
//reg define
reg [31:0] delay_cnt;
reg key_reg;
//*****************************************************
//** main code
//*****************************************************
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_reg <= 1'b1;
delay_cnt <= 32'd0;
end
else begin
key_reg <= key;
if(key_reg != key) //一旦检测到按键状态发生变化(有按键被按下或释放)
delay_cnt <= 32'd1000000; //给延时计数器重新装载初始值(计数时间为 20ms)
else if(key_reg == key) begin //在按键状态稳定时,计数器递减,开始 20ms 倒计时
if(delay_cnt > 32'd0)
delay_cnt <= delay_cnt - 1'b1;
else
delay_cnt <= delay_cnt;
end
end
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_flag <= 1'b0;
key_value <= 1'b1;
end
else begin
if(delay_cnt == 32'd1) begin //当计数器递减到 1 时,说明按键稳定状态维持了 20ms
key_flag <= 1'b1; //此时消抖过程结束,给出一个时钟周期的标志信号
key_value <= key; //并寄存此时按键的值
end
else begin
key_flag <= 1'b0;
key_value <= key_value;
end
end
end
endmodule
第一种软件消抖要比第二种软件消抖简单高效,用按键输入电平值作为判断。
1、3
光耦问题:
实际使用PS2801-4光耦,作为开关管起到隔离作用,输出端集电极接入到FPGA中,但是开关管有5us的开关稳定时间,光耦之前一直用作PWM输出,没有单独采集过信号。所以光耦是电流驱动型里面同样也有开关抖动时间的说法。
1、4
三极管是否有抖动:
1、5
MOSS管是否有抖动: