module key_debounce ( input wire clk, //系统时钟 50MHz input wire rst_n, //复位信号 input wire key, //按键输入信号 output reg key_done //消抖之后的按键信号 ); reg key_r0; //同步信号(滤波作用,滤除小于一个周期的抖动) reg key_r1; //打拍 reg flag; //标志位 wire nedge; //下降沿检测(检测到下降沿代表开始抖动) //计时器定义 reg [19:0] cnt; wire add_cnt; //计时器开启 wire end_cnt; //计时记满 parameter MAX_CNT=23'd200000; //20ms延时 //同步 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin key_r0<=1'b1; end else key_r0<=key; end //打拍 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin key_r1<=1'b1; end else key_r1<=key_r0; end assign nedge = ~key_r0 & key_r1; //检测到下降沿拉高 //标志位 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) begin flag<=1'b0; end end //延时模块 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt<=20'b0; end else if (add_cnt) begin if (end_cnt) begin cnt<=20'b0; end else cnt<=cnt+1; end end assign add_cnt=flag; //计时器开启 assign end_cnt=add_cnt&&cnt==MAX_CNT-1; //计时器关闭 //key_done输出 always @(posedge clk or negedge rst_n) begin if (!rst_n) begin key_done<=1'b0; end else if (end_cnt) begin //延时满20ms采样 key_done<=~key_r0; end else key_done<=1'b0; end endmodule //key_debounce
时间: 2024-02-15 07:27:30 浏览: 184
这是一个Verilog代码实现的按键消抖模块,主要功能是对输入的按键信号进行消抖处理,输出消抖之后的按键信号。下面是对该代码的一些解释和说明:
1. 该模块使用时钟信号clk和复位信号rst_n进行同步,其中rst_n为低电平有效,用于初始化寄存器。
2. key_r0寄存器用于滤波作用,滤除小于一个周期的抖动。key_r1寄存器用于打拍,即记录上一次按键的状态。
3. flag标志位用于表示是否检测到按键的下降沿,如果检测到,则打开标志位,表示开始进行计时。
4. 计时器模块用于延时20ms,即消抖时间,达到一定的时间后,才会输出消抖之后的按键信号。
5. 最后的key_done输出信号表示消抖之后的按键信号,如果检测到按键的下降沿,则开始进行消抖,否则输出低电平。
注:该代码需要在实际硬件环境中进行测试和验证,具体的电路实现和参数配置需要根据实际情况进行调整。
相关问题
详细解释该段代码://说明:当三个独立按键的某一个被按下后,相应的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; 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 = key_rst_r & ( ~key_rst); //--------------------------------------------------------------------------- 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 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由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
### 代码解析
#### 功能概述
该模块实现了通过三个独立按键 (`sw1_n`, `sw2_n`, `sw3_n`) 控制对应的 LED 灯 (`led_d1`, `led_d2`, `led_d3`) 的亮灭功能。具体来说,当任意一个按键被按下(输入电平从高变低),其对应 LED 将切换状态(如果灯是熄灭的则点亮;如果是点亮的状态,则熄灭)。此外,电路设计中还包括了按键消抖处理。
---
#### 主要组成部分及工作原理:
##### **1. 按键输入捕获**
```verilog
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;
always @ (posedge clk or negedge rst_n)
if (!rst_n) key_rst_r <= 3'b111;
else key_rst_r <= key_rst;
wire[2:0] key_an = key_rst_r & (~key_rst);
```
- `key_rst` 和 `key_rst_r` 分别用于存储当前时刻和上一时刻的按键状态。
- 当某一按键发生下降沿时(即按钮从释放变为按下的瞬间),`key_an` 对应位会置为高电平,并持续一个时钟周期。
**作用**: 这部分完成对按键边沿触发的检测,避免误触导致多次操作。
---
##### **2. 去抖动计数器**
```verilog
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
low_sw <= {sw3_n, sw2_n, sw1_n};
```
- 设立了一个宽为20比特的计数器 `cnt` 来记录时间间隔。
- 只有在连续大约20毫秒内没有额外的按键中断时,才认为按键已经稳定下来并将此时的有效信号存入变量 `low_sw` 中。
**目的**: 实现去抖动机制,滤除因机械开关接触不良造成的短促跳变干扰。
---
##### **3. 输出逻辑更新**
```verilog
reg[2:0] low_sw_r;
always @ (posedge clk or negedge rst_n)
if (!rst_n) low_sw_r <= 3'b111;
else low_sw_r <= low_sw;
wire[2:0] led_ctrl = low_sw_r & (~low_sw);
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
if (led_ctrl[0]) d1 <= ~d1; // 切换第一个LED的状态
if (led_ctrl[1]) d2 <= ~d2; // 切换第二个LED的状态
if (led_ctrl[2]) d3 <= ~d3; // 切换第三个LED的状态
end
assign led_d3 = d1 ? 1'b1 : 1'b0;
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;
```
- 使用另一个寄存器组 `low_sw_r` 存储经过去抖后的前一刻按键状态。
- 根据两者之间的差异计算出是否有实际有效的按键事件(`led_ctrl`)。
- 最终依据每次有效按键操作改变相应 LED 引脚输出状态(d1,d2,d3),并通过条件赋值驱动外部硬件设备(LED).
---
### 总结
上述RTL描述了一种基本的手动控制电子装置的行为模式—用户交互式的开/关管理流程。它结合同步采样技术解决了物理器件固有的不稳定因素影响系统正常运行的问题。同时保持简单直观易于理解维护的设计思路贯穿始终。
module key_debounce(key_in,key_out,clk,rst); input clk; input rst; input [7:0] key_in; output [7:0] key_out; reg [19:0] cnt=0; always @ (posedge clk or posedge rst) if (rst) cnt <= 20'd0; else if(cnt==20'hfffff) cnt<=0; else cnt <= cnt + 1'b1; reg [7:0] low_key=8'b1111_1111; always @(posedge clk or posedge rst) if (rst) low_key <= 8'b1111_1111; else if (cnt == 20'hfffff) low_key <= key_in; reg [7:0] low_key_r=8'b1111_1111; always @ ( posedge clk or posedge rst ) if (rst) low_key_r <= 8'b1111_1111; else low_key_r <= low_key; wire[7:0] lift_ctr = low_key_r[7:0] & ( ~low_key[7:0]); assign key_out=lift_ctr; endmodule 这段verilog代码什么意思
这段Verilog代码实现了按键去抖动的功能。以下是对代码的解释:
- 声明了一个模块 `key_debounce`,该模块有四个输入端口和一个输出端口。
- 输入端口包括时钟信号 `clk`,复位信号 `rst`,以及一个8位的按键输入信号 `key_in`。
- 输出端口是一个8位的去抖动后的按键信号 `key_out`。
- 定义了一个19位的寄存器 `cnt`,用于计数去抖动时间。
- 使用 `always` 块来处理时钟上升沿和复位信号。
- 如果复位信号 `rst` 为高电平,则将计数器 `cnt` 归零。
- 如果计数器 `cnt` 达到最大值(20'hfffff),则将其重置为零。
- 否则,计数器 `cnt` 自增1。
- 定义了一个8位的寄存器 `low_key`,用于存储按键的低电平状态。
- 使用另一个 `always` 块来处理时钟上升沿和复位信号。
- 如果复位信号 `rst` 为高电平,则将低电平按键寄存器 `low_key` 设置为全高电平状态。
- 如果计数器 `cnt` 达到最大值(20'hfffff),则将低电平按键寄存器 `low_key` 更新为当前的按键输入值 `key_in`。
- 定义了一个8位的寄存器 `low_key_r`,用于存储上一个时钟周期的低电平按键状态。
- 使用另一个 `always` 块来处理时钟上升沿和复位信号。
- 如果复位信号 `rst` 为高电平,则将上一个时钟周期的低电平按键寄存器 `low_key_r` 设置为全高电平状态。
- 否则,将上一个时钟周期的低电平按键寄存器 `low_key_r` 更新为当前的低电平按键寄存器 `low_key`。
- 定义了一个8位的线网 `lift_ctr`,用于存储去抖动后的按键信号。
- 使用 `assign` 语句将 `lift_ctr` 设置为上一个时钟周期的低电平按键状态 `low_key_r` 与当前低电平按键状态 `~low_key` 的按位与运算结果。
- 最后,将 `lift_ctr` 赋值给输出端口 `key_out`。
整体来说,这段代码实现了按键去抖动的功能,通过计数器实现去抖动时间延迟,并通过寄存器记录低电平按键状态,最终输出去抖动后的按键信号。
阅读全文
相关推荐

