5.4 按键消抖

1、行列按键基本原理

1.1 原理图

在这里插入图片描述
第一行是高电平时,假如key2闭合,那么列输出就是0100
在这里插入图片描述
第二行是高电平时,假如key8闭合,那么列输出就是0001
在这里插入图片描述

1.2 实物图

按键是图片右下角4*4的按键
在这里插入图片描述
:R10、P10、M6、K3
:T10、R11、T12、R12
可以对按键进行任意编号
在这里插入图片描述

1.3 按键消抖

按键过程:按下抖动可能花销5-20ms,
将按下抖动+稳定期+释放抖动 转化成一个标准的矩形波(如上面波形);使用时钟是20ms的移位寄存器可以满足取到稳定期的信号
在这里插入图片描述
在这里插入图片描述

1.4 按键消抖设计

用数码管显示我们的按键,比如;按第0个按键,数码管最低位显示0;按第10个按键,数码管最低位显示A;
在这里插入图片描述

去抖的设计是:声明3个寄存器btn0、btn1、btn2,并将它们组合成移位寄存器,将移位方向定义为btn0->btn1->bun2

always@ (posedge btn_clk)
begin
btn0<=btn;
btn1<=btn0;
btn2<=btn1;
end

在这里插入图片描述
将按键输入送btn0。每隔20ms执行一次移位。
在每20ms后,btn0中存储的是当前的按键电平,btn1中存储的是20ms之前的按键电平,btn2中存储的是40ms之前的按键电平

assign btn_out=(btn2&btn1&btn0)|(~btn2&btn1&btn0)|(btn2&btn1&~btn0);

2、行列按键实践

2.1 行列代码

代码如下(示例):

reg[3:0] row=4'b0001;

