FPGA入门五
程序员文章站
2021-12-30 07:19:03
按键消抖实验按键消抖四种状态:1.高脉冲稳定状态 2.按下消抖状态 3.低电平稳定状态 4.键起消抖状态边沿检测电路:两个寄存器...
按键消抖实验
按键消抖四种状态:
1.高脉冲稳定状态 2.按下消抖状态 3.低电平稳定状态 4.键起消抖状态
边沿检测电路:两个寄存器
编写主逻辑文件
module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
input Clk;
input Rst_n;
input Key_in;
output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
output reg Key_data;
reg [3:0]state;
reg [19:0]count; //寄存器计数
reg count_start;//时能计时
reg count_full;//计满脉冲信号
reg reg0,reg1;
wire rise,fall;
localparam
High_pulse = 4'b0001, //高电平稳定状态
Low_eliminate = 4'b0010, //键下降沿稳定状态
Low_pulse = 4'b0100, //低电平稳定状态
High_eliminate = 4'b1000; //键上升沿稳定状态
always @(posedge Clk or negedge Rst_n) //脉冲边沿检测 两个寄存器
if(!Rst_n) begin
reg0 <= 1'b0;
reg1 <=1'b0;
end
else begin
reg0 <= Key_in;
reg1 <= reg0;
end
assign fall = !reg0 & reg1; //检测到下降沿
assign rise = reg0 & !reg1; //检测到上升沿
always @(posedge Clk or negedge Rst_n) //20ms计数器
if(!Rst_n)
count <= 20'b0;
else if(count_start)
count <= count + 20'b1;
else count <= 20'b0;
always @(posedge Clk or negedge Rst_n) //20ms计数器计数满标志脉冲信号
if(!Rst_n)
count_full <= 1'b0;
else if(count == 99_9999)
count_full <= 1'b1;
else count_full <= 1'b0;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n) begin
Key_data <= 1'b1;
state <= High_pulse;
Key_flag <= 1'b0;
count_start <= 1'b0;
end
else case(state)
High_pulse : begin Key_flag <= 0;
Key_data <= 1'b1;
if(fall) begin
state <= Low_eliminate;
count_start <= 1'b1; //使能开始20ms计数
end
else state <= High_pulse;
end
Low_eliminate :
if(count_full) begin
state <= Low_pulse;
Key_flag <= 1'b1;
Key_data <= 1'b0;
count_start <= 1'b0; //关闭使能20ms计数
end
else begin
if(rise) begin
state <= High_pulse;
count_start <= 1'b0; //关闭使能20ms计数
end
else
state <= Low_eliminate;
end
Low_pulse: begin Key_flag <= 1'b0;
if(rise) begin
state <= High_eliminate;
count_start <= 1'b1; //开始计时
end
else state <= Low_pulse;
end
High_eliminate :
if(count_full) begin
Key_data <= 1'b1;
Key_flag <= 1'b1;
state <= High_pulse;
end
else begin
if(fall) begin
state <= Low_pulse;
count_start <= 1'b0;
end else
state <= High_eliminate;
end
default : begin
state <= High_pulse; //默认状态
count_start <= 1'b0;
Key_data <= 1'b1;
Key_flag <= 1'b0;
end
endcase
endmodule
主逻辑文件仿真
`timescale 1ns/1ns
`define period_clock 20
module Key_shake_tb;
reg Clk;
reg Rst_n;
reg Key_in; //高电平为按键默认状态
wire Key_flag;
wire Key_data;
reg [15:0]random_time; //寄存器存储不超过20ms的随机时间
Key_shake Key_shake0(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in),
.Key_flag(Key_flag),
.Key_data(Key_data)
);
initial Clk = 1;
always #(`period_clock/2) Clk = ~Clk;
initial begin
Rst_n = 0;
Key_in = 1;
#(`period_clock*10);
Rst_n = 1;
#(`period_clock*10);
press_key;
Key_in = 0;
#(`period_clock*1000010);
press_key;
Key_in = 1;
#(`period_clock*1010010);
$stop;
end
task press_key; //任务
begin
repeat(50) begin
random_time = {$random}%65535; //无{}表示取值范围-65535~65535
#random_time Key_in = ~ Key_in;
end
end
endtask
endmodule
仿真结果
在消抖稳定后20m产生Key_flag信号和Key_state信号。
仿真模型概念
Key_shake_tb0模块应为Key_shake0模块,写错。
Key_shake_tb仿真模型模块
`timescale 1ns/1ns
`define period_clock 20
module Key_shake_tb;
reg Clk;
reg Rst_n;
wire Key_in; //高电平为按键默认状态 两个寄存器连接wire型
wire Key_flag;
wire Key_data;
reg [15:0]random_time; //寄存器存储不超过20ms的随机时间
Key_shake Key_shake0(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in),
.Key_flag(Key_flag),
.Key_data(Key_data)
);
Key_model Key_model(
.Key(Key_in)
);
initial Clk = 1;
always #(`period_clock/2) Clk = ~Clk;
initial begin
Rst_n = 0;
#(`period_clock*10);
Rst_n = 1;
#(`period_clock*10+1);
end
endmodule
Key_model模块
模拟产生按键抖动信号
`timescale 1ns/1ns
`define period_clock 20
module Key_model(Key); //按键模型
output reg Key;
reg [15:0]random_time; //寄存器存储不超过20ms的随机时间
initial begin
Key = 1'b1;
press_key;
#(`period_clock*1000);
press_key;
#(`period_clock*1010);
$stop;
end
task press_key; //任务
begin
repeat(50) begin
random_time = {$random}%65535; //无{}表示取值范围-65535~65535
#random_time Key = ~ Key;
end
Key = 0;
#(`period_clock*1000010);
repeat(50) begin
random_time = {$random}%65535; //无{}表示取值范围-65535~65535
#random_time Key = ~ Key;
end
Key = 1;
#(`period_clock*2000010);
end
endtask
endmodule
仿真结果
异步信号处理
在输入信号之前添加两级寄存器。减少D触发器震荡。按键事件对响应时间要求不高,可以使用多级触发器。
module Key_shake(Clk,Rst_n,Key_in,Key_flag,Key_data);
input Clk;
input Rst_n;
input Key_in;
output reg Key_flag; //当按键按下20ms消除抖动成功后产生一个脉冲flag信号,检测到产生flag信号后产生按键按下低电平。
output reg Key_data;
reg [3:0]state;
reg [19:0]count; //寄存器计数
reg count_start;//时能计时
reg count_full;//计满脉冲信号
reg reg0,reg1;
reg key_in_now0,key_in_now1;
wire rise,fall;
localparam
High_pulse = 4'b0001, //高电平稳定状态
Low_eliminate = 4'b0010, //键下降沿稳定状态
Low_pulse = 4'b0100, //低电平稳定状态
High_eliminate = 4'b1000; //键上升沿稳定状态
always @(posedge Clk or negedge Rst_n) //对输入按键信号做同步处理,消除异步信号亚稳态的影响。
if(!Rst_n) begin
key_in_now0 <= 1'b0;
key_in_now1 <=1'b0;
end
else begin
key_in_now0 <= Key_in;
key_in_now1 <= key_in_now0;
end
always @(posedge Clk or negedge Rst_n) //脉冲边沿检测 两个寄存器
if(!Rst_n) begin
reg0 <= 1'b0;
reg1 <=1'b0;
end
else begin
reg0 <= key_in_now1;
reg1 <= reg0;
end
assign fall = !reg0 & reg1; //检测到下降沿
assign rise = reg0 & !reg1; //检测到上升沿
always @(posedge Clk or negedge Rst_n) //20ms计数器
if(!Rst_n)
count <= 20'b0;
else if(count_start)
count <= count + 20'b1;
else count <= 20'b0;
always @(posedge Clk or negedge Rst_n) //20ms计数器计数满标志脉冲信号
if(!Rst_n)
count_full <= 1'b0;
else if(count == 99_9999)
count_full <= 1'b1;
else count_full <= 1'b0;
状态机(同上)
endmodule
顶层文件Key_led_top
module Key_led_top(Clk,Rst_n,Key_in0,Key_in1,led);
input Clk;
input Rst_n;
input Key_in0,Key_in1;
output [3:0]led;
wire Key_flag0,Key_flag1;
wire Key_data0,Key_data1;
Key_shake Key_shake0(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in0),
.Key_flag(Key_flag0),
.Key_data(Key_data0)
);
Key_shake Key_shake1(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_in(Key_in1),
.Key_flag(Key_flag1),
.Key_data(Key_data1)
);
Led_control Led_control0(
.Clk(Clk),
.Rst_n(Rst_n),
.Key_flag0(Key_flag0),
.Key_state0(Key_data0),
.Key_flag1(Key_flag1),
.Key_state1(Key_data1),
.Led(led)
);
endmodule
LED驱动文件Led_control
module Led_control(Clk,Rst_n,Key_flag0,Key_state0,Key_flag1,Key_state1,Led);
input Clk,Rst_n;
input Key_flag0,Key_flag1;
input Key_state0,Key_state1;
output [3:0]Led;
reg [3:0]Led_un;
always @(posedge Clk or negedge Rst_n)
if(!Rst_n)
Led_un <= 4'b0000;
else if(Key_flag0 && Key_state0)
Led_un <= Led_un + 4'b1;
else if(Key_flag1 && Key_state1)
Led_un <= Led_un - 4'b1;
else
Led_un <= Led_un;
assign Led = ~Led_un;
endmodule
顶层仿真文件
`timescale 1ns/1ns
`define period_clock 20
module Led_top_tb;
reg Clock;
reg Rst_n;
wire Key_in0;
wire Key_in1;
reg press0,press1;
wire [3:0]led;
Key_led_top Key_led_top0(
.Clk(Clock),
.Rst_n(Rst_n),
.Key_in0(Key_in0),
.Key_in1(Key_in1),
.led(led)
);
Key_model Key_model0(
.Press(press0),
.Key(Key_in0)
);
Key_model Key_model1(
.Press(press1),
.Key(Key_in1)
);
initial Clock = 1;
always #(`period_clock/2) Clock = ~Clock;
initial begin
Rst_n = 0;
press0 = 0;
press1 = 0;
#(`period_clock*4);
Rst_n = 1;
#(`period_clock*10+1);
press0 = 1; //自加
#(`period_clock*2);
press0 = 0;
#(`period_clock*4_000_000);
press0 = 1;
#(`period_clock*2);
press0 = 0;
#(`period_clock*4_000_000);
press1 = 1; //自加
#(`period_clock*2);
press1 = 0;
#(`period_clock*4_000_000);
press1 = 1;
#(`period_clock*2);
press1 = 0;
#(`period_clock*4_000_000);
$stop;
end
endmodule
仿真结果
Key_flag0信号使LED自增,Key_flag1信号使LED自减。led取反。
本文地址:https://blog.csdn.net/qq_43485409/article/details/108103829