Verilog实现4位按键消抖,分别控制一个LED
参考:(主要是第一篇,第二篇不严谨)
《按键消抖与LED控制》实验的个人思考与总结
Verilog实现的多个按键消抖(具体模块可用)
代码思路(完整代码在后)
之前的一篇博文中,
FPGA触摸按键控制LED——拓展:按下之后LED闪烁,再次按下停止闪烁
介绍了使用下降沿来判断按键按下并做出反应的方式,那么这次有4个按键,其实同样可以使用这种方法进行采样。但是,这种方法有一个缺点,如果采样的时候有抖动,就会发生误判,只能寄希望于,这个抖动确实很短,人眼观察不到这个细微抖动带来的led的状态的变化。
但是这里我们进行改进。我们之前学过,按键消抖,需要20ms的延时判断,之前是我们计数器只有按键按下,计数器才开始计数,计数满-1就置位flag,说明被按下。这里我们改变思路,每20ms进行一次按键值采样,将两次采样值进行取下降沿操作,得到的值为1的那位就是被按下的按键。
具体思路如下:
我们每20ms进行一次按键值第一次将4位按键信号相与,检测信号的下降沿,只要信号发生下降沿就说明此时按键有抖动,20ms的计数器清零,之前我们的计数器是用来计数按键按下到达20ms的,只要按键为电平就清零,但这里的方式稍微不同,我们采取每20ms保存一次键值,如果抖动,就将计数器清零,直到计数器满才取一次键值,我们取到的就是按键稳定时候的键值,然后将两次的键值进行取下降沿操作,此时取到的下降沿(也就是变量中为一的那一位)就代表了按键的对应位确实被按下了。
首先定义
module key_led
#(
parameter KEY_20MS = 999_999
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] key_in ,
output wire [3:0] led_out
);
将4位按键相与
reg key_rst;//检测是否有按键按下,并且同步到时钟下
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst <= 1'b1;
else
key_rst <= key_in[3] & key_in[2] & key_in[1] & key_in[0];//将四个按键的值存进,只要有一位被按下就会发生跳变
end
然后对此信号打一拍,之后取下降沿
reg key_rst_reg;//对key_rst进行延一拍
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst_reg <= 1'b1;
else
key_rst_reg <= key_rst;
end
//检测下降沿操作
wire key_en = (~key_rst) & key_rst_reg;
只要下降沿信号置位,说明此时还在按键不稳定的状态,计数器清零。因为计数器清零就代表不对按键值采样,所以这是符合逻辑的。
reg [19:0] cnt_20ms;//20ms计数器
//cnt_20ms 计数器
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_20ms <= 20'd0;
else if(key_en) //检测到抖动
cnt_20ms <= 20'd0;
else
cnt_20ms <= cnt_20ms + 20'd1;
end
然后就是对按键值进行采样,以及进行延一拍的操作
reg [3:0] key_flag;//作为按键检测标志位
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag <= 4'b1111;
else if(cnt_20ms == KEY_20MS)//每20ms保存一次键值
key_flag <= key_in;
end
reg [3:0] key_flag_reg;//延一拍
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag_reg <= 4'b1111;
else
key_flag_reg <= key_flag;
end
最后对按键值进行取下降沿,然后选择LED
//保存按键值
wire [3:0] key_out = (~key_flag) & key_flag_reg;
reg [3:0] led_reg;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led_reg <= 4'b0000;
else
begin
if(key_out == 4'b0001) led_reg[0] <= ~led_out[0];
if(key_out == 4'b0010) led_reg[1] <= ~led_out[1];
if(key_out == 4'b0100) led_reg[2] <= ~led_out[2];
if(key_out == 4'b1000) led_reg[3] <= ~led_out[3];
end
end
assign led_out[0] = led_reg[0];
assign led_out[1] = led_reg[1];
assign led_out[2] = led_reg[2];
assign led_out[3] = led_reg[3];
完整代码
module key_led
#(
parameter KEY_20MS = 999_999
)
(
input wire sys_clk ,
input wire sys_rst_n ,
input wire [3:0] key_in ,
output wire [3:0] led_out
);
/*
还是有两个part,一个part用来检测按键是否按下,输入key_in,输出key_out,为第几位
第二个part用来运行蜂鸣器,根据按键值case,未按下的,不响,按下就DO RE MI FA四个输出
对于一位的按键消抖,我们在前面的教程中学过两节按键检测,一节是按键消抖的课
一节是触摸按键的检测,是通过取下降沿的方式检测按键按下的信号,
对于此处检测4个按键,我们将上述两种方法合二为一,第一次将4位按键信号相与,
检测信号的下降沿,只要信号发生下降沿就说明此时按键有抖动,20ms的计数器清零,
之前我们的计数器是用来计数按键按下到达20ms的,只要按键为电平就清零,
但这里的方式稍微不同,我们采取每20ms保存一次键值,如果抖动,就将计数器清零,
直到计数器满才取一次键值,我们取到的就是按键稳定时候的键值,
然后将两次的键值进行取下降沿操作,此时取到的下降沿(也就是变量中为一的那一位)
就代表了按键的对应位确实被按下了。
*/
reg key_rst;//检测是否有按键按下,并且同步到时钟下
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst <= 1'b1;
else
key_rst <= key_in[3] & key_in[2] & key_in[1] & key_in[0];//将四个按键的值存进,只要有一位被按下就会发生跳变
end
reg key_rst_reg;//对key_rst进行延一拍
always @(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_rst_reg <= 1'b1;
else
key_rst_reg <= key_rst;
end
//检测下降沿操作
wire key_en = (~key_rst) & key_rst_reg;
reg [19:0] cnt_20ms;//20ms计数器
//cnt_20ms 计数器
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
cnt_20ms <= 20'd0;
else if(key_en) //检测到抖动
cnt_20ms <= 20'd0;
else
cnt_20ms <= cnt_20ms + 20'd1;
end
reg [3:0] key_flag;//作为按键检测标志位
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag <= 4'b1111;
else if(cnt_20ms == KEY_20MS)//每20ms保存一次键值
key_flag <= key_in;
end
reg [3:0] key_flag_reg;//延一拍
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
key_flag_reg <= 4'b1111;
else
key_flag_reg <= key_flag;
end
//保存按键值
wire [3:0] key_out = (~key_flag) & key_flag_reg;
reg [3:0] led_reg;
always@(posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
led_reg <= 4'b0000;
else
begin
if(key_out == 4'b0001) led_reg[0] <= ~led_out[0];
if(key_out == 4'b0010) led_reg[1] <= ~led_out[1];
if(key_out == 4'b0100) led_reg[2] <= ~led_out[2];
if(key_out == 4'b1000) led_reg[3] <= ~led_out[3];
end
end
assign led_out[0] = led_reg[0];
assign led_out[1] = led_reg[1];
assign led_out[2] = led_reg[2];
assign led_out[3] = led_reg[3];
endmodule