一、项目分析
1、实现原理
将按键按下到释放的整个过程分为4个过程: 按键按下抖动状态 按键按下保持低电平状态 按键释放状态 按键释放保持高电平状态 由此,可以把按键被按下的过程划分为4个状态, 即空闲状态、按下状态、保持状态、释放状态
2、状态转移图
IDLE:空闲状态,按键释放/未按下时保持高电平的状态; FILETER_DOWN:按键按下抖动状态; HOLD_DOWN:按键按下保持低电平状态; FILETER_UP:按键释放抖动状态。
3、时序图
4、模块框图
按键消抖模块:检测两个按键的状态,输出一个单周期的脉冲信号,标志按键被按下。 LED控制模块:当接收到按键消抖模块发出的脉冲信号时,改变4个LED的流水状态。 按键和LED是不同的外设,这里划分模块时,可以把按键消抖设计为一个模块,LED控制设计为一个模块;当然,工程比较简单,也可以直接把按键消抖和LED控制放在一个模块设计。
二、项目源码
1、顶层模块
module key_top (
input clk ,
input rst_n ,
input [1:0] key_in,
output [3:0] led
);
wire [1:0] key_done ;
key u_key(
.clk (clk) ,
.rst_n (rst_n) ,
.key_in (key_in) ,
.key_done (key_done)
);
led u_led(
.clk (clk) ,
.rst_n (rst_n) ,
.key_done (key_done) ,
.led (led)
);
endmodule
2、消抖模块
法一:检测到下降沿就开始计数
module key (
input clk ,
input rst_n ,
input [1:0] key_in ,
output reg [1:0] key_done
);
parameter MAX_20MS = 20'd999_999 ;
parameter IDLE = 4'b0001 ,
FILTER_DOWN = 4'b0010 ,
HOLD = 4'b0100 ,
FILTER_UP = 4'b1000 ;
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [1:0] key_r0 ;
reg [1:0] key_r1 ;
wire [1:0] nedge ;
wire [1:0] pedge ;
reg [19:0] cnt_20ms ;
wire add_cnt_20ms ;
wire end_cnt_20ms ;
wire idle2filter_down ;
wire filter_down2idle ;
wire filter_down2hold ;
wire hold2filter_up ;
wire filter_up2idle ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state_c <= IDLE ;
end
else begin
state_c <= state_n ;
end
end
always @(*) begin
case (state_c)
IDLE :begin
if(idle2filter_down)
state_n = FILTER_DOWN ;
else
state_n = IDLE ;
end
FILTER_DOWN :begin
if(filter_down2idle)
state_n = IDLE ;
else if(filter_down2hold)
state_n = HOLD ;
else
state_n = FILTER_DOWN ;
end
HOLD :begin
if(hold2filter_up)
state_n = FILTER_UP ;
else
state_n = HOLD ;
end
FILTER_UP :begin
if(filter_up2idle)
state_n = IDLE ;
else
state_n = FILTER_UP ;
end
default:state_n = IDLE ;
endcase
end
assign idle2filter_down = state_c == IDLE && (nedge != 0) ;
assign filter_down2idle = state_c == FILTER_DOWN && (end_cnt_20ms && (key_r0 == 2'b11)) ;
assign filter_down2hold = state_c == FILTER_DOWN && (end_cnt_20ms && (key_r0 != 2'b11)) ;
assign hold2filter_up = state_c == HOLD && (pedge != 0) ;
assign filter_up2idle = state_c == FILTER_UP && (end_cnt_20ms) ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_r0 <= 2'b11;
key_r1 <= 2'b11;
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
end
end
assign nedge = (~key_r0) & (key_r1);
assign pedge = (key_r0) & (~key_r1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_20ms <= 1'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 1'd0;
end
else begin
cnt_20ms <= cnt_20ms + 1'd1;
end
end
end
assign add_cnt_20ms = (state_c == FILTER_DOWN || state_c == FILTER_UP);
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == MAX_20MS ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_done <= 2'b00;
end
else if(filter_down2hold)begin
key_done <= ~key_r0;
end
else begin
key_done <= 2'b00;
end
end
endmodule
法二:抖动结束后计数20ms
module key (
input clk ,
input rst_n ,
input [1:0] key_in ,
output reg [1:0] key_done
);
parameter MAX_20MS = 20'd999_999 ;
parameter IDLE = 4'b0001 ,
FILTER_DOWN = 4'b0010 ,
HOLD = 4'b0100 ,
FILTER_UP = 4'b1000 ;
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [1:0] key_r0 ;
reg [1:0] key_r1 ;
wire [1:0] nedge ;
wire [1:0] pedge ;
reg [19:0] cnt_20ms ;
wire add_cnt_20ms ;
wire end_cnt_20ms ;
wire idle2filter_down ;
wire filter_down2idle ;
wire filter_down2hold ;
wire hold2filter_up ;
wire filter_up2idle ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
state_c <= IDLE ;
end
else begin
state_c <= state_n ;
end
end
always @(*) begin
case (state_c)
IDLE :begin
if(idle2filter_down)
state_n = FILTER_DOWN ;
else
state_n = IDLE ;
end
FILTER_DOWN :begin
if(filter_down2idle)
state_n = IDLE ;
else if(filter_down2hold)
state_n = HOLD ;
else
state_n = FILTER_DOWN ;
end
HOLD :begin
if(hold2filter_up)
state_n = FILTER_UP ;
else
state_n = HOLD ;
end
FILTER_UP :begin
if(filter_up2idle)
state_n = IDLE ;
else
state_n = FILTER_UP ;
end
default:state_n = IDLE ;
endcase
end
assign idle2filter_down = state_c == IDLE && nedge != 0 ;
assign filter_down2idle = state_c == FILTER_DOWN && pedge != 0 ;
assign filter_down2hold = state_c == FILTER_DOWN && end_cnt_20ms ;
assign hold2filter_up = state_c == HOLD && pedge != 0 ;
assign filter_up2idle = state_c == FILTER_UP && end_cnt_20ms ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_r0 <= 2'b11;
key_r1 <= 2'b11;
end
else begin
key_r0 <= key_in;
key_r1 <= key_r0;
end
end
assign nedge = (~key_r0) & (key_r1);
assign pedge = (key_r0) & (~key_r1);
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
cnt_20ms <= 1'd0;
end
else if(add_cnt_20ms)begin
if(end_cnt_20ms)begin
cnt_20ms <= 1'd0;
end
else begin
cnt_20ms <= cnt_20ms + 1'd1;
end
end
end
assign add_cnt_20ms = (state_c == FILTER_DOWN || state_c == FILTER_UP);
assign end_cnt_20ms = add_cnt_20ms && (cnt_20ms == MAX_20MS || filter_down2idle) ;
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
key_done <= 2'b00;
end
else if(filter_down2hold)begin
key_done <= ~key_r0;
end
else begin
key_done <= 2'b00;
end
end
endmodule
3、LED模块
module led (
input clk ,
input rst_n ,
input [1:0] key_done ,
output reg [3:0] led
);
always @(posedge clk or negedge rst_n) begin
if(!rst_n)begin
led <= 4'b1111;
end
else if(key_done[0])begin
led <= ~led;
end
else if(key_done[1])begin
led <= 4'b1100;
end
end
endmodule
三、仿真测试
1、仿真代码
`timescale 1ns/1ps
module key_tb ();
reg clk ;
reg rst_n ;
reg [1:0] key_in ;
wire [3:0] led ;
key_top u_key_top(
.clk (clk) ,
.rst_n (rst_n) ,
.key_in (key_in) ,
.led (led)
);
parameter CYCLE = 20;
defparam u_key_top.u_key.MAX_20MS = 20;
always #(CYCLE/2) clk = ~clk;
integer i,j;
initial begin
clk = 1'b1;
rst_n = 1'b1;
key_in = 2'b11;
#(CYCLE*10);
rst_n = 1'b0;
#(CYCLE*10);
rst_n = 1'b1;
for(i = 0;i < 10;i=i+1)begin
key_in[0] = {$random};
j = {$random}%30;
#(CYCLE*j);
end
key_in = 2'b11;
#(CYCLE*10);
for(i = 0;i < 10;i=i+1)begin
key_in[1] = {$random}%2;
j = {$random}%25;
#(CYCLE*j);
end
key_in = 2'b11;
#(CYCLE*10);
$stop;
end
endmodule
2、仿真波形
|