FPGA基础入门篇(六) 按键防抖电路实现
本次实验的按键消抖电路实现用Zynq 7000 系列的FPGA实现,时钟频率为500MHz, 按键消抖延时为20ms。
此次电路的实现涉及到如下过程:
- 按键作为异步信号输入,需要进行同步处理。可以采用两级以上的异步复位,同步释放的dff进行时钟同步处理。(异步复位,同步释放,可利用多级边沿检测电路)
- 根据抖动时间和时钟周期,确定计数器位宽:20ms。
关于防抖电路实现的具体例程和用其他方法实现:
具体例程:FPGA基础入门篇(六) 按键防抖电路实现(二)
其他实现方法:FPGA基础入门篇(六) 按键防抖电路实现(三)
一、抖动的产生
按键的示意图如下,一般为低电平有效,当按键由高电平转向低电平的瞬间会有短暂的高阻态,因此电路需要RS触发器存储电路保存电路的当前状态。但是实际的按键并不是理想的按键,当按键按下时中间会产生抖动状态,即产生脉冲的干扰。
抖动的信号如下所示:
二、消抖的方式
- Verilog 代码设计实现
module key(
input clk_i, rst_n_i, key_i,
output reg key_state,
output reg key_flag
);
//parameter for satate machine
parameter S1 = 2'b00; //key=1
parameter S2 = 2'b01;
parameter S3 = 2'b11;
parameter S4 = 2'b10;
//key_edge detect module 边沿检测电路模块。
//
wire data_i;
reg tri_1;
reg tri_2;
wire negedge_o;
wire posedge_o;
always@(posedge clk_i or negedge rst_n_i)
begin
if (! rst_n_i)
begin
tri_1 <= 1'b0;
tri_2 <= 1'b0;
end
else
begin
tri_1 <= data_i;
tri_2 <= tri_1;
end
end
assign negedge_o = tri_2 & (~tri_1); // neg_edge detect
assign posedge_o = (~tri_2) & tri_1; // pos_edge detect
assign data_i = key_i;
//counter module 计数器模块
//
reg [19:0]cnt;
reg en_cnt; //the enable signal start
reg count_full; //the flag of count is 20ms
//count
always@(posedge clk_i or negedge rst_n_i)
begin
if(!rst_n_i)
cnt <= 20'b0;
else if (en_cnt)
cnt <= cnt + 1'b1;
else if(count_full) //2nd set
cnt <= 20'b0;
else
cnt <= 20'b0;
end
//count_full flag
always@(posedge clk_i or negedge rst_n_i)
begin
if(!rst_n_i)
begin
count_full <= 1'b0;
end
else if (cnt == 20'd3_999_999)
count_full <= 1'b1;
else
count_full <= 1'b0;
end
//state machine 状态机模块
//
reg [1:0]state;
//
always@(posedge clk_i or negedge rst_n_i)
begin
if (!rst_n_i)
begin
en_cnt <= 1'b0;
state <= S1;
key_state <= 1'b1;
key_flag <= 1'b0;
end
else
begin
case (state)
S1: //IDLE 空闲状态
begin
key_state <= 1'b1;
key_flag <= 1'b0;
if (negedge_o)
begin
state <= S2;
en_cnt <= 1'b1;
end
else
state <= S1;
end
S2: //filter 按下滤波状态等待20ms延时
begin
if (count_full)
begin
key_state <= 1'b0;
key_flag <= 1'b1;
en_cnt <= 1'b0;
state <= S3;
end
else if (posedge_o)
begin
state <= S1;
en_cnt <= 1'b0;
end
else
state <= S2;
end
S3: // 按下状态,等待抬起
begin
key_flag <= 1'b0;
key_state <= 1'b0;
if (posedge_o)
begin
state <= S4;
en_cnt <= 1'b1;
end
else
state <= S3;
end
S4: //抬起状态,等待20ms延时
begin
if (count_full)
begin
key_flag <= 1'b1;
key_state <= 1'b1;
en_cnt <= 1'b0;
state <= S1;
end
else if (negedge_o)
begin
state <= S3;
en_cnt <= 1'b0;
end
else
state <= S4;
end
default:
begin
en_cnt <= 1'b0;
state <= S1;
key_state <= 1'b1;
key_flag <= 1'b0;
end
endcase
end
end
endmodule
- 测试代码:
`timescale 1ns / 1ps
//
module key_test(
);
reg clk_i, rst_n_i,key_i;
wire key_state, key_flag;
//instance
key u1(
.clk_i(clk_i),
.rst_n_i(rst_n_i),
.key_i(key_i),
.key_state(key_state),
.key_flag(key_flag)
);
initial
begin
clk_i = 1;
rst_n_i = 0;
key_i = 1;
#100
rst_n_i =1;
#20
key_i=0;
repeat(10)
begin
#100 key_i= ~key_i;
end
#5000
key_i=1;
repeat(10)
begin
#100 key_i= ~key_i;
end
end
always begin
#0.001 clk_i = ~ clk_i;
end
endmodule
- RTL仿真结果图:
key_inkey_state 为滤波后的信号。即按键按下时(低电平有效),经过20ms延时,如果仍然低电平则确实是被按下;抬起时,经过20ms的延时仍然高电平,则确实被抬起。整体效果测试结果如下:
仿真时钟周期采用2ps进行功能验证。
当按键按下瞬间:
抬起瞬间: