顶层文件
输入:Clk(时钟),Rst_n(复位信号),key_in0(按键输入),key_in1(按键输入)
输出:led(4个led灯)
module key_led_top(Clk,Rst_n,key_in0,key_in1,led);
input Clk;
input Rst_n;
input key_in0;
input key_in1;
output [3:0]led;
wire key_flag0,key_flag1;
wire key_state0,key_state1;
key_filter key_filter0( //调用按键消抖模块
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_in0),
.key_flag(key_flag0),
.key_state(key_state0)
);
key_filter key_filter1( //调用按键消抖模块
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(key_in1),
.key_flag(key_flag1),
.key_state(key_state1)
);
led_ctrl led_ctrl0( //调用控制led模块
.Clk(Clk),
.Rst_n(Rst_n),
.key_flag0(key_flag0),
.key_flag1(key_flag1),
.key_state0(key_state0),
.key_state1(key_state1),
.led(led)
);
endmodule
按键消抖模块:
输入:Clk(时钟信号),Rst_n(复位信号),key_in(按键输入)
输出:key_flag(检测到按键标志位,为1为有按键输入),key_state(按键稳定标志位,为0位有按键输入)
module key_filter(Clk,Rst_n,key_in,key_flag,key_state);
input Clk;
input Rst_n;
input key_in;
output reg key_flag;
output reg key_state;
localparam
IDEL = 4'b0001,
FILTER0 = 4'b0010,
DOWN = 4'b0100,
FILTER1 = 4'b1000;
reg [3:0]state;
reg [19:0]cnt;
reg en_cnt; //使能计数寄存器
//对外部输入的异步信号进行同步处理
reg key_in_sa,key_in_sb;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_sa <= 1'b0;
key_in_sb <= 1'b0;
end
else begin
key_in_sa <= key_in;
key_in_sb <= key_in_sa;
end
reg key_tmpa,key_tmpb;
wire pedge,nedge;
reg cnt_full;//计数满标志信号
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_tmpa <= 1'b0;
key_tmpb <= 1'b0;
end
else begin
key_tmpa <= key_in_sb;
key_tmpb <= key_tmpa;
end
//产生跳变沿信号
//key_tmpa,key_tmpb本身一直为0,key_in_sb为输入信号,
//经过赋值之后key_tmpa变成输入信号(也就是相对于过去为高电平),
//key_tmpb依旧为0,所以!key_tmpa & key_tmpb; key_tmpa取反为0和b相同则为下降沿
//在key_tmpa已经是当前高电平的情况下key_tmpa & (!key_tmpb);key_tmpb取反为0和b相同则为上升沿
assign nedge = !key_tmpa & key_tmpb;
assign pedge = key_tmpa & (!key_tmpb);
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
en_cnt <= 1'b0;
state <= IDEL;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDEL :
begin
key_flag <= 1'b0;
if(nedge)begin //检测到下降沿
state <= FILTER0;//切换状态
en_cnt <= 1'b1; //计数器使能,只有使能才能开始计数
end
else
state <= IDEL;//没有就保持
end
FILTER0:
if(cnt_full)begin //计数满的标志
key_flag <= 1'b1;
key_state <= 1'b0;
en_cnt <= 1'b0;
state <= DOWN;
end
else if(pedge)begin
state <= IDEL;
en_cnt <= 1'b0;
end
else
state <= FILTER0;
DOWN:
begin
key_flag <= 1'b0;
if(pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
state <= DOWN;
end
FILTER1:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b1;
en_cnt <= 1'b0;
state <= IDEL;
end
else if(nedge)begin
en_cnt <= 1'b0;
state <= DOWN;
end
else
state <= FILTER1;
default:
begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
//计数器
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 999_999) //时钟20ns一次,20毫秒就计数到1,000,000
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
endmodule
led控制
输入:Clk,Rst_n,key_flag0,key_flag1,key_state0,key_state1,
输出:led
module led_ctrl(Clk,Rst_n,key_flag0,key_flag1,key_state0,key_state1,led);
input Clk;
input Rst_n;
input key_flag0,key_flag1;
input key_state0,key_state1;
output [3:0]led;
reg [3:0]led_r;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
led_r <= 4'b0000;
else if(key_flag0 && !key_state0)
led_r <= led_r + 1'b1;
else if(key_flag1 && !key_state1)
led_r <= led_r - 1'b1;
else
led_r <= led_r;
assign led = ~led_r; //板子上led灯1灭,0亮
endmodule