按键抖动延时+按键输入控制LED状态

前几日,针对多个按键输入时按键抖动延时考虑太过复杂,仿真效果不佳,后看到正点原子 蜂鸣器章节-抖动延时章节,有所启发,针对每个按键分别进行抖动延时处理,再根据所有抖动延时之后的按键值进行LED灯状态控制。

 

//****************************************Copyright (c)***********************************//
//原子哥在线教学平台:www.yuanzige.com
//技术支持:http://www.openedv.com/forum.php
//淘宝店铺:https://zhengdianyuanzi.tmall.com
//关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2023-2033
//All rights reserved                                  
//----------------------------------------------------------------------------------------
// File name:           key_led
// Created by:          正点原子
// Created date:        2023年2月22日14:17:02
// Version:             V1.0
// Descriptions:        按键控制LED灯实验
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//
module key_led(
    //input
    input               sys_clk,
    input               sys_rst_n,
    input        [3:0]  key,
    //output        
//    output   cnt1_start,
    output  reg  [1:0]   led_flag,
//    output  reg  [3:0]   key_valid,
//    output  reg  [19:0]  cnt1,
    output  reg  [3:0]   key_filter,
    output  reg  [3:0]  led 
    );
//参数
//parameter CNT_MAX = 25'd25000000;   
//parameter CNT_MAX1 = 20'd1000000;     

parameter CNT_MAX = 25'd2500;   //simulation time reduce 2500*20ns
parameter CNT_MAX1 = 20'd500;   //500*20ns
//reg define
reg  [24:0]  cnt;
//reg  [19:0]  cnt1;
//reg  [1:0]   led_flag;
//reg  [3:0]   key_valid;
//reg  [3:0]   key_down;
//reg  [3:0]   key1;
//reg  [3:0]   key2;
//reg cnt1_start;
wire  [3:0]   key_filter;//value after debounce
//*****************************************************
//**                    main code
//*****************************************************

//计数器计时0.5s
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        cnt <= 25'd0;
    else if(cnt < (CNT_MAX - 25'd1))
        cnt <= cnt + 25'd1;
    else
        cnt <= 25'd0;
end

////计数器计时20ms
//always @(posedge sys_clk or negedge sys_rst_n) begin
//    if(!sys_rst_n)
//        cnt1 <= 19'd0;
//    else if(cnt1_start==1 && cnt1 < (CNT_MAX1 - 19'd1))   //计数开始且计数未达最大值
//        cnt1 <= cnt1 + 19'd1;
//    else if(cnt1_start==1) 
//        cnt1 <= 19'd0;
//end

key_debounce #(
    .CNT_MAX1    (CNT_MAX1)  
)u_key0_debounce(
    .sys_clk       (sys_clk),
    .sys_rst_n     (sys_rst_n),
    .key           (key[0]),
    .key_filter    (key_filter[0])
    );

key_debounce #(
    .CNT_MAX1    (CNT_MAX1)  
)u_key1_debounce(
    .sys_clk       (sys_clk),
    .sys_rst_n     (sys_rst_n),
    .key           (key[1]),
    .key_filter    (key_filter[1])
    );

key_debounce #(
    .CNT_MAX1    (CNT_MAX1)  
)u_key2_debounce(
    .sys_clk       (sys_clk),
    .sys_rst_n     (sys_rst_n),
    .key           (key[2]),
    .key_filter    (key_filter[2])
    );

key_debounce #(
    .CNT_MAX1    (CNT_MAX1)  
)u_key3_debounce(
    .sys_clk       (sys_clk),
    .sys_rst_n     (sys_rst_n),
    .key           (key[3]),
    .key_filter    (key_filter[3])
    );

//延时20ms后判断按键是否按下有效
//always @(posedge sys_clk or negedge sys_rst_n) begin
//    if(!sys_rst_n)
//        key_valid <= 4'b0000;
//    else if(key[0]==key1[0]&&cnt1 == (CNT_MAX1 - 19'd2)) //抖动延时等于20ms且此时按键被按下,认为有效
//        key_valid <= 4'b0001;
//    else if(key[1]==key1[1]&&cnt1 == (CNT_MAX1 - 19'd2))
//        key_valid <= 4'b0010;
//    else if(key[2]==key1[2]&&cnt1 == (CNT_MAX1 - 19'd2))
//        key_valid <= 4'b0100;
//    else if(key[3]==key1[3]&&cnt1 == (CNT_MAX1 - 19'd2))
//        key_valid <= 4'b1000;
//    else if (key[0]==key1[0]&&cnt1 == (CNT_MAX1 - 19'd2)&& key_valid==4'b0001)
//        key_valid <= 4'b0000;
//    else if (key[1]==key1[1]&&cnt1 == (CNT_MAX1 - 19'd2)&& key_valid==4'b0010)
//        key_valid <= 4'b0000;  
//    else if (key[2]==key1[2]&&cnt1 == (CNT_MAX1 - 19'd2)&& key_valid==4'b0100)
//        key_valid <= 4'b0000;
//    else if (key[3]==key1[3]&&cnt1 == (CNT_MAX1 - 19'd2)&& key_valid==4'b1000)
//        key_valid <= 4'b0000;
//    else if(key==key1&&cnt1 == (CNT_MAX1 - 19'd2))
//        key_valid <= 4'b0000;
//end


