前言
学习说明此文档为本人的学习笔记,注重实践,关于理论部分会给出相应的学习链接。
学习视频:是根据野火FPGA视频教程——第十六讲 https://www.bilibili.com/video/BV1nQ4y1Z7zN?p=3
理论学习
按键常常作为系统复位信号和控制信号的外部输入,主要分为自锁按键、机械按键和薄膜按键等。开发板上一般采用机械按键,但是当机械触电断开、闭合时,由于机械触电的弹性作用,一个按键开关在闭合时不会马上稳定导通,断开时不会一下子完全断开,会出现瞬间的抖动。影响按键的输入状态,因此需要对按键进行消抖处理。
抖动时常由按键本身决定,一般在5ms~10ms。
1、硬件消抖
????????按键较少时可以使用硬件消除抖动,即使其在状态第一次出发后就保持。这里用到了RS触发器作为常用的硬件去抖。
?当按键未按下时输出0,按下为1。按键因弹性抖动而产生瞬时断开(抖动跳开B),只要不返回原始状态A,双稳态电路的状态不会改变,输出保持0,不会产生抖动波形。
2、软件消抖
????????当按键较多时使用硬件消抖需要占用太多资源,因此通过软件编程消抖。即检测到按键闭合后执行一个延时程序,由于抖动时间在5~10ms,我们产生一个20ms的延时,当前后两个状态相同时才能确定按键的真实状态
实战演练
1、设计规划
实现一个按键消抖模块,将按键做消抖处理后正常被其他模块调用
2、程序设计
2.1 波形图绘制
按键脉冲产生方式:
- 传统方法:当按键被检测按下时,延时20ms+10ms时间重新检测,如果依然是低电平则产生一个时钟单位的脉冲。问题:这里计数器需要将10ms的时间计算在内浪费资源。
- 脉冲计数器:当检测到按键被按下时开始脉冲计数,当20ms后(50MHz,计数到999999)后产生一个时钟单位的脉冲,且重新计数。当按键未按下时,计数器清零。问题:当按键按的时间过长时,将会产生多个脉冲。
- 保持计数:当计数器计到999999时,计数器的值将会保持,只有检测到按键未按下的高电平时,才会被清零。因此计数器确定被按下时计数器的999998值只会出现一次。这时产生一时钟周期的脉冲即可。
2.2 代码编写?
module key_filter
#(
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(key_in == 1'b0 && 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 - 1'b1)
key_flag <= 1'b1;
else
key_flag <= 1'b0;
endmodule
三、逻辑仿真
3.1 仿真文件的编写
`timescale 1ns / 1ns
//
// Company: 追逐者——桥的小作坊
// Create Date: 2022/05/12 17:19:33
// Design Name: 按键消抖
// Module Name: tb_key_filter
//
module tb_key_filter();
reg sys_clk ;
reg sys_rst_n ;
reg key_in ;
reg [7:0] tb_cnt ;
wire key_flag ;
initial begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#20
sys_rst_n <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
//通过计数器模拟按键抖动过程
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 + 8'd1;
always @(posedge sys_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
key_in <= 1'b1;
else if(((tb_cnt >= 8'd19) && (tb_cnt <= 8'd49))
||((tb_cnt >= 8'd149) && (tb_cnt <= 8'd199)))
key_in <= {$random} % 2;
else if((tb_cnt < 8'd19) || (tb_cnt > 8'd199))
key_in <= 1'b1;
else
key_in <= 1'b0;
//实例化
key_filter
#(
.CNT_MAX(20'd24) //最大计数时间20ms
)
key_filter_inst
(
.sys_clk (sys_clk ),
.sys_rst_n(sys_rst_n),
.key_in (key_in ),
.key_flag (key_flag )
);
endmodule
3.2 仿真波形图对比
?
|