数电实验 按键防抖设计(状态机)
主程序:
module yyc2018113559_6_1(
input clk,rst,key_in,
output reg key_state, //按键状态,低电平为未按下,高电平为按下状态
output reg [3:0] key_count, //用于数码管输出的数字
output reg [6:0] codeout
);
parameter IDLE=2'b00; //定义4种状态
parameter FILTER0=2'b01;
parameter DOWN=2'b10;
parameter FILTER1=2'b11;
//内部信号声明
reg key_tmp0;
reg key_tmp1;
wire pose_edge;
wire nege_edge;
//边沿检测模块
always@(posedge clk or negedge rst)
begin
if(~rst)
begin //复位
key_tmp0<=0;
key_tmp1<=0;
end
else
begin
key_tmp0<=key_in; //tmp0等于输入,tmp0表示当前状态
key_tmp1<=key_tmp0; //tmp1表示前一个状态
end
end
assign pose_edge=(~key_tmp1)&(key_tmp0); //上升沿,当前与前一个状态比,由低到高
assign nege_edge=(key_tmp1)&(~key_tmp0); //下降沿,当前与前一个状态比,由高到低
reg [9:0] cnt;
reg en; //使能信号
reg cnt_full;
//计数器,实际用于延时,计数器记满
//即是在规定时间内抖动,即认为是一次按下按键,记录。若没有记满,则不认为是一次按下,不记录
always@(posedge clk or negedge rst)
begin
if(~rst) cnt<=10'd0;
else if(en) cnt<=cnt+1;
else cnt<=10'd0;
end
//计数满信号
always@(posedge clk or negedge rst)
begin
if(~rst)
cnt_full<=1'b0;
else if(cnt==10'd999) //cnt记满999,cnt_full标志置1
cnt_full<=1'b1;
else cnt_full<=1'b0;
end
//状态机
reg [1:0] current_state;
always@(posedge clk or negedge rst) //敏感触发为clk上升沿,复位信号rst下降沿
begin
if(~rst)
begin //复位
en<=1'b0;current_state<=IDLE;
key_state<=1'b0;key_count<=4'b0000;
end
else
begin
case (current_state) //4中状态
IDLE: //未按下空闲状态
begin
key_state<=1'b0;
if(pose_edge) //检测到上升沿,进入下一个状态,计数器开始计数
begin current_state<=FILTER0; en<=1'b1;end
else //否则仍处于未按下空闲状态
current_state<=IDLE;
end
FILTER0: //按下抖动滤除状态
begin
if(cnt_full)//计数器记满,达到稳定状态,计数停止
begin
key_state<=1'b1; //key_state为1表示按键按下
key_count<=key_count+1'b1; //按下输出就得加一
if(key_count==4'b1001) //超过9归0
key_count<=4'b0000;
en<=1'b0;
current_state=DOWN; //下一状态
end
else if(nege_edge) //若计数没有记满,即在延时前结束了抖动,那么认为不是按下操作,回到前一状态
begin
current_state<=IDLE;
en<=1'b0;
end
else //维持当前状态
current_state<=FILTER0;
end
DOWN: //按下稳定状态
begin
key_state<=1'b1;
if(nege_edge)
begin
current_state<=FILTER1; //下一状态
en<=1'b1;
end
else //保持当前状态
current_state<=DOWN;
end
FILTER1: //释放抖动滤除状态
begin
if(cnt_full) //计数记满了,即超过规定延时,认为为一次按下操作
begin
key_state<=1'b0;
en<=1'b0;
current_state<=IDLE;
end
else if(pose_edge) //若计数没有满,不认为有按下操作,保持当前状态
begin
current_state<=DOWN;
en<=1'b0;
end
end
default:
begin
en<=1'b0;
current_state<=IDLE;
key_state<=1'b0;
end
endcase
end
end
always@(key_count) //数码管输出
begin
case(key_count)
4'd0:codeout=7'b0111111;
4'd1:codeout=7'b0000110;
4'd2:codeout=7'b1011011;
4'd3:codeout=7'b1001111;
4'd4:codeout=7'b1100110;
4'd5:codeout=7'b1101101;
4'd6:codeout=7'b1111101;
4'd7:codeout=7'b0000111;
4'd8:codeout=7'b1111111;
4'd9:codeout=7'b1101111;
default:codeout=7'b0000000; //说明其他输入情况的输出取值
endcase
end
endmodule
modelsim仿真
`timescale 10 ns/1 ns
module yyc2018113559_6_1_test;
reg clk;
reg rst;
reg key_in;
wire key_state;
wire [3:0] key_count;
wire [6:0] codeout;
yyc2018113559_6_1 test(
.clk(clk),
.rst(rst),
.key_in(key_in),
.key_state(key_state),
.key_count(key_count),
.codeout(codeout)
);
initial begin
clk=1'b0;
rst=1'b1;
key_in=1'b0;
#100000
rst=1'b0;
#100000
rst=1'b1;
end
always
begin
key_in=1'b0;
repeat(20)
begin
#50000 key_in=~key_in;//抖动时间20*10*50000ns=10ms
end
#3000000//按下时长30ms
key_in=1'b1;
repeat(20)//抖动时间10ms
begin
#50000 key_in=~key_in;
end
#3000000;
end
always #500 clk=~clk; //时钟周期为10us
endmodule