//LED状态切换标志位
//  0  1  2  3
//  00 01 10 11
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led_flag <= 2'd0;
    else if(cnt == (CNT_MAX - 25'd1))   
        led_flag <= led_flag + 2'd1;
    else
        led_flag <= led_flag;
end    
    

    
//add debounce detection
// always @(posedge sys_clk or negedge sys_rst_n) begin
    // if(!sys_rst_n)
        // key_valid <= 4'b0000;
        // key_down <= 4'b0000;
    // else begin
        // case(key)    
        // 4'b1111 : key_down  <= 4'b0000;
        // 4'b1110 : begin
            // key_down <=4'b0001;
        // end
        // 4'b1101 : begin
            // key_down <=4'b0010;
        // end
        // 4'b1011 : begin
            // key_down <=4'b0100;
        // end
        // 4'b0111 : begin
            // key_down <=4'b1000;
        // end
        // endcase
        // end
        // end
        
    // assign key1=key;    //   assign语句副职只能用阻塞赋值   与通过always延迟两拍有何不同,always语句可以阻塞和非阻塞赋值
    // assign key2=key1;    
//    always @(posedge sys_clk or negedge sys_rst_n) begin
//    if(!sys_rst_n) begin
//      key1<= 4'b1111;
//      key2<= 4'b1111;
//      end
//    else begin
//      key1<=key;
//      key2<=key1;
//      end
//    end
      
      
      
    
//    always @(posedge sys_clk or negedge sys_rst_n) begin
//    if(!sys_rst_n) begin
//        key_valid <= 4'b0000;
//        key_down <= 4'b0000;
//        end
//    else begin
//        case(key)    //对按键进行判断
//        4'b1111 : begin
//            if(key!=key1)
//            cnt1_start  <= 1'b1;
           
//            else if(key==key1&& key_valid == 4'b0000)
//            cnt1_start<=1'b0;
//            end
//        4'b1110 : begin
//            // if(key[0]|key1[0]==1'b0)//连续两次按键为低,抖动计数开始 此种判断只能针对按下,松开时抖动无法去抖
//            if(key[0]!=key1[0])//
//            cnt1_start<=1'b1;
        
//            else if(key[0]==key1[0]&& key_valid[0]==4'b0001)
//            cnt1_start<=1'b0;
//        end
//        4'b1101 : begin
//            if(key[1]!=key1[1])//连续两次按键为低,抖动计数开始
//            cnt1_start<=1'b1;
//            else if(key[1]==key1[1]&& key_valid[1]==4'b0010)
//            cnt1_start<=1'b0;
//        end
//        4'b1011 : begin
//            if(key[2]!=key1[2])//连续两次按键为低,抖动计数开始
//            cnt1_start<=1'b1;
//            else if(key[2]==key1[2]&& key_valid[2]==4'b0100)
//            cnt1_start<=1'b0;
//        end
//        4'b0111 : begin
//            if(key[3]!=key1[3])//连续两次按键为低,抖动计数开始
//            cnt1_start<=1'b1;
//            else if(key[3]==key1[3]&& key_valid[3]==4'b1000)
//            cnt1_start<=1'b0;
//        end
//        endcase
//        end
//        end
        
    // assign key1=key;    
    // assign key2=key1;    
    
//LED控制(根据哪个KEY被按下,和led_flag,对LED进行赋值)
always @(posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        led <= 4'b0000;
    else begin
        case(~key_filter)
            4'b0000 : led <= 4'b0000;
            4'b0001 : begin 
                if(led_flag == 2'd0)
                    led <= 4'b0001;
                else if(led_flag == 2'd1)
                    led <= 4'b0010;
                else if(led_flag == 2'd2)
                    led <= 4'b0100;
                else
                    led <= 4'b1000;  
            end
            4'b0010 : begin 
                if(led_flag == 2'd0)
                    led <= 4'b1000;
                else if(led_flag == 2'd1)
                    led <= 4'b0100;
                else if(led_flag == 2'd2)
                    led <= 4'b0010;
                else
                    led <= 4'b0001;  
            end  
            4'b0100 : begin 
                if(led_flag[0] == 1'b0)
                    led <= 4'b1111;
                else
                    led <= 4'b0000;
            end        
            4'b1000 : led <= 4'b1111;
            default : ;
        endcase    
    end
end      
    
endmodule

去抖模块代码:
//****************************************Copyright (c)***********************************//
//原子哥在线教学平台:www.yuanzige.com
//技术支持:http://www.openedv.com/forum.php
//淘宝店铺:https://zhengdianyuanzi.tmall.com
//关注微信公众平台微信号:"正点原子",免费获取ZYNQ & FPGA & STM32 & LINUX资料。
//版权所有,盗版必究。
//Copyright(C) 正点原子 2023-2033
//All rights reserved                                  
//----------------------------------------------------------------------------------------
// File name:           key_debounce
// Created by:          正点原子
// Created date:        2023年2月16日14:20:02
// Version:             V1.0
// Descriptions:        按键消抖模块
//
//----------------------------------------------------------------------------------------
//****************************************************************************************//

module key_debounce(
    input        sys_clk   ,
    input        sys_rst_n ,

    input        key       ,   //外部输入的按键值
    output  reg  key_filter    //按键消抖后的值
);

//parameter define
parameter  CNT_MAX1 = 20'd100_0000;    //消抖时间20ms

//reg define
reg [19:0] cnt ;
reg        key_d0;            //将按键信号延迟一个时钟周期
reg        key_d1;            //将按键信号延迟两个时钟周期

//*****************************************************
//**                    main code
//*****************************************************

//对按键端口的数据延迟两个时钟周期
always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) begin
        key_d0 <= 1'b1;
        key_d1 <= 1'b1;
    end
    else begin
        key_d0 <= key;
        key_d1 <= key_d0;
    end 
end

//按键值消抖
always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n) 
        cnt <= 20'd0;
    else begin
        if(key_d1 != key_d0)    //检测到按键状态发生变化
            cnt <= CNT_MAX1;     //则将计数器置为20'd100_0000,
                                //即延时100_0000 * 20ns(1s/50MHz) = 20ms
        else begin              //如果当前按键值和前一个按键值一样,即按键没有发生变化
            if(cnt > 20'd0)     //则计数器递减到0
                cnt <= cnt - 1'b1;  
            else
                cnt <= 20'd0;
        end
    end
end

//将消抖后的最终的按键值送出去
always @ (posedge sys_clk or negedge sys_rst_n) begin
    if(!sys_rst_n)
        key_filter <= 1'b1;
    //在计数器递减到1时送出按键值
    else if(cnt == 20'd1) 
        key_filter <= key_d1;
    else
        key_filter <= key_filter;
end

endmodule

仿真代码:


`timescale  1ns/1ns   //仿真的单位/仿真的精度

module tb_key_led();

parameter  CLK_PERIOD = 20;
parameter  CNT_MAX = 25'd2500;  //仅用于仿真,对应500000ns
parameter  CNT_MAX1 = 25'd500;  //仅用于仿真,对应10000ns

reg            sys_clk;     //周期20ns
reg            sys_rst_n;
reg   [3:0]    key;

wire  [3:0]    led;
//wire   cnt1_start;
wire [1:0]   led_flag;
wire  [3:0]  key_filter;
//wire  [19:0]  cnt1;

initial begin
    sys_clk <= 1'b0;
    sys_rst_n <= 1'b0;
    key <= 4'b1111;
    #200
    sys_rst_n <= 1'b1;  
    #2000
    key <= 4'b1110;  //按下KEY0
    #20
    key <= 4'b1111;  //增加抖动
    #30
    key <= 4'b1110;  //增加抖动
    #50
    key <= 4'b1111;  //增加抖动
    #200
    key <= 4'b1110;  //增加抖动
    #2000000
    key <= 4'b1111;  //释放KEY0
    #2000000
    key <= 4'b1101;  //按下KEY1
    #2000000
    key <= 4'b1111;  //释放KEY1    
    #2000000
    key <= 4'b1011;  //按下KEY2
    #2000000
    key <= 4'b1111;  //释放KEY2    
    #2000000
    key <= 4'b0111;  //按下KEY3
    #2000000
    key <= 4'b1111;  //释放KEY3    
end

always #(CLK_PERIOD/2) sys_clk = ~sys_clk;

key_led  #(
    .CNT_MAX      (CNT_MAX),
    .CNT_MAX1      (CNT_MAX1)
    )
u_key_led(
    .sys_clk      (sys_clk  ), 
    .sys_rst_n    (sys_rst_n),
    .key          (key      ),
//    .cnt1_start   (cnt1_start      ),
    .led_flag   (led_flag      ),
    .key_filter    (key_filter     ),
//    .cnt1         (cnt1      ),
    .led          (led      )
    );

endmodule

仿真结果如下:

 抖动延时判断之后才更新按键状态,与设计相符。

 

 

posted @ 2024-04-15 23:50  古月照今尘  阅读(8)  评论(0编辑  收藏  举报