前言
独立按键消抖(Debouncing)是指在电子设备中对按键操作信号进行处理,以消除由于机械开关接触不良导致的多次触发信号。机械按键在按下和释放时可能会产生短暂的接触不稳定,导致多个信号被误读。消抖技术可以确保每次按键操作只被识别为一次有效的输入,提高系统的稳定性和可靠性。消抖技术可以防止因为按键抖动产生的重复信号,避免了由于抖动引起的误操作。例如,一个按键被按下时,系统可能会错误地认为按键被按了多次,导致不必要的功能触发。通过消抖处理,按键的响应更加准确和一致,用户在操作设备时不会因为信号错误而感到困惑或不满意,从而提升整体用户体验。常见的消抖方法有硬件消抖(使用电容、RC电路等)和软件消抖(通过编程实现延迟判断、状态监测等)。选择适当的消抖方法取决于具体的应用需求和系统设计。
正文
一、独立按键消抖
1.项目需求
利用有限状态机理论,设计并验证对开发板上集成的独立按键进行消抖处理,使其产生一个标志信号flag表示对按键进行一次操作,同时也产生一个使能信号key_en表示按键稳定保持在低电平。
2技术介绍
将对按键进行一次操作划分为四个状态,分别为:空闲状态,下抖动状态,稳定状态,上抖动状态。
空闲到下抖动状态的条件为:检测到按键的电压值变化为0。下抖动状态到稳定状态的条件为:延时计数器cnt计数到最大值:假设我们对按键进行检测的时钟频率为1khz,那么最大值为:9;稳定状态到上抖动状态的条件为:检测到按键的电压值变化为1。上抖动状态到空闲状态的条件为:延时计数器cnt计数到最大值。
状态 | 执行动作 |
空闲 | 表示对按键没有操作 |
下抖动 | 表示刚开始对按键进行操作 |
稳定 | 标志信号flag只拉高一个驱动时钟周期 |
上抖动 | 表示对按键进行一次操作马上结束 |
状态转移图为:
3.顶层架构
cnt_freq模块为分频器,由于常用开发板时钟频率为50MHz,该时钟频率较快,为确保下抖动状态时不产生flag信号,需要一相对较慢的时钟信号,这里选用1kHz时钟信号。jitter_ctrl为独立按键消抖控制模块,利用三段式有限状态机完成消抖处理。
4.端口描述
clk | 开发板时钟 |
rst_n | 复位按键(低电平有效) |
key | 目标按键 |
flag | 按键有效信号 |
key_en | 输出使能(为1时输出有效) |
二、代码验证
cnt_freq模块
module cnt_freq(
input clk,
input rst_n,
output reg clk_1khz
);
//计数目标信号半个周期0.5ms
reg [14:0] cnt;
//开发板时钟50MHz,目标信号1KHz
//即50_000个时钟信号产生一个目标信号
//需要的目标信号也是作为下个模块时钟信号
//占空比50%,即记25_000个时钟信号使目标信号电平翻转一次
parameter MAX = 15'd25_000;
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
cnt <= 15'd0;
else
if(cnt < MAX - 1)//记25_000个时钟信号
cnt <= cnt + 15'd1;
else
cnt <= 15'd0;
end
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
clk_1khz <= 1'b0;
else
if(cnt == MAX - 1)
clk_1khz <= ~clk_1khz;//目标信号电平翻转
else
clk_1khz <= clk_1khz;
end
endmodule
jitter_ctrl模块
//三段式
module jitter_ctrl_v2(
input clk,
input rst_n,
input key,
output reg flag,
output reg key_en
);
reg [3:0] n_state;
reg [3:0] c_state;
parameter s0 = 4'b0001;//空闲状态
parameter s1 = 4'b0010;//下抖动状态
parameter s2 = 4'b0100;//稳定状态
parameter s3 = 4'b1000;//上抖动状态
reg [3:0] cnt;
//FSM1
always @(posedge clk,negedge rst_n)
begin
if(rst_n == 0)
c_state <= s0;
else
c_state <= n_state;
end
//FSM2
always @(*)//描述状态转移
begin
if(rst_n == 0)
n_state <= s0;
else
case(c_state)
s0 : begin
if(key == 0)//默认按键低电平有效,即=0时按键按下
n_state <= s1;
else
n_state <= s0;
end
s1 : begin
if(cnt == 9)//延时计数器判断
n_state <= s2;
else
n_state <= s1;
end
s2 : begin
if(key == 1)//按键弹起,状态改变
n_state <= s3;
else
n_state <= s2;
end
s3 : begin
if(cnt == 9)//延时计数器判断
n_state <= s0;
else
n_state <= s3;
end
default : n_state <= s0;
endcase
end
//FSM3
always @(posedge clk,negedge rst_n)//状态输出产生flag信号
begin
if(rst_n == 0)
begin
cnt <= 4'd0;
flag <= 1'b0;
key_en <= 1'b0;
end
else
case(c_state)
s0 : begin
cnt <= 4'd0;
flag <= 1'b0;
key_en <= 1'b0;
end
s1 : begin
if(cnt < 9)//驱动延时计数器
cnt <= cnt + 4'd1;
else
cnt <= 4'd0;
if(cnt == 9)//延时计数器记满,产生flag
begin
flag <= 1'b1;//flag只产生一个时钟周期
key_en <= 1'b1; //输出使能
end
else
begin
key_en <= 1'b0;
flag <= 1'b0;
end
end
s2 : begin
flag <= 1'b0;
key_en <= 1'b1;
cnt <= 4'd0;
end
s3 : begin
flag <= 1'b0;//状态初始化,准备判断下次按键按下
key_en <= 1'b0;
if(cnt < 9)
cnt <= cnt + 4'd1;
else
cnt <= 4'd0;
end
default : begin
cnt <= 4'd0;
flag <= 1'b0;
key_en <= 1'b0;
end
endcase
end
endmodule
顶层代码
module key_jitter(
input clk,
input rst_n,
input key,
output flag,
output key_en
);
wire clk_1khz;//定义连线
cnt_freq #(.MAX(5)) cnt_freq_inst(//给分频计数器最大值改5,方便仿真
.clk(clk),
.rst_n(rst_n),
.clk_1khz(clk_1khz)
);
jitter_ctrl_v2 jitter_ctrl_inst(
.clk(clk_1khz),
.rst_n(rst_n),
.key(key),
.flag(flag),
.key_en(key_en)
);
endmodule
仿真代码
`timescale 1ns/1ps
module key_jitter_tb;
reg clk;
reg rst_n;
reg key;
wire flag;
wire key_en;
key_jitter key_jitter_inst(
.clk(clk),
.rst_n(rst_n),
.key(key),
.flag(flag),
.key_en(key_en)
);
initial clk = 1;
always #10 clk = ~clk;
//模拟时钟周期为20ns,目标时钟由于分频计数器最值改5后,目标时钟变为200ns
initial begin
rst_n = 0;
key = 1;
#200
rst_n = 1;
#200
key = 0;//模拟按键按下
#240//
key = 1;//模拟下抖动状态
#230//延时计数器启动
key = 0;
#213
key = 1;
#123
key = 0;//模拟稳定状态
#3000//230+213+123+1234 = 1800 = 200 * 9
//即在3000的第1234ns左右时,产生一个flag
key = 1;//模拟上抖动状态
#213
key = 0;
#123
key = 1;
#234
key = 0;
#231
key = 1;
#3000
$stop;
end
endmodule
三、仿真验证
运行仿真
可以看到,有一个flag信号产生,调出中间信号分析:
flag产生一个时钟周期,满足设计要求
延时计数器记满9后,进入按键按下稳定状态,并且输出flag。
即在3000ns的第1234ns左右时,输出一个flag,满足仿真预设。