当前位置: 首页 > news >正文

【特权FPGA】之按键消抖

 完整代码如下所示:

`timescale 1ns / 1ps// Company: 
// Engineer:		 特权
//
// Create Date:  
// Design Name:    
// Module Name: 
// Project Name:   
// Target Device:  
// Tool versions:  
// Description:
//
// Dependencies:
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 欢迎加入EDN的FPGA/CPLD助学小组一起讨论:http://group.ednchina.com/1375///说明:当三个独立按键的某一个被按下后,相应的LED被点亮;
//		再次按下后,LED熄灭,按键控制LED亮灭
/*****************************************************************************************************************/
module sw_debounce(clk,rst_n,sw1_n,sw2_n,sw3_n,led_d1,led_d2,led_d3);input   clk;	//主时钟信号,50MHz
input   rst_n;	//复位信号,低有效
input   sw1_n,sw2_n,sw3_n; 	//三个独立按键,低表示按下
output  led_d1,led_d2,led_d3;	//发光二极管,分别由按键控制//--------------------------------------------------------------------------------------------------------------------reg[2:0] key_rst;  // 20ms滤除抖动always @(posedge clk  or negedge rst_n)if (!rst_n) key_rst <= 3'b111;else key_rst <= {sw3_n,sw2_n,sw1_n};reg[2:0] key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中always @ ( posedge clk  or negedge rst_n )if (!rst_n) key_rst_r <= 3'b111;else key_rst_r <= key_rst;//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期 
wire[2:0] key_an ;
assign key_an = key_rst_r & (~key_rst);
/*
key_rst     1 1 1 0 0 1 
~key_rst    0 0 0 1 1 0
key_rst_n   _ 1 1 1 0 0 1
key_an        0 0 1 0 0
*/
//-------------------------------------------------------------------------------------------------------------------------reg[19:0]  cnt;	//计数寄存器always @ (posedge clk  or negedge rst_n)if (!rst_n) cnt <= 20'd0;	//异步复位else if(key_an) cnt <=20'd0;// 按键消抖代码核心else cnt <= cnt + 1'b1;reg[2:0] low_sw;always @(posedge clk  or negedge rst_n)if (!rst_n) low_sw <= 3'b111;else if (cnt == 20'hfffff) 	//满20ms,将按键值锁存到寄存器low_sw中	 cnt == 20'hfffff,之所以这样设计,人按下按键通常至少也要几百毫秒的时间,这20ms的时间窗口内,什么也不需要做。low_sw <= {sw3_n,sw2_n,sw1_n};//---------------------------------------------------------------------------
reg  [2:0] low_sw_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中always @ ( posedge clk  or negedge rst_n )if (!rst_n) low_sw_r <= 3'b111;else low_sw_r <= low_sw;
/*
low_sw		111 111 111 110 110 110  
~low_sw     000 000 000 001 001 001
low_sw_r        111 111 111 110 110 110led_ctrl	000 000 000 001 000 000 */
//当寄存器low_sw由1变为0时,led_ctrl的值变为高,维持一个时钟周期 
wire[2:0] led_ctrl =  low_sw_r[2:0] & ( ~low_sw[2:0]);reg d1;
reg d2;
reg d3;always @ (posedge clk or negedge rst_n)if (!rst_n) begind1 <= 1'b0;d2 <= 1'b0;d3 <= 1'b0;endelse begin		//某个按键值变化时,LED将做亮灭翻转if ( led_ctrl[0] ) d1 <= ~d1;	if ( led_ctrl[1] ) d2 <= ~d2;if ( led_ctrl[2] ) d3 <= ~d3;endassign led_d3 = d1 ? 1'b1 : 1'b0;		//LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;endmodule

边沿检测

always @(posedge clk  or negedge rst_n)if (!rst_n) key_rst <= 3'b111;else key_rst <= {sw3_n,sw2_n,sw1_n};reg[2:0] key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中always @ ( posedge clk  or negedge rst_n )if (!rst_n) key_rst_r <= 3'b111;else key_rst_r <= key_rst;//当寄存器key_rst由1变为0时,led_an的值变为高,维持一个时钟周期 
wire[2:0] key_an ;
assign key_an = key_rst_r & (~key_rst);
/*
key_rst     1 1 1 0 0 1 
~key_rst    0 0 0 1 1 0
key_rst_n   _ 1 1 1 0 0 1
key_an        0 0 1 0 0
*/

key_rst_n的数据延迟了一个时钟周期。

知识点1:

按位与(&):

2个多位操作数按位进行与运算,各位的结果按顺序组成一个新的多位数。例如,a=2’b10,b=2’b11,则a&b的结果为2’b10;[1]

知识点2:

 设置两个寄存器,对前一状态和后一状态进行寄存,若前后两个状态不同,则检测到了边沿。对于上升沿和下降沿的确定可以用组合逻辑比较来确定。若前一状态D[1]为高电平,后一状态D[0]为低电平,则为下降沿,反之为上升沿。[2]

当然,TQ老师用的方法不一样,道理是相同的。个人推荐文献2中提到的办法。要注意的是,后者是双边沿检测。

//设置两个寄存器,实现前后电平状态的寄存
//相当于对dat_i 打两拍always @(posedge clk or negedge rst_n)beginif(rst_n == 1'b0)beginD <= 2'b00;endelse beginD <= {D[0], data};  	//D[1]表示前一状态,D[0]表示后一状态(新数据) endend//组合逻辑进行边沿检测assign  pos_edge = ~D[1] & D[0];assign  neg_edge = D[1] & ~D[0];assign  data_edge = pos_edge | neg_edge;

接下来是核心代码。按键按下了,计数器清零,等待20ms,然后再把按键值输出给led灯。

//-------------------------------------------------------------------------------------------------------------------------reg[19:0]  cnt;	//计数寄存器always @ (posedge clk  or negedge rst_n)if (!rst_n) cnt <= 20'd0;	//异步复位else if(key_an) cnt <=20'd0;// 按键消抖代码核心else cnt <= cnt + 1'b1;reg[2:0] low_sw;always @(posedge clk  or negedge rst_n)if (!rst_n) low_sw <= 3'b111;else if (cnt == 20'hfffff) 	//满20ms,将按键值锁存到寄存器low_sw中	 cnt == 20'hfffff,之所以这样设计,人按下按键通常至少也要几百毫秒的时间,这20ms的时间窗口内,什么也不需要做。low_sw <= {sw3_n,sw2_n,sw1_n};//---------------------------------------------------------------------------

reg d1;
reg d2;
reg d3;always @ (posedge clk or negedge rst_n)if (!rst_n) begind1 <= 1'b0;d2 <= 1'b0;d3 <= 1'b0;endelse begin		//某个按键值变化时,LED将做亮灭翻转if ( led_ctrl[0] ) d1 <= ~d1;	if ( led_ctrl[1] ) d2 <= ~d2;if ( led_ctrl[2] ) d3 <= ~d3;endassign led_d3 = d1 ? 1'b1 : 1'b0;		//LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;

参考文献

[1]FPGA_Verilog基础篇:verilog基本逻辑运算_verilog两组多位宽信号相与-CSDN博客

[2]FPGA基础学习——Verilog实现的边沿检测(上升沿下降沿检测)及Modelsim仿真_verilog 上升沿检测-CSDN博客 


http://www.mrgr.cn/news/97936.html

相关文章:

  • 蓝桥杯备赛知识点总结
  • YOLOv8主干网络升级——融合ResNet18/34/50/101的高效特征提取架构设计
  • 天梯集训笔记整理
  • 剑指offer经典题目(三)
  • Java—HTML:3D形变
  • 简化DB操作:Golang 通用仓库模式
  • 【正点原子】一键锁定IP:STM32MP135 开机就上网!
  • windows主机中构建适用于K8S Operator开发环境
  • C++学习之ORACLE①
  • OpenGL学习笔记(简介、三角形、着色器、纹理、坐标系统、摄像机)
  • Java从入门到“放弃”(精通)之旅——启航①
  • 使用Go语言实现自动清理应用系统日志
  • 【Linux】网络编程
  • 2025ArkTS基础UI(一)——Column、Row、Text、Button组件
  • YOLO 8 入坑(持续更新)
  • wsl2+ubuntu22.04安装blenderproc教程
  • 蓝桥杯刷题--宝石组合
  • Kubernetes 入门篇之网络插件 calico 部署与安装
  • [leetcode]01背包问题
  • opencv人脸性别年龄检测