always @ (posedge clk)begin
if (row[3:0]==4'b1000)
	row[3:0]=4'b0001;
else
	row[3:0]=row[3:0]<<1; 
end

always @ (negedge clk)begin
case (row[3:0])
	4'b0001:begin btn[3:0]=col;end
	4'b0010:begin btn[7:4]=col;end
	4'b0100:begin btn[11:8]=col;end
	4'b1000:begin btn[15:12]=col;end 
	default:btn=0; 
endcase
end

2.2 功能代码

2.2.1 分频代码

// Description:分频时钟
module divclk(
clk,clk_ms,btnclk
    );
input     clk;
output    clk_ms,btnclk;

reg [31:0] cnt1 = 0;
reg [31:0] btnclk_cnt = 0;
reg clk_ms = 0;//1ms的时钟
reg btnclk = 0;//20ms按键消抖的时钟    

//***************1ms的时钟**********************
always @ (posedge clk)begin //系统时钟分频50M/1000=50000 1000Hz
    if(cnt1==26'd25000)begin
        clk_ms = ~clk_ms;
        cnt1 = 0;
    end
    else 
        cnt1 = cnt1 + 1'b1;
end 
//***************1ms的时钟**********************

//***************20ms的时钟**********************   
always @ (posedge clk)begin//20ms  50M/50=100000 50Hz   
    if(btnclk_cnt==500000)begin
        btnclk = ~btnclk;
        btnclk_cnt = 0;
    end
    else 
        btnclk_cnt = btnclk_cnt + 1'b1;
end    
    
//***************20ms的时钟**********************    
endmodule

2.2.2 按键消抖代码

// Description: 按键消抖模块
module v_ajxd(
input  clk,
input  btn_clk,//20ms时钟
input  [3:0]col,//列(输入是一行一行的扫描)
output [3:0]row,//行,注意,这里是输出
output [15:0]btn_out//处理之后的按键输出
    );
reg [15:0] btn = 0;
reg [15:0] btn0 = 0;   
reg [15:0] btn1 = 0;
reg [15:0] btn2 = 0; 
reg [3:0] row = 4'b0001; 
//**************对每一行按键扫描*************
always @ (posedge clk)begin
	if(row[3:0]==4'b1000)
	   row[3:0] = 4'b0001;
	else
	   row[3:0] =  row[3:0]<<1;    
end    
//**************对每一行按键扫描*************

//**************读取值*************
always @ (negedge clk)begin
    case (row[3:0]) 
    4'b0001:begin btn[3:0] = col ;end
    4'b0010:begin btn[7:4] = col ;end
    4'b0100:begin btn[11:8] = col ;end
    4'b1000:begin btn[15:12] = col ;end
    default:begin btn =0 ;end
    endcase   
end 
//**************读取值*************
assign btn_out=(btn2&btn1&btn0)|(~btn2&btn1&btn0)|(btn2&btn1&~btn0);


//**************移位*************
always @ (posedge btn_clk)begin
    btn0 <= btn;
	btn1 <= btn0;
	btn2 <= btn1;  
end 
//**************移位*************
    
endmodule

2.2.3 显示模块代码

// Description: 数码管显示模块
module v_disp(
input clk,//输入是50MHz的时钟
input  rst,
input  [23:0]dispdate,
output [7:0]seg,
output [5:0]an
    );
reg [14:0] divclk_cnt = 0;  //分频计数值
reg divclk = 0;             //分频后的时钟
reg [7:0] seg = 8'b0;       //段码
reg [5:0] an = 6'b111110;   //位码
reg [3:0]disp_dat = 0;      //要显示的数据
reg [2:0]disp_bit = 0;      //要显示的位
parameter maxcnt=25000;
always @ (posedge clk)begin 
    if(divclk_cnt==maxcnt)begin
        divclk = ~divclk;
        divclk_cnt = 0;
    end
    else 
        divclk_cnt = divclk_cnt + 1'b1;        
end        

always @ (posedge divclk)begin
    if(disp_dat>=5)
        disp_bit = 0;
    else 
        disp_bit = disp_bit + 1'b1;        
    case (disp_bit)//**********位码***************   
    3'h0:begin disp_dat = dispdate[3:0];an = 6'b111110;end//显示第一个数码管,低电平有效
    3'h1:begin disp_dat = dispdate[7:4];an = 6'b111101;end//显示第二个数码管,低电平有效
    3'h2:begin disp_dat = dispdate[11:8];an = 6'b111011;end//显示第三个数码管,低电平有效
    3'h3:begin disp_dat = dispdate[15:12];an = 6'b110111;end//显示第四个数码管,低电平有效
    3'h4:begin disp_dat = dispdate[19:16];an = 6'b101111;end//显示第五个数码管,低电平有效
    3'h5:begin disp_dat = dispdate[23:20];an = 6'b011111;end//显示第六个数码管,低电平有效
    default:begin disp_dat = dispdate[15:12];an = 6'b111111;end//显示第四个数码管,低电平有效
    endcase
end 
//**********段码***************   
always @ (disp_dat)begin
    case (disp_dat)
    4'h0 : seg = 8'h3f; //显示"0",abcdrfg_dp分别是1111_1100,二进制倒过来看是0011_1111= 8'h3f
    4'h1 : seg = 8'h06; 
    4'h2 : seg = 8'h5b; 
    4'h3 : seg = 8'h4f; 
    4'h4 : seg = 8'h66; 
    4'h5 : seg = 8'h6d; //显示"5" abcdrfg_dp分别是1011_0110,二进制倒过来看是0110_1101= 8'h6d
    4'h6 : seg = 8'h7d; //显示"6"
    4'h7 : seg = 8'h07; //显示"7"
    4'h8 : seg = 8'h7f; //显示"8"
    4'h9 : seg = 8'h6f; //显示"9"
    4'ha : seg = 8'h77; //显示"a"
    4'hb : seg = 8'h7c; //显示"b"
    4'hc : seg = 8'h39; //显示"c"
    4'hd : seg = 8'h5e; //显示"d"
    4'he : seg = 8'h79; //显示"e"
    4'hf : seg = 8'h71; //显示"f"
    endcase
end
//**********段码*************** 
    
endmodule

2.2.4 顶层模块代码

module v1(
input  clk,
input  [11:0]sw,
input  [3:0]col,
output [3:0]row,
output [11:0]led,
output [7:0]seg,//段码
output [5:0]an//位码
    );
wire clk_ms,clk_20ms;    
wire [15:0]btnout;
reg [11:0] led = 12'b0;
reg [23:0] showdat = 0;   
v_disp a(//例化显示模块
.clk(clk),
.rst(0),
.dispdate(showdat),
.seg(seg),
.an(an)
    );
    
divclk my_divclk(//调用分频模块
.clk(clk),
.clk_ms(clk_ms),
.btnclk(clk_20ms)
    );    
reg[4:0] i=0;
always @ (posedge clk_ms)begin    
led<=btnout[11:0];//led调试使用,用led显示一下按键
for(i=0;i<=15;i=i+1)
    if(btnout[i]==1)
        showdat <= i;
end   
        
v_ajxd ajxd_uut(//调用按键消抖
.clk(clk_ms),
.btn_clk(clk_20ms),
.col(col),
.row(row),
.btn_out(btnout)
    );    
        
endmodule
  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值