当前位置: 首页 > 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 110

led_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) begin
        d1 <= 1'b0;
        d2 <= 1'b0;
        d3 <= 1'b0;
      end
    else begin		//某个按键值变化时,LED将做亮灭翻转
        if ( led_ctrl[0] ) d1 <= ~d1;	
        if ( led_ctrl[1] ) d2 <= ~d2;
        if ( led_ctrl[2] ) d3 <= ~d3;
      end

assign 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)begin
	    if(rst_n == 1'b0)begin
	        D <= 2'b00;
	    end
	    else begin
	        D <= {D[0], data};  	//D[1]表示前一状态,D[0]表示后一状态(新数据) 
	    end
	end
	
//组合逻辑进行边沿检测

	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) begin
        d1 <= 1'b0;
        d2 <= 1'b0;
        d3 <= 1'b0;
      end
    else begin		//某个按键值变化时,LED将做亮灭翻转
        if ( led_ctrl[0] ) d1 <= ~d1;	
        if ( led_ctrl[1] ) d2 <= ~d2;
        if ( led_ctrl[2] ) d3 <= ~d3;
      end

assign 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博客 

相关文章:

  • 自动驾驶时间同步
  • vue3 开发电子地图功能
  • Daz3D角色UE5材质优化
  • 解锁塔能科技,开启工厂绿色转型与可持续发展双引擎
  • 基于 OpenHarmony 5.0 的星闪轻量型设备应用开发-Ch1 开发环境搭建
  • 0201概述-机器学习-人工智能
  • go-zero自动生成repository文件和测试用例
  • 无人机击落技术难点与要点分析!
  • 探索 OpenHarmony 开源硬件的学习路径:从入门到实战的全攻略
  • 14. git clone
  • MySQL 架构设计:数据库的“城市规划指南“
  • ubuntu18.04安装miniforge3
  • 基于Python的网络爬虫技术研究
  • OpenBayes 一周速览|1分钟生成完整音乐,DiffRhythm人声伴奏一键搞定; Stable Virtual Camera重塑3D视频创作
  • 按键消抖(用状态机实现)
  • Elasticsearch 学习规划
  • 技术优化实战解析:Stream重构与STAR法则应用指南
  • 16. git push
  • [ctfshow web入门] web33
  • Manifold-IJ 2022.1.21 版本解析:IntelliJ IDEA 的 Java 增强插件指南
  • 17网站一起做网店普宁/游戏推广对接平台
  • 建站视频网站/域名注册管理中心网站
  • 做网站英文怎么说/晋城今日头条新闻
  • 如何用wordpress建网站/域名注册商有哪些
  • 福州网站制作专业/营销型网站制作成都
  • 手机维修网站那个公司做的/网站推广方案策划