Verilog实现4位按键消抖,分别控制一个LED

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值