08按键消抖之驱动流水灯
一设计功能
(一)实验现象: 在赛灵思的开发板上,按键每按下一次,四个LED灯向左移位一次实现流水灯效果。
(二)知识点:按键消抖,移位。按键按下与释放,首先得把按键的完整过程分为下降沿和上升沿,下降沿与上升沿都得维持10ms,才能看作真正的按下与释放。否则为抖动。移位分成左移和右移,实现方式有两种,位拼接符和移位运算符。我个人喜欢移位拼接符实现,相较于运算符少了最高位溢出全为零的情况。
(三)系统设计框图
![]() |
二设计输入
(一)顶层模块实现按键消抖和LED两个模块的连接和最终的端口定义
//top modue to link all module by inst module top( input wire clk, input wire Rst, input wire key, output wire [3:0] led );
wire key_flag; //例化按键消抖 key_filter key_filterm0 ( .clk(clk), .Rst(Rst), .Key1(key), .Pin_out(key_flag) ); //例化流水灯 Water_led inst_Water_led ( .clk(clk), .Rst(Rst), .shift_en(key_flag), .led(led));
endmodule
|
(二)按键消抖模块
按键消抖分为,边沿检测模块,按键按下与释放及其消抖的状态机模块。边沿检测,两级寄存器实现了按键的下降沿和上升沿检测。按键的状态机模块:一开始按键处于初始状态,当检测到下降沿到来时,进入按下消抖状态:若按下时间超过10ms当作真正的按下,并产生一个按下的标志信号。若没计满10ms就来了上升沿,则为抖动滤除。若没有前面两种情况则计数直到10ms产生按下标志信号进入下一个状态。按键低电平状态:就等待按键的上升沿到来,进入按键释放状态。按键释放状态:与按下消抖状态相似,只是滤除抖动后,不会产生标志信号,且进入初始状态。
module key_filter ( clk, Rst, Key1, Pin_out ); input clk; input Rst; input Key1; // output Pin_out; //the output of key,when the Key1==1(means press) //the output of key,when the Key1==0(means release)
//be careful the binary(二进刍 localparam IDEL = 4'b0001, //the idel state FILTER0 = 4'b0010, //first filter(key from 1 to 0) DOWN = 4'b0100, //when the Key1==0 FILTER1 = 4'b1000; //second filter(key from 0 to 1)
reg H2L_F1; //first reg(store the value of key from 1 to 0,press) reg H2L_F2; reg L2H_F1; //first reg(store the value of key from 0 to 1,press) reg L2H_F2; //the clasic edge detection(边沿检浍 always@(posedge clk or negedge Rst) if(!Rst) begin H2L_F1<=1'b1; H2L_F2<=1'b1; L2H_F1<=1'b0; L2H_F2<=1'b0; end else begin H2L_F1<=Key1; H2L_F2<=H2L_F1; L2H_F1<=Key1; L2H_F2<=L2H_F1; end
wire nedge,podge;
assign nedge = !H2L_F1 & H2L_F2;//the negedge of key assign podge = L2H_F1 & !L2H_F2;//the posedge of key
parameter T10MS = 19'd500_000-1; reg [18:0]delay_cnt; reg [3:0]state; reg Pin_out;
always@(posedge clk or negedge Rst) if(!Rst)begin state<=IDEL; delay_cnt<=19'd0; Pin_out<=1'b0; end else begin case(state) IDEL:begin delay_cnt<=19'd0; if(!H2L_F1 & H2L_F2) state<=FILTER0; else state<=IDEL; end FILTER0:begin
if((delay_cnt==T10MS) )begin state<=DOWN; Pin_out<=1'b1; delay_cnt<=19'd0; end else if(podge) state<=IDEL; else delay_cnt<=delay_cnt+1'b1;
end DOWN:begin
Pin_out<=1'b0; if(L2H_F1 & !L2H_F2) state<=FILTER1; else state<=DOWN; end FILTER1:begin
if((delay_cnt==T10MS) )begin state<=IDEL; Pin_out<=1'b0; delay_cnt<=delay_cnt; end else if(nedge) state<=DOWN; else delay_cnt<=delay_cnt+1'b1;
end
default:state<=IDEL; endcase
end
endmodule |
(三)流水灯
按键按下产生一个标志信号,作为LED灯的输入,驱动led向左移位一次。不断按下就会实现流水灯效果。
|
三仿真
仿真设计,刚开始产生几次机械抖动,然后再模仿真正的按下一次,看抖动是否被滤除,看看LED灯是否向左移位一次。
`timescale 1ns/1ns `define clk_period 20
module top_tb(); reg clk; reg Rst; reg key; wire [3:0]led; top inst_top ( .clk(clk), .Rst(Rst), .key(key), .led(led) ); initial clk = 1; always#(`clk_period/2) clk =~clk; initial begin Rst = 0; key = 0; #(`clk_period*300); Rst = 1;key = 1; #({$random}%65536) key =0; #({$random}%65536) key =1; #({$random}%65536) key =0; #({$random}%65536) key =1; #({$random}%65536) key =0; #({$random}%65536) key =1; #(`clk_period*600000);key =0; #(`clk_period*600000);key =1; #(`clk_period*500001);key =0; #(`clk_period*30);Rst = 0;#(`clk_period*300); $stop; end endmodule
|
通过波形图,机械抖动都没有产生按下的标志信号,且LED灯没有左移。在按键真正按下时,产生了一个周期的按下标志信号,并且驱动LED左移一次。
四总结
在ISE14.7编译时曾出现了一次警告如下:
delay_cnt_19> (without init value) has a constant value of 0 in block <top>. This FF/Latch will be trimmed during the optimization process. 报错为:在按键消抖模块的寄存器变量delay_cnt的第19位一直为零,综合时会把它优化了,就没了。 解决方法:直接把原先的delay_cnt 20位的位宽改为19位就欧了。 |