FPGA系统性学习笔记连载_Day15【按键检测、按键消抖】 【原理及verilog仿真】篇
本系列为FPGA系统性学习学员学习笔记整理分享,如有学习或者购买开发板意向,可加交流群联系群主。
连载《叁芯智能fpga设计与研发-第15天》 【按键检测、按键消抖】 【原理及verilog仿真】
原创作者:紫枫术河 转载请联系群主授权,否则追究责任
本篇文章,记录按键检测原理、按键消抖原理,及verilog代码实现及仿真。
一、按键检测
按键事件:分为按下事件、抬起事件,判断按键的状态是否改变,需要比较连续2个clk的电平是否发生改变,我用一张图,形象展示按键的检测原理。
1.1、从图中可以看出,我们在2个时钟的上升沿采集了2个电平值,A、B
1.2、如果 A=1,B=0,则为按下事件,A=0,B=1,则为抬起事件。
1.3、所以我们需要连续2次对按键信号进行采样,然后比较这2次的采样值是否发生改变,来确定是按下还是抬起事件;
1.4、用 assign key_down = A&(~B),检测按下,用assign key_up = (~A)&B,检测抬起。
1.5、上面的采集判断,相当于下面这个电路,他只对信号进行了一级缓存,如下图
1.6、上面的电路,检测到下降沿后,我们逻辑产生的脉冲宽度是随机的,最大一个clk,这个宽度是不能作为FPGA其他逻辑的输入信号,因为会导致亚稳态
1.7、为了解决上面的问题,我们采用2级缓存,如下电路
从这个图,可以看出我们2个寄存器,用这2个寄存器缓存的值来判断,就能避免脉宽变窄的问题
1.8、verilog代码实现
代码语言:javascript复制module edge_check(
input signal,
input clk,
input rst_n,
output flag_up,
output flag_down
);
reg [1:0] signal_temp;
always@(posedge clk,negedge rst_n)
begin
if(!rst_n)
signal_temp <= 2'b0;
else
begin
signal_temp[0] <= signal;
signal_temp[1] <= signal_temp[0];
end
end
assign flag_down = signal_temp[1]&(~signal_temp[0]);
assign flag_up = (~signal_temp[1])&signal_temp[0];
endmodule
1.9、代码仿真
从图中看出,下降沿、上升沿检测正确
二、按键消抖
由于按键的机械按键,按下的过程会有抖动,这个抖动时间在5-20ms,我们取10ms进行判断
1.1、verilog代码,
edge_check.v key_fiter.v
edge_check为按下的边缘检测模块,用来启动key_fiter.v模块内的一个定时计数模块
1.2、edge_check.v
代码语言:javascript复制module edge_check(
input signal,
input clk,
input rst_n,
output flag_up,
output flag_down
);
reg [1:0] signal_temp;
always@(posedge clk,negedge rst_n)
begin
if(!rst_n)
signal_temp <= 2'b0;
else
begin
signal_temp[0] <= signal;
signal_temp[1] <= signal_temp[0];
end
end
assign flag_down = signal_temp[1]&(~signal_temp[0]);
assign flag_up = (~signal_temp[1])&signal_temp[0];
endmodule
1.3、key_fiter.v
代码语言:javascript复制module key_fiter(
input signal,
input clk,
input rst_n,
output reg flag_up,
output reg flag_down
);
parameter T_10ms = 500_000;
reg [18:0] cnt;
wire flag_up_r;
wire flag_down_r;
edge_check edge_check_inst(
.signal (signal),
.clk (clk),
.rst_n (rst_n),
.flag_up (flag_up_r),
.flag_down (flag_down_r)
);
reg start_timer;
reg clr_timer;
always@(posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
cnt <= 0;
flag_up <= 1'b0;
flag_down <= 1'b0;
end
else if(clr_timer)
begin
cnt <= 0;
flag_up <= 1'b0;
flag_down <= 1'b0;
end
else if(start_timer)
begin
if(signal == 1'b0) //信号线0,10ms计数
begin
if(cnt >= T_10ms -1)
begin
cnt <= 1'b0;
flag_down <= 1'b1;
end
else
begin
cnt <= cnt 1'b1;
flag_down <= 1'b0;
end
end
else //信号线1,10ms计数
begin
if(cnt >= T_10ms -1)
begin
cnt <= 1'b0;
flag_up <= 1'b1;
end
else
begin
cnt <= cnt 1'b1;
flag_up <= 1'b0;
end
end
end
else
begin
cnt <= 0;
flag_up <= 1'b0;
flag_down <= 1'b0;
end
end
//判断启动定时器和清零计时器值
always@(posedge clk,negedge rst_n)
begin
if(!rst_n)
begin
start_timer <=0;
clr_timer <=0;
end
else if(flag_down_r || flag_up_r)
begin
start_timer <= 1'b1;
clr_timer <= 1'b1;
end
else
begin
clr_timer <= 1'b0;
if(cnt == T_10ms -1)
start_timer <= 1'b0;
else
start_timer <= start_timer;
end
end
endmodule
1.4、仿真模块 key_fiter_tb.v
代码语言:javascript复制`timescale 1ns/1ps
module key_fiter_tb;
reg signal;
reg clk;
reg rst_n;
wire flag_up;
wire flag_down;
key_fiter key_fiter(
.signal (signal),
.clk (clk),
.rst_n (rst_n),
.flag_up (flag_up),
.flag_down (flag_down)
);
always #10 clk = ~clk;
initial begin
clk = 0;
signal = 1;
rst_n = 0;
#20;
rst_n = 1;
//模拟按下
signal = 1;
//延时15ms
#15000000;
repeat(50)
begin
//抖动
signal = 0;
#10;
signal = 1;
#15;
//抖动
signal = 0;
#17;
signal = 1;
#26;
end
signal = 0;
//延时15ms
#15000000;
//模拟抬起
signal = 1;
repeat(50)
begin
//抖动
signal = 0;
#10;
signal = 1;
#15;
//抖动
signal = 0;
#17;
signal = 1;
#26;
end
signal = 1;
#500;
//延时15ms
#15000000;
$stop;
end
endmodule
1.5、仿真结果
从图中看,按键按下和抬起,都有很长时间的抖动,但是按键的有效脉冲都是在10ms后才给出的。
【QQ交流群】
群号:173560979,进群暗语:FPGA技术江湖粉丝。
多年的FPGA企业开发经验,各种通俗易懂的学习资料以及学习方法,浓厚的交流学习氛围,QQ群目前已有1000多名志同道合的小伙伴,无广告纯净模式,给技术交流一片净土,从初学小白到行业精英业界大佬等,从军工领域到民用企业等,从通信、图像处理到人工智能等各个方向应有尽有。
【微信交流群】
现微信交流群已建立09群,人数已达数千人,欢迎关注“FPGA技术江湖”微信公众号,可获取进群方式。
完
后续会持续更新,带来Vivado、 ISE、Quartus II 、candence等安装相关设计教程,学习资源、项目资源、好文推荐等,希望大侠持续关注。
江湖偌大,继续闯荡,愿大侠一切安好,有缘再见!