ZYNQ microphase 7020 开发板学习(2.键控灯)按键消抖
代码基于microphase学习改进:
module key_led(
input wire clk,
input wire rst_n,
input wire [2:0] key,
output wire [2:0] led
);
reg [5:0] key_dd;
always @(posedge clk or negedge rst_n)begin
if(~rst_n)begin
key_dd <='d0;
end
else begin
key_dd <= {key_dd[2:0],key};
end
end
assign led = key_dd[5:3];
endmodule
机械按键本身在处理机械按键输入时。机械按键在被按下或松开时,通常不会立即达到稳定状态,而是会有一个短暂的抖动期。这些抖动会导致在按键的状态变化过程中产生多次高低电平的切换,从而造成误判。以上是微相提供的学习资料。
对于该示例,由于使用按键操作控制LED灯的亮灭,对于高频时钟,LED的亮灭体现对人眼而言识别并不需要消除这种抖动。想简单的实现键控灯只用将按键接入LED灯即可,所谓的按键抖动最终在LED亮灭上的体现在人眼上是分辨不出来的。因此以下代码也可满足实验需求:
module key_led(
input wire clk,
input wire rst_n,
input wire [2:0] key,
output reg [2:0] led
);
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
// reset
led <= 3'b000;
end
else begin
led <= key;
end
end
endmodule
对于机械按键的消除抖动的方式主要了解了以下3种,和大家分享:
1.基于寄存器的消除抖动
通过将按键信号保存到一个寄存器阵列中,并在时钟上升沿或复位信号下降沿进行更新。这样可以在一定程度上过滤掉抖动信号,但是否足够有效取决于具体的应用场景和抖动的时长。(微相提供的学习资料就是基于这一方式实现的,使用一个时钟周期的寄存器来延迟,将时钟信号取值保存之后一个周期判断)。
2.延时滤波法
考虑按键输入信号的长度在判断满足一定时长后,判定该按键有效。
module key_flag(
input wire clk,
input wire rst,
input wire key,
output reg key_flag
);
parameter cnt_max = 5;
reg [2:0] cnt;
reg key_sync;
// 同步按键信号以避免亚稳态
always @(posedge clk or negedge rst) begin
if (~rst) begin
key_sync <= 1'b1;
end else begin
key_sync <= key;
end
end
always @(posedge clk or negedge rst) begin
if (~rst) begin
// reset
cnt <= 3'd0;
end
else if (~key_sync) begin
if (cnt == cnt_max) begin
cnt <= 3'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end else begin
cnt <= 3'd0; // 如果按键未按下,计数器重置
end
end
always @(posedge clk or negedge rst) begin
if (~rst) begin
key_flag <= 1'b1;
end else begin
key_flag <= (cnt == cnt_max) ? 1'b0 : 1'b1;
end
end
endmodule
在自己写代码的过程中,出现了LED特别淡的情况,发现写的按键模块输出FLAG信号标志有效只占按键时长的1/5。
而且按键抖动不止发生在按键触发,按键移除同样存在,于是想到和现状异或然后计时,不仅可以满足按键标志位和按键时长基本一致,而且可以满足按键移除和按键触发同时存在。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/07/08 18:46:11
// Design Name:
// Module Name: key_led
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
例化每一位按键和标志位///
module key_led(
input wire clk,
input wire rst_n,
input wire [2:0] key,
output wire [2:0] led
);
genvar i;
generate
for(i=0;i< 3;i=i+1)begin:key_flag_gen
key_flag key_top(
.clk(clk),
.rst(rst_n),
.key(key[i]),
.key_flag(led[i])
);
end
endgenerate
endmodule
//-------------------------------------按键消除抖动模块------------------------------------
module key_flag(
input wire clk,
input wire rst,//低电平有效
input wire key,
output reg key_flag
);
parameter cnt_max = 5;
reg [2:0] cnt;
reg key_sync;
// 同步按键信号以避免亚稳态
always @(posedge clk or negedge rst) begin
if (~rst) begin
key_sync <= 1'b1;
end else begin
key_sync <= key;
end
end
always @(posedge clk or negedge rst) begin
if (~rst) begin
// reset
cnt <= 3'd5;
end
else if (key_sync != key_flag) begin
if (cnt == cnt_max) begin
cnt <= 3'd0;
end
else begin
cnt <= cnt + 1'b1;
end
end else begin
cnt <= 3'd0; // 如果按键未按下,计数器重置
end
end
always @(posedge clk or negedge rst) begin
if (~rst) begin
key_flag <= 1'b0;
end
else if(cnt == cnt_max)begin
key_flag <= key_sync ;
end
else begin
key_flag <= key_flag;
end
end
endmodule
可以看出,在和之前状态不同连续发生5次后,按键标识发生改变。实线按键消抖。
3.多采样法
考虑在一定的时间长度内,判断该按键信号的电平始终有效。使用将按键信号移位保存在多位的寄存器中,当多位寄存器的数值一样时,将该信号给到按键标识位。(个人感觉这个最简单)
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/07/08 20:14:11
// Design Name:
// Module Name: key_led
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module key_led(
input wire clk,
input wire rst_n,
input wire [2:0] key,
output wire [2:0] led
);
genvar i;
generate
for(i=0;i< 3;i=i+1)begin:key_flag_gen
key_flag key_top(
.clk(clk),
.rst_n(rst_n),
.key(key[i]),
.key_flag(led[i])
);
end
endgenerate
endmodule
//-------------------这个简单又好理解------------------------
module key_flag(
input wire clk,
input wire rst_n, // 低电平有效
input wire key,
output reg key_flag
);
reg [4:0] key_sync;
// 同步按键信号以避免亚稳态同时寄存按键每次的采样值
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
key_sync <= 5'b0;
end else begin
key_sync <= {key_sync[3:0], key};
end
end
// 判定每次的采样的数值均相同
always @(posedge clk or negedge rst_n) begin
if (~rst_n) begin
key_flag <= 1'b0;
end else if ((|key_sync == 0) || (&key_sync == 1)) begin
key_flag <= |key_sync;
end
end
endmodule
满足了按键消抖的功能
第三种多采样法确实电路简单,容易理解,代码短。无敌