网站建设公司主要,做网站能赚钱吗知乎,中国国音电商平台官网,做一个打鱼网站需要多少钱文章目录 一、按键消抖简介1.1、为什么要按键消抖 二、C4开发板原理图三、按键消抖源码3.1、方案一#xff08;每当检测到下降沿便开始重新计数#xff09;3.2、方案二#xff08;检测到第一次下降沿后便开始计数#xff09; 四、仿真代码及仿真波形图五、拓展#xff1a;… 文章目录 一、按键消抖简介1.1、为什么要按键消抖 二、C4开发板原理图三、按键消抖源码3.1、方案一每当检测到下降沿便开始重新计数3.2、方案二检测到第一次下降沿后便开始计数 四、仿真代码及仿真波形图五、拓展按键消抖版按键控制LED状态 一、按键消抖简介
1.1、为什么要按键消抖
生活中常用的按键为机械按键而机械按键在按下后就会产生按键抖动
按键抖动按键抖动通常的按键所用开关为机械弹性开关当机械触点断开、闭合时由于机械触点的弹性作用一个按键开关在闭合时不会马上稳定地接通在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。当按下一次按键可能在A点检测到一次低电平在B点检测到一次高电平在C点又检测到一次低电平。同时抖动是随机不可测的。那么按下一次按键抖动可能会误以为按下多次按键。 一般我们认为抖动存在时间保持在5-10ms左右而在5-10ms后才会保持稳定 因此我们很容易意识到可以通过延时采样稳定的按键信号进行消抖。而本次设计中博主在消抖后输出的是一个脉冲信号。
本文博主提出两种按键消抖的解决方案
第一种每当检测到下降沿便开始重复进行20ms计数第二种检测到第一次下降沿后进行一次并只进行这一次20ms计数
第一种方案为博主自行编写代码略显繁琐。第二种方案为博主老师的方法较容易理解但博主认为从第一次下降沿开始便检测有些许不太严谨大家可以依据自己的喜好进行选择。
二、C4开发板原理图 由原理图可以看出博主的开发板按键按下后低电平有效博主接下来提供的代码请各位根据自己实际的开发板进行适当的修改
三、按键消抖源码
3.1、方案一每当检测到下降沿便开始重新计数
源码分析 在方案一中博主设计了key_r0、key_r1、key_r2三个中间寄存器分别用于同步按键信号key_in并进行打两拍。大家可以认为同步打拍就是在时序电路中分别将前一个信号的值寄存下来第一次为同步后续即为打拍这里打一拍还是打两拍取决于各位甚至只同步不打拍也可以进行下降沿检测。 由于是在时序电路中进行非阻塞赋值因此三个中间信号分别相差了一个时钟周期而博主就是利用后两个信号进行下降沿检测具体如图 由波形图可以看出我们可以通过key_r1取反并逻辑与上key_r2的运算检测出下降沿就即assign nedge ~key_r1 key_r2 在本方案中博主设计的20ms计数器在不断的计数但是通过前面的分析我们得知要想按键消抖应该在检测到下降沿的时候才开始计数因此博主在计时器中添加了检测nedge的条件一旦检测到nedge就会自动将进行计数器清零以此确保计数器检测到下降沿后是从0开始计数。 但大家又会想到按键按下的时间如果远远大于20ms计数器就会不止一次地计数到20ms而我们也将采样多次前文博主说道要求输出脉冲信号因此在按键按下的低电平持续时间内会产生多个脉冲信号并不符合要求。对此博主的解决方案是在计数器计数到最大值-1代码中是MAX - 2 因为计数是从0开始时便输出脉冲信号而一旦计数器计数到最大值MAX - 1便将cnt保持在最大值从而避免产生多个脉冲。最大值-1与最大值只相差一个时钟周期20ns因此影响可以忽略不计。 同时注意在一切条件都满足后输出的key_out的值不应该置1。如果只是单纯置1假设抖动后按键仍保持在高电平或者松开按键时产生的释放抖动也会输出一个脉冲信号很明显在这两种情况下是不成立的因此key_out的值应该赋为key_in(或者三个中间信号均可)并取反按键低电平有效需要取反并且在一个时钟周期后置0初始值为0才是输出脉冲信号。 本方案每当检测到下降沿便会重新开始20ms计数
module key_filter(input wire clk ,input wire rst_n ,input wire key_in ,//按键输入信号output reg key_out //输出稳定的脉冲信号
);parameter MAX 20d1_000_000;reg [19:0] cnt_delay ; //20ms延时计数寄存器
wire add_cnt_delay ; //开始计数的标志
wire end_cnt_delay ; //结束计数的标志reg key_r0 ; //同步
reg key_r1 ; //打一拍
reg key_r2 ; //打两拍wire nedge ; //下降沿寄存器//同步打拍
always (posedge clk or negedge rst_n) beginif(!rst_n)beginkey_r0 1b1;key_r1 1b1;key_r2 1b1;endelse beginkey_r0 key_in; //同步key_r1 key_r0; //寄存一拍key_r2 key_r1; //寄存两拍end
end//20ms计数器
always (posedge clk or negedge rst_n)beginif(!rst_n)begincnt_delay 1b0;endelse if(add_cnt_delay )beginif(nedge)begin //检测到下降沿从0开始计数cnt_delay 1b0;endelse if(cnt_delay MAX - 1b1)begincnt_delay cnt_delay; //计数计满结束后保持避免产生多个输出脉冲endelse begincnt_delay cnt_delay 1b1;endendelse begincnt_delay 1b0;end
endassign nedge ~key_r1 key_r2; //下降沿检测
assign add_cnt_delay 1b1;
assign end_cnt_delay add_cnt_delay cnt_delay MAX - 1b1;//key_out脉冲信号赋值
always(posedge clk or negedge rst_n)beginif(!rst_n)beginkey_out 1b0;endelse if(cnt_delay MAX - 2d2)begin //计数到最大值减1时产生按键脉冲key_out ~key_in;//不可直接置为1endelse beginkey_out 1b0;end
endendmodule3.2、方案二检测到第一次下降沿后便开始计数
源码分析
原理与第一种方案类似但是此方案引入了中间变量flag初始值为0检测到下降沿后便将flag置为1此时再将flag作为开始计数的条件同时计满20ms后再将flag置为0也就省去了在最大值-1时赋值key_out并保持计数器最大值的繁琐。此方法与方案一的区别就在于方案一相当于是在抖动过程中最后一次下降沿才开始计数而此方案是在抖动过程中的第一次下降沿便开始计数。
/**************************************功能介绍***********************************
Date : 2023-07-26 14:43:33
Author : majiko
Version : 1.0
Description: 按键消抖模块1位按键
*********************************************************************************///---------模块及端口声名------------------------------------------------------
module key_filter( input clk ,input rst_n ,input key_in ,output reg key_down //输出脉冲信号按键按下1次
);
//---------参数定义--------------------------------------------------------- parameter TIME_20MS 1000_000;//20ms//---------内部信号定义-----------------------------------------------------reg key_r0 ;//同步reg key_r1 ;//打两拍reg key_r2 ;wire n_edge ;//下降沿reg flag ;//计数器计数的标志信号按键按下抖动标志reg [19:0] cnt_20ms ;//20ms计数器wire add_cnt_20ms ;wire end_cnt_20ms ;//****************************************************************
//--同步打拍
//****************************************************************//同步将key_in信号同步到clk时钟域下面always (posedge clk or negedge rst_n)begin if(!rst_n)beginkey_r0 1b1;end else begin key_r0 key_in;end end//打两拍always (posedge clk or negedge rst_n)begin if(!rst_n)beginkey_r1 1b1;key_r2 1b1;end else begin key_r1 key_r0;key_r2 key_r1;end end//****************************************************************
//--n_edge
//****************************************************************assign n_edge ~key_r1 key_r2;//下降沿检测// assign p_edge key_r1 ~key_r2;//上升沿检测//****************************************************************
//--cnt_20ms
//****************************************************************always (posedge clk or negedge rst_n)begin if(!rst_n)begincnt_20ms d0;end else if(add_cnt_20ms)begin if(end_cnt_20ms)begin cnt_20ms d0;endelse begin cnt_20ms cnt_20ms 1b1;end endend assign add_cnt_20ms flag;assign end_cnt_20ms add_cnt_20ms cnt_20ms TIME_20MS - 1;//****************************************************************
//--flag
//****************************************************************always (posedge clk or negedge rst_n)begin if(!rst_n)beginflag d0;end else if(n_edge)begin flag 1b1;end else if(end_cnt_20ms)beginflag 1b0;endend//****************************************************************
//--key_down
//****************************************************************always (posedge clk or negedge rst_n)begin if(!rst_n)beginkey_down 1d0;end else if(end_cnt_20ms)begin key_down ~key_in;end else begin key_down 1b0;end endendmodule四、仿真代码及仿真波形图
本次仿真较为随意只是为了展示逻辑功能
源码
timescale 1ns/1ns
module tb_key_filter();reg clk ;
reg rst_n ;
reg key_in ;wire key_out; parameter CYCLE 20;
always #(CYCLE / 2) clk ~clk;initial beginclk 1b0;rst_n 1b1;key_in 1b1;#20rst_n 1b0;#20rst_n 1b1;#60key_in 1b0;#(30*CYCLE);$stop;
endkey_filter#(.MAX(10)) u_key_filter(.clk (clk ) ,.rst_n (rst_n ) ,.key_in (key_in ) ,.key_out(key_out)
);endmodule仿真波形图
可以看出成功输出了一次脉冲信号key_out
五、拓展按键消抖版按键控制LED状态
按键消抖模块
module key_filter(input wire clk ,input wire rst_n ,input wire [3:0] key_in ,//按键输入信号output reg [3:0] key_out //输出稳定的脉冲信号
);parameter MAX 20d1_000_000;reg [19:0] cnt_delay ; //20ms延时计数寄存器
wire add_cnt_delay ; //开始计数的标志
wire end_cnt_delay ; //结束计数的标志reg [3:0] key_r0 ; //同步
reg [3:0] key_r1 ; //打一拍
reg [3:0] key_r2 ; //打两拍wire nedge ; //下降沿寄存器//同步打拍
always (posedge clk or negedge rst_n) beginif(!rst_n)beginkey_r0 4b1111;key_r1 4b1111;key_r2 4b1111;endelse beginkey_r0 key_in; //同步key_r1 key_r0; //寄存一拍key_r2 key_r1; //寄存两拍end
end//20ms计数器
always (posedge clk or negedge rst_n)beginif(!rst_n)begincnt_delay 1b0;endelse if(add_cnt_delay )beginif(nedge)begin //检测到下降沿从0开始计数cnt_delay 1b0;endelse if(cnt_delay MAX - 1b1)begincnt_delay cnt_delay; //计数计满结束后保持避免产生多个输出脉冲endelse begincnt_delay cnt_delay 1b1;endendelse begincnt_delay 1b0;end
endassign nedge (~key_r1[0] key_r2[0]) || (~key_r1[1] key_r2[1]) || (~key_r1[2] key_r2[2]) || (~key_r1[3] key_r2[3]); //下降沿检测
assign add_cnt_delay 1b1;
assign end_cnt_delay add_cnt_delay cnt_delay MAX - 1b1;//key_out脉冲信号赋值
always(posedge clk or negedge rst_n)beginif(!rst_n)beginkey_out 4b0000;endelse if(cnt_delay MAX - 2d2)begin //计数到最大值减1时产生按键脉冲key_out ~key_in;endelse beginkey_out 4b0000;end
endendmoduleLED控制模块
module led (input wire clk ,input wire rst_n ,input wire [3:0] key_in ,output reg [3:0] led
);parameter MAX 15_000_000 ;
reg [25:0] cnt ;
reg [3:0] flag;always (posedge clk or negedge rst_n) beginif(!rst_n)begincnt 1b0;endelse if(cnt MAX - 1b1)begincnt 1b0;endelse begincnt cnt 1b1;end
endreg [2:0] state ;always (posedge clk or negedge rst_n)beginif(!rst_n)beginstate 1b0;endelse if(cnt MAX - 1b1)beginstate state 1b1;endelse beginstate state;end
endalways(posedge clk or negedge rst_n)beginif(!rst_n)beginflag 4b0000;endelse if(key_in[0] 1b1)beginflag 4b0001;endelse if(key_in[1] 1b1)beginflag 4b0010;endelse if(key_in[2] 1b1)beginflag 4b0100;endelse if(key_in[3] 1b1)beginflag 4b1000;endelse beginflag flag;end
endalways (posedge clk or negedge rst_n) beginif(!rst_n)beginled 4b0001;endelse if(flag 4b0001)begincase(state)3d0 : led 4b0000;3d1 : led 4b0001;3d2 : led 4b0011;3d3 : led 4b0111;3d4 : led 4b1111;3d5 : led 4b0111;3d6 : led 4b0011;3d7 : led 4b0001;default:;endcaseendelse if(flag 4b0010 cnt MAX - 1b1)beginled {led[2:0],led[3]};endelse if(flag 4b0100 cnt MAX - 1b1)beginled {led[2:0],~led[3]};endelse if(flag 4b1000 cnt MAX - 1b1)beginled 4b1111;end
endendmodule顶层模块
module top_key_led(input wire clk ,input wire rst_n ,input wire [3:0] key_in ,output wire [3:0] led
);wire [3:0] key_out;key_filter u_key_filter(.clk (clk ),.rst_n (rst_n ),.key_in (key_in),.key_out (key_out)
);led u_led(.clk (clk ),.rst_n (rst_n ),.key_in (key_out),.led (led )
);endmodule注意led模块有一点问题在key0跑马灯开始后再按下key1key2时呈现效果也许不是流水灯因为跑马灯开始后led状态不再是4’b0001 六、参考资料 https://blog.csdn.net/weixin_43828944/article/details/122360794