• FPGA之旅设计99例之第二例-----按键


    一.简介

    这是FPGA之旅的第二个设计实例了,按键在项目中的作用是非常大的,使用的很频繁,本例将带大家设计一个实用的按键模块。

    欢迎关注微信公众号 FPGA之旅 哦
    可以在上面联系,有什么问题的话

    二. 按键电路

    按键为输入设备,通过电路图可以知道,当按键按下的时候,FPGA会检测到低电平,按键没有按下的时候,FPGA检测到的是高电平。

    在这里插入图片描述


    三. Verilog代码编写

    直接来一段最简单的按键检测的代码编写,都不用仿真。

    按键按下,LED灯状态取反。

    module KEY(
    	input		clk,
        input		rst_n,
        input		key,
        output	reg	led
    );
        always@(posedge clk or negedge rst_n)
        begin
            if(rst_n == 1'b0) 
                led <= 1'b0;
            else if(key == 1'b0)
                led <= ~led;
            else
                led <= led;
        end
    endmodule 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    当按键按下后,LED的状态取反,这个在仿真的时候是可以看到变化的,但是实际上板测试的话,是没有效果的,因为clk的时钟周期一般为20ns,每次按键按下的持续时间可以达到ms以上,所以LED会多次取反,所以这么简单粗暴是不可以的,需要我们做一些额外处理。此外在按键按下的瞬间,电平会出现不稳定的情况,也需要进行处理。解决这些问题,正是这个例程的重点。


    四. 解决方案设计

    按键按下的时候,一共有两个问题

    1. 电平不稳定
    2. 短时间内重复检测

    第一个问题可以通过按键消抖来解决,第二个问题,可以在按键消抖的基础上,增加一些判断来解决,于是就有了以下三种模式

    1. 模式一,按下生效,释放,算一次
    2. 模式二,按下,释放生效,算一次
    3. 模式三,按下,一段时间算一次

    这里通过状态机的方式来实现,第一步就是要分析一共有几个状态。

    • 空闲态:按键没有按下时,所处的状态。
    • 消抖态: 按键按下后,进入消抖态,在此期间,如果按键释放了的话,回到空闲态,否则进入延时态。
    • 延时态:延时,直到按键释放。
    • 释放态:按键释放,回归到空闲态。

    模式一,可以在消抖态完成后,生效。
    模式二,可以在释放态,生效。
    模式三,可以在延时态,生效。
    完美,这不就全部都解决了嘛! 代码如下。

    //按键消抖
    module btn_dis_shake(
    			
    	input			clk,
    	input			rst_n,
    	
    	input			ikey,				//按键输入
    	output			okey				//按键输出
    
    );
    
    //模式
    //0   按下生效,抬起,算一次
    //1   按下抬起,算一次
    //2	按下后,一段时间算一次
    parameter			mode = 2;
    localparam			S_IDLE		=		'd0;
    localparam			S_DIS_SHAKE =       'd1;
    localparam			S_DEALY		=		'd2;
    localparam			S_UP		=		'd3;
    
    localparam			DIS_SHAKE	=	'd6000;		//消抖延时
    localparam			DELAY		=	'd50000;	//模式2中,一段时间
    
    reg[3:0] state	,	next_state;
    
    wire neg_key,pos_key;	//按键下降沿上升沿
    reg	 key0,key1;			//按键状态储存
    
    reg[30:0]	delay_cnt;
    
    assign		neg_key =	key1 & (~key0);		//判断按键信号的下降沿
    assign		pos_key	=	(~key1) & key0;		//判断按键信号的上升沿
    
    
    //根据模式来判断按键输出
    assign 	okey 	=	(mode == 0 &&  state == S_DIS_SHAKE && delay_cnt == DIS_SHAKE) ? 1'b1 : (mode == 1 && state == S_UP)?1'b1:(mode==2 && state == S_DEALY && delay_cnt == DELAY) ? 1'b1:1'b0;
    
    
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n  == 1'b0)
    	begin
    			key0 <= 1'b1;
    			key1 <= 1'b1;
    	end
    	else
    	begin
    		key0 <= ikey;
    		key1 <= key0;
    	end
    end
    
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		state <= S_IDLE;
    	else
    		state <= next_state;
    end
    
    always@(*)
    begin
    	case(state)
    		S_IDLE:
    			if(neg_key	==	1'b1)
    				next_state <= S_DIS_SHAKE;
    			else
    				next_state <= S_IDLE;
    		S_DIS_SHAKE:			//按下消抖
    				if(delay_cnt == DIS_SHAKE)
    					next_state <= S_DEALY;
    				else if(pos_key == 1'b1)
    					next_state <= S_IDLE;
    				else
    					next_state <= S_DIS_SHAKE;
    		S_DEALY:			//延时
    			if(delay_cnt == DELAY	&& pos_key == 1'b1)
    				next_state <= S_UP;
    			else if( pos_key == 1'b1)
    				next_state <= S_UP;
    			else
    				next_state <= S_DEALY;
    		S_UP:
    			next_state <= S_IDLE;
    		default:	next_state <= S_IDLE;
    	endcase
    end
    
    //延时计数
    always@(posedge clk or negedge rst_n)
    begin
    	if(rst_n == 1'b0)
    		delay_cnt <= 'd0;
    	else if(state != next_state)
    		delay_cnt  <= 'd0;
    	else if(state == S_DIS_SHAKE)
    		delay_cnt <= delay_cnt + 1'b1;
    	else if(state == S_DEALY && delay_cnt == DELAY)
    		delay_cnt <= 'd0;
    	else if(state == S_DEALY)
    		delay_cnt <= delay_cnt + 1'b1;
    	else
    		delay_cnt <= 'd0;
    end
    endmodule 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106

    代码是写完了,对不对呢 ? 上仿真!!!仿真的时候别忘记了将DIS_SHAKE这个参数调小一点了,可以设置为2就可以了,否则,你可以试试哦,就只对模式一进行仿真,其他的模式,也可以自行尝试喔!

    `timescale  1ns/1ps
    module testbeach();
    
        reg clk;
        reg rst_n;
        reg ikey;
        wire okey;
        	
       	always #50 clk <= ~clk; 
        initial begin
            
           clk = 1'b0;
           rst_n = 1'b1;
           ikey = 1'b1;
            
            #100
            rst_n = 1'b0;
            #100
            rst_n = 1'b1;
            
            ikey = 1'b0;  //按下
            #400
            ikey = 1'b1;  //释放
            #200
            ikey = 1'b0;  //按下
            #600
            ikey = 1'b1;  //释放
     
        end
    
    btn_dis_shake #(.mode(0))btn_dis_shakeHP(
    			
        .clk    (clk),
        .rst_n  (rst_n),
    	
        .ikey    (ikey),				//按键输入
        .okey	(okey)			//按键输出
    );    
    endmodule
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    当当当当!!! 完美对应起来,测试通过!
    在这里插入图片描述

  • 相关阅读:
    【基于Python加django的学生综合成绩管理系统-哔哩哔哩】 https://b23.tv/6zjxc4Z
    2022/8/6
    xxl-job分布式调度框架
    最详细记录安装NCNN:windows,NCNN下载和编译
    【linux命令讲解大全】105.掌握磁盘配额管理的edquota命令
    vue面试题
    Qt 拖放功能详解:理论与实践并举的深度指南
    Java中 数组的定义与使用
    pytorch的backward()的底层实现逻辑
    用SuperMap版leaflet框架对SuperMap iServer发布的3个数据集进行叠加分析
  • 原文地址:https://blog.csdn.net/weixin_44678052/article/details/126305983