目录
说明
1、功能分析
2、模块代码
3、仿真分析
说明
按键消抖在机械按键中很有必要,保护电路的安全性。一般来说,机械按键抖动持续时间约为20ms,本文从状态机的角度进行按键消抖以及按键状态判别的实现。
场景说明:
按键默认上拉到高电平,按下时为低电平。
代码仅供设计参考,编程方式不唯一。主要是设计思路和功能需求到状态转移图的映射。
不足之处,评论区留言~
1、功能分析
首先状态划分:
1、空闲状态:此时按键没有按下,处于等待按键按下的状态;
2、按下消抖:此时按键按下,进行消抖操作;
3、等待释放:按下消抖已经完成,确认按键已经稳定按下,等待释放;
4、释放消抖:此时按键释放,进行消抖操作;
状态转移条件分析:
当前状态为状态 1 :
- 若检测到有下降沿到来,进入状态 2并开始计时 ;
- 否则一直处于状态 1 等待。
当前状态为状态 2 :
- 若检测到有上升沿到来,且计时时间小于20ms,返回状态 1;
- 若计时时间大于等于20ms,进入状态 3,认为此时按键已经稳定按下;
- 若以上均不满足,保持在本状态,持续计时。
当前状态为状态 3 :
- 若检测到有上升沿到来,进入状态4并开始计时;
- 否则一直处于本状态,等待释放;
当前状态为状态 4 :
- 若检测到有下降沿到来且计时时间小于20ms,返回状态 3;
- 若计时时间大于等于20ms,进入状态 1,认为此时按键已经稳定释放;
- 若以上均不满足,保持在本状态,持续计时。
计数器行为分析:
状态1:清零
状态2:持续计时
状态3:清零
状态4:持续计时
状态转换图:
?
?
2、模块代码
VERILOG 编写模块:
// BY :在路上,正出发
// 说明:实现按键检测的状态机控制(三段式)
`timescale 1ns/1ps
module KEY_FILTER_FSM(
// -----------------------------输入端口声明
input I_CLK, //外部激励时钟 50MHz
input I_RSTN,//外部复位信号 开关控制
input I_KEY, //外部按键
// -----------------------------输出端口声明
output O_KEY1 //脉冲信号 拉高表示检测到按键稳定按下一次
);
// -----------------------------内部参数声明
// 状态机参数
localparam P_STATE_IDLE = 4'b0001;//空闲
localparam P_STATE_DOWN = 4'b0010;//下降(按下)
localparam P_STATE_WAIT = 4'b0100;//等待释放
localparam P_STATE_UP = 4'b1000;//上升(释放)
// 计数参数
localparam P_COUNT_MAX = 20'hF_4240;//20ms 计数峰值
// -----------------------------内部信号声明
// 状态机相关
reg [3:0] R_CS;//当前状态
reg [3:0] R_NS;//下一状态
// 计数相关
reg [19:0] R_COUNT_20MS;// 20ms计数器
// 输入打拍
reg R_KEY; //输入 I_KEY 打1拍
reg RR_KEY;//输入 I_KEY 打2拍
// 边沿
wire W_KEY_POS;//上升边沿
wire W_KEY_NEG;//下降边沿
// 按键状态
reg R_KEY_DOWN;//单脉冲信号 拉高表示按键稳定按下
reg R_KEY_UP; //单脉冲信号 拉高表示按键稳定释放
// -----------------------------功能实现
// 输入打拍
always @ (posedge I_CLK)
begin
if(I_RSTN == 1'b0)
begin
R_KEY <= 1'b0;
RR_KEY <= 1'b0;
end
else
begin
R_KEY <= I_KEY;
RR_KEY <= R_KEY;
end
end
// 边沿检测
assign W_KEY_POS = R_KEY & (~RR_KEY);//上升沿
assign W_KEY_NEG = (~R_KEY) & RR_KEY;//下降沿
// 三段式状态机
// 第一段 时序逻辑:状态转移
always @ (posedge I_CLK )
begin
if(I_RSTN == 1'b0)
begin
R_CS <= P_STATE_IDLE;
end
else
begin
R_CS <= R_NS;
end
end
// 第二段 组合逻辑:状态迁移条件
always @ (*)
begin
case(R_CS)
P_STATE_IDLE:
begin
if(W_KEY_NEG == 1)
begin
R_NS = P_STATE_DOWN;
end
else
begin
R_NS = P_STATE_IDLE;
end
end
P_STATE_DOWN:
begin
if(W_KEY_POS == 1 && R_COUNT_20MS < P_COUNT_MAX)
begin
R_NS = P_STATE_IDLE;
end
else if(R_COUNT_20MS >= P_COUNT_MAX)
begin
R_NS = P_STATE_WAIT;
end
else
begin
R_NS = P_STATE_DOWN;
end
end
P_STATE_WAIT:
begin
if(W_KEY_POS == 1)
begin
R_NS = P_STATE_UP;
end
else
begin
R_NS = P_STATE_WAIT;
end
end
P_STATE_UP:
begin
if(W_KEY_NEG == 1 && R_COUNT_20MS < P_COUNT_MAX)
begin
R_NS = P_STATE_WAIT;
end
else if(R_COUNT_20MS >= P_COUNT_MAX)
begin
R_NS = P_STATE_IDLE;
end
else
begin
R_NS = P_STATE_UP;
end
end
default:
begin
R_NS = P_STATE_IDLE;
end
endcase
end
// 第三段 时序逻辑:状态事件
always @ (posedge I_CLK )
begin
case(R_CS)
P_STATE_IDLE:
begin
R_COUNT_20MS <= 0;
R_KEY_UP <= 0;
R_KEY_DOWN <= 0;
end
P_STATE_DOWN:
begin
R_COUNT_20MS <= R_COUNT_20MS+1;
if(R_COUNT_20MS >= P_COUNT_MAX)
begin
R_KEY_DOWN <= 1;
end
end
P_STATE_WAIT:
begin
R_COUNT_20MS <= 0;
R_KEY_DOWN <= 0;
end
P_STATE_UP:
begin
R_COUNT_20MS <= R_COUNT_20MS+1;
if(R_COUNT_20MS >= P_COUNT_MAX)
begin
R_KEY_UP <= 1;
end
end
default:
begin
R_COUNT_20MS <= 0;
R_KEY_UP <= 0;
R_KEY_DOWN <= 0;
end
endcase
end
assign O_KEY1 = R_KEY_UP;
endmodule
3、仿真分析
建立激励文件,模拟按键抖动过程:
// 测试按键消抖以及按键稳定按下模块的功能
// BY:在路上,正出发
`timescale 1ns/1ps
module SIM_KEY_DETECT();
reg I_CLK;
reg I_RSTN;
reg I_KEY;
wire O_KEY1;
// 时钟
`define clock_period 20
initial I_CLK = 0;
always #(`clock_period/2) I_CLK = ~I_CLK;
initial
begin
I_RSTN = 0;
I_KEY = 1;
# 40
I_RSTN = 1;
# 40
I_KEY = 0;
# 1000000
I_KEY = 1;
# 3000000
I_KEY = 0;
# 5000000
I_KEY = 1;
# 8000000
I_KEY = 0;
# 5000000
I_KEY = 1;
# 100
I_KEY = 0;
# 30000000
I_KEY = 1;
# 1000000
I_KEY = 0;
# 3000000
I_KEY = 1;
# 5000000
I_KEY = 0;
# 8000000
I_KEY = 1;
# 5000000
I_KEY = 0;
# 100
I_KEY = 1;
# 30000000
I_KEY = 0;
$stop;
end
// 例化模块
KEY_FILTER_FSM inst_KEY_FILTER_FSM (
.I_CLK(I_CLK),
.I_RSTN(I_RSTN),
.I_KEY(I_KEY),
.O_KEY1(O_KEY1));
endmodule
时序结果图:
?
|