四、按键控制跑马灯

1、key_filter.v

//按键消抖模块,延时法
//延时法:由于机械按键按下时,机械按键会产生抖动,抖动时间一般在5-10ms左右
//所以我们可以采用直接检测到按键被按下时,让延时计数器开始计数、计数20ms后再对按键信号进行采样
//1.如何检测按键被按下了?因为按键初值为高电平,按下后变为低电平,所以当按键按下时必然会出现下降沿
//设计如何检测下降沿?对输入信号进行同步打拍,同步打拍的作用:①消除亚稳态,
//②由于同步信号key_r0和打拍信号key_r1有一个时钟周期的延迟,所以我们可以采用打拍信号key_r1不变&对同步信号取反
//我们要将下降沿nedge信号作为判断条件,因此我们需要nedge信号高电平保持一个时钟周期

//检测到按键被按下,让延时计数器开始计数,延时计数20ms
//延时计数器如何才能开始工作,因为代码中我们采用的是下降沿nedge信号作为按键按下的的判断条件
//直接把nedge信号作为计数器开始累加的条件是不行的,我们需要持续的有效信号让计数器开始计数
//因此就产生了标志信号flag信号(当下降沿有效时把flag信号拉高,当计数完成时flag信号拉低,其他情况保持不变)
//这样flag信号就可以作为计数器的开启条件

module  key_filter(
    input   wire        clk     ,//系统时钟
    input   wire        rst_n   ,//复位信号
    input   wire        key     ,//输入的按键信号

    output  reg         key_flag  //输出按键被按下时的标志信号
);

parameter       CNT_MAX_20MS = 20'd999_999;

reg [19:0]      cnt_20ms;
wire            add_cnt_20ms;
wire            end_cnt_20ms;

reg             key_r0;//同步
reg             key_r1;//打拍
reg             flag  ;//标志信号定义
wire            nedge ;//下降沿标志信号

//20ms延时计数器模块
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_20ms <= 20'd0;
    end
    else if(add_cnt_20ms)begin
        if(end_cnt_20ms)begin
            cnt_20ms <= 20'd0;
        end
        else begin
            cnt_20ms <= cnt_20ms + 1'd1;
        end
    end
    else begin
        cnt_20ms <= cnt_20ms;
    end
end

assign add_cnt_20ms = flag;//开始计数的条件
assign end_cnt_20ms = add_cnt_20ms && cnt_20ms == CNT_MAX_20MS;

//进行同步打拍检测下降沿
//同步和打拍
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_r0 <= 1'b1;
        key_r1 <= 1'b1;
    end
    else begin
        key_r0 <= key   ;
        key_r1 <= key_r0;
    end
end

assign nedge = key_r1 & (~key_r0) ;//下降沿信号的描述

//出现下降沿,让延时计数器开始计数
//flag信号赋值
//用flag信号对nedge信号进行寄存
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
       flag <= 1'b0; 
    end
    else if(nedge)begin
        flag <= 1'b1;
    end
    else if(end_cnt_20ms)begin
        flag <= 1'b0;
    end
    else begin
        flag <= flag;
    end
end

//描述输出,时序逻辑
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        key_flag <= 1'b0;
    end
    else if(end_cnt_20ms)begin
        key_flag <= ~key;
    end
    else begin
        key_flag <= 1'b0;
    end
end

endmodule

2、d_key_led.v

module d_key_led (
    input   wire        clk  ,
    input   wire        rst_n,
    input   wire  [1:0] key ,

    output  reg [3:0]   led
);

parameter CNT_MAX = 25'd24_999_999;//0.5s计数器最大值
reg [24:0] cnt_500ms   ;
wire       add_cnt_500ms;
wire       end_cnt_500ms;

wire [1:0] key_flag;

reg [7:0]   led_r;
reg [3:0]   led_w;//流水灯信号寄存

reg [1:0]   flag ;

key_filter u1_key_filter(
  .  clk     (clk),//系统时钟
  .  rst_n   (rst_n),//复位信号
  .  key     (key[0]),//输入的按键信号

  .  key_flag(key_flag[0])  //输出按键被按下时的标志信号
);

key_filter u2_key_filter(
  .  clk     (clk),//系统时钟
  .  rst_n   (rst_n),//复位信号
  .  key     (key[1]),//输入的按键信号

  .  key_flag(key_flag[1])  //输出按键被按下时的标志信号
);


always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        cnt_500ms <= 25'd0;
    end
    else if(add_cnt_500ms)begin
        if(end_cnt_500ms)begin
            cnt_500ms <= 25'd0;
        end
        else begin
            cnt_500ms <= cnt_500ms + 1'd1;
        end
    end
    else begin
        cnt_500ms <= cnt_500ms;
    end
end

assign add_cnt_500ms = 1'b1;
assign end_cnt_500ms = add_cnt_500ms && cnt_500ms == CNT_MAX ;

//流水灯模块的实现
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        led_w <= 4'b0001;
    end
    else if(end_cnt_500ms)begin
       led_w <= {led_w[2:0],led_w[3]}; 
    end
    else begin
        led_w <= led_w;
    end
end 

//跑马灯模块的实现
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        led_r <= 8'b1111_0000;
    end
    else if(end_cnt_500ms)begin
       led_r <= {led_r[0],led_r[7:1]}; 
    end
    else begin
        led_r <= led_r;
    end
end 

//flag信号,对按键输出标志信号进行寄存,持续保持高电平
always @(posedge clk or negedge rst_n)begin
    if(!rst_n)begin
        flag <= 2'b00;
    end
    else if(key_flag)begin
        flag <= key_flag;
    end
    else begin
        flag <= flag;
    end
end

//组合逻辑选择不同的类型
always @(*)begin
   case(flag)
        2'b01: led = led_w;//key1按下流水灯模式
        2'b10: led = led_r[3:0];//key2按下跑马灯模式
        default:led = 4'b0000;
   endcase
end


endmodule

3、key_filter_tb.v

//按键消抖模块仿真
//模拟按键抖动
`timescale 1ns/1ns 
module key_filter_tb();

reg         clk     ;
reg         rst_n   ;
reg         key     ;
wire        key_flag;

initial begin
    clk   = 1'b1;
    rst_n = 1'b0;
    key   = 1'b1;
    #15
    rst_n = 1'b1;
    #50000
//模拟按键前抖动
    key   = 1'b0;
    #50
    key   = 1'b1;
    #45
    key   = 1'b0;
    #35
    key   = 1'b1;
    #5 
    key   = 1'b0;
    #20000
//模拟按键后抖动
    key   = 1'b1;
    #15
    key   = 1'b0;
    #15
    key   = 1'b1;
    #5
    key   = 1'b0;
    #25
    key   = 1'b1;
    #50000
    $stop;
end

always #10 clk = ~clk;

defparam  u_key_filter.CNT_MAX_20MS = 20'd99; 

key_filter  u_key_filter(
    .     clk    (clk),
    .     rst_n  (rst_n),
    .     key    (key),

    .     key_flag(key_flag)
);

endmodule

  • 11
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值