前言
本课程是按键消抖的一个扩展内容,主要是通过实验观察按键消抖和不消抖的一个区别。
一、按键消抖
按键抖动:按键抖动通常的按键所用开关为机械弹性开关,当机械触点断开、闭合时,由于机械触点的弹性作用,一个按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键,可能在A点检测到一次低电平,在B点检测到一次高电平,在C点又检测到一次低电平。同时抖动是随机,不可测的。那么按下一次按键,抖动可能会误以为按下多次按键。
1.按键消抖目的:消除按键抖动对我们程序的影响。
3. 按键消抖解决方案2:信号变化频率平稳后并且持续20ms则采样。
二、任务描述
使用按键控制开发板上一个led灯的亮灭,当按键按下的时候led灯就亮,当再一次按下按键的时候led就不亮了。由于按键存在抖动,按键松开的时候led灯就不亮,所以需要一个消抖模块对按键消抖。通过实验来观察消抖和不消抖的一个区别。
三、系统框图
四、模块描述
五、模块代码
- 创建single_led文件,编写single_led模块代码
module single_led(
input clk ,//开发板晶振频率50MHz
input rst_n ,//复位下降沿有效
input flag ,//消抖完成标志
input key_value,//稳定按键信号
output reg led //led灯
);
//计时器模块
always@(negedge rst_n or posedge clk)begin
if(!rst_n)begin
led <= 1'b0;//初始化led灯,高电平有效
end
else if(flag && ~key_value)begin
led <= ~led;//稳定电平对led取反
end
else begin
led <= led;//其他时刻,led等于其自身
end
end
endmodule
- 创建key_debounce文件,编写key_debounce模块代码
module key_debounce(
input wire clk ,//时钟50MHz
input wire rst_n,//复位信号,下降沿有效
input wire key ,//按键
output reg flag ,//消抖完成标志,0表示还在抖动,1表示抖动结束
output reg key_value//消抖完的有效电平
);
parameter DELAY = 20'd1000_000;//20ms
reg [19:0] delay_cnt ;//延迟计数器
reg key_reg ;//中间按键寄存器,存放当前按键信号,用于和下次按键信号做比较
//延迟计数器模块
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin//按键复位
delay_cnt <= 20'd0;//初始延迟计数器默认为0
key_reg <= 1'b1;//初始中间按键寄存器为高电平,按键高电平无效
end
else begin
key_reg <= key;
if(key_reg ==1 && key == 0)begin//如果抖动
delay_cnt <= DELAY;//开始设置延迟计数器初值
end
else begin//没有抖动
if(delay_cnt > 20'd0)begin//如果延迟计数器大于0
delay_cnt <= delay_cnt - 20'd1;//开始倒数计时
end
else begin
delay_cnt <= 20'd0;//如果延迟计数器小于等于0,延迟计数器清0
end
end
end
end
//根据延迟计数器获取状态以及稳定的按键信号
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin//复位信号
flag <= 1'b0;//抖动标志,初始为抖动
key_value <= 1'b1;//记录按键稳定信号
end
else if(delay_cnt == 1'd1)begin//计数从0包括999_999一共1_000_000个数
flag <= 1'b1;//条件满足,改变抖动标志
key_value <= key;//key_value将稳定信号输出
end
else begin
flag <= 1'b0;//计数器没有记满1_000_000个数,始终为抖动
key_value <= key_value;//key_value保持不变
end
end
endmodule
- 新建top_key_led文件,编写top_key_led模块代码
module top_key_led(
input wire clk ,//时钟50MHz
input wire rst_n,//复位信号,下降沿有效
input wire key ,//按键信号
output wire led//led灯信号
);
parameter DELAY = 20'd1_000_000;
wire flag ;
wire key_value;
key_debounce#(.DELAY (DELAY)) u_key_debounce(//实例化按键消抖模块
.clk (clk) ,//时钟信号
.rst_n (rst_n),//复位信号
.key (key) ,//按键信号
.flag (flag) ,//消抖信号
.key_value (key_value)//按键稳定信号
);
single_led u_single_led(//实例化led控制模块
.clk (clk ) ,//时钟信号
.rst_n (rst_n) ,//复位信号
.flag (flag) ,//消抖信号
.key_value (key_value),//按键稳定信号
.led (led)//led灯信号
);
endmodule
六、项目仿真
- 编写top_key_led_tb.v文件,进行仿真
`timescale 1ns/1ns//单位/精度
module top_key_led_tb();
parameter CYCLE = 20;
parameter DELAY = 4'd10;//倒计时10x20=200ns
reg clk ;//时钟
reg rst_n;//复位信号
reg key ;//按键信号
wire led;//led信号
always#(CYCLE/2) clk = ~clk;//模拟时钟信号,周期20ns
initial begin
clk = 1'b0 ;//时钟低电平
rst_n = 1'b0 ;//复位信号低电平
#(CYCLE) ;//延迟20ns
rst_n = 1'b1 ;//复位信号拉高
key = 1'b1 ;//按键高电平,未按下
#(CYCLE) ;//延迟20ns
key = 1'b0 ;//按键按下
#(CYCLE) ;//延迟20ns
key = 1'b1 ;//按键高电平,未按下
#(CYCLE) ;//延迟20ns
key = 1'b0 ;//按键按下
#(CYCLE) ;//延迟20ns
key = 1'b1 ;//按键高电平,未按下
#(CYCLE) ;//延迟20ns
key = 1'b0 ;//按键按下
#(CYCLE*DELAY*4);//延迟超过倒计时时间,观察led信号
$stop ;//停止
end
//实例化顶层模块
top_key_led#(.DELAY (DELAY)) u_top_key_led(
.clk (clk ),
.rst_n (rst_n) ,
.key (key ),
.led (led )
);
endmodule
- 仿真结果
七、管脚信息
元件 | 管脚 |
---|---|
LED0 | G15 |
LED1 | F16 |
LED2 | F15 |
LED3 | D16 |
KEY0 | E15 |
KEY1 | E16 |
KEY2 | M16 |
KEY3 | M15 |
COLOCK(时钟) | E1 |
八、运行效果

按键控制led+按键消抖
总结
消抖前的按键控制led和消抖后的按键控制led,效果是不一样的。按键的物理特性导致按键存在抖动,我们不能改变按键的物理特性,但是可以通过代码,来消除按键抖动带来的负面影响。下期课程——按键控制蜂鸣器,将加入同样的按键消抖模块,使用消抖的按键控制蜂鸣器鸣叫。敬请期待!