热度 10
2016-3-4 15:52
758 次阅读|
0 个评论
图1 KEY原理图 图1 KEY原理图 图2 LED原理图 图2 LED原理图 key_led.v 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 module key_led( input CLOCK_50, input Q_KEY, input KEY, output reg LED ); //++++++++++++++++++++++++++++++++++++++ // 获取键值 开始 //++++++++++++++++++++++++++++++++++++++ wire key_val; // 键值 key_debounce u0( .i_clk (CLOCK_50), .i_rst_n (Q_KEY), .i_key (KEY), .o_key_val (key_val) // 按下为0,松开为1 ); //-------------------------------------- // 获取键值 结束 //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ // 按下键后开关LED 开始 //++++++++++++++++++++++++++++++++++++++ always @ (posedge CLOCK_50, negedge Q_KEY) if (!Q_KEY) LED = 4'hF; // 0灭1亮 else case (1'b0) key_val : LED = ~LED ; key_val : LED = ~LED ; key_val : LED = ~LED ; key_val : LED = ~LED ; default : LED = LED ; // 缺省亮灭情况不变 endcase //-------------------------------------- // 按下键后开关LED 结束 //-------------------------------------- endmodule key_debounce.v 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 module key_debounce( input i_clk, input i_rst_n, input i_key, // 按下为0,松开为1 output reg o_key_val // 键值 ); //++++++++++++++++++++++++++++++++++++++ reg key_samp1, key_samp1_locked; // 将i_key采集至key_samp1 always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp1 = 4'hF; else key_samp1 = i_key; // 将key_samp1锁存至key_samp1_locked always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp1_locked = 4'hF; else key_samp1_locked = key_samp1; //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ wire key_changed1; // 当key_samp1由1变为0时 // key_changed1由0变为1,只维持一个时钟周期 assign key_changed1 = key_samp1_locked (~key_samp1); //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ reg cnt; // 一旦有按键按下,cnt立即被清零 always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) cnt = 20'h0; else if(key_changed1) cnt = 20'h0; else cnt = cnt + 1'b1; //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ reg key_samp2, key_samp2_locked; // 只有当按键不变化(不抖动),且维持20ms以上时 // 才将i_key采集至key_samp2 always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp2 = 4'hF; else if(cnt == 20'hF_FFFF) // 0xFFFFF/50M = 20.9715ms key_samp2 = i_key; // 将key_samp2锁存至key_samp2_locked always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) key_samp2_locked = 4'hF; else key_samp2_locked = key_samp2; //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ wire key_changed2; // 当key_samp2由1变为0时 // key_changed2由0变为1,只维持一个时钟周期 assign key_changed2 = key_samp2_locked (~key_samp2); //-------------------------------------- //++++++++++++++++++++++++++++++++++++++ // 每次按键稳定后,输出键值 // 按下为0,松开为1 always @ (posedge i_clk, negedge i_rst_n) if(!i_rst_n) o_key_val = 4'hF; else o_key_val = ~key_changed2; //-------------------------------------- endmodule HDL思路 3cdd09ff-062d-4e68-85e7-697eab9c0e22 图3 按键抖动 如图3所示,按键在按下和释放的时候都会有一定的抖动。本代码仅消除了按下抖动,没有对释放抖动进行处理。 代码中使用了一个计数器来实现延时。只要这个计数器正常计数到20'hF_FFFF,我们就采样一个值key_samp2,并将其锁存进key_samp2_locked。一旦有下降沿来临,就输出按键改变值key_changed2。同时,该模块也在使用高频时钟i_clk不停地采集键值到key_samp1,并锁存进key_samp1_locked;一旦下降沿存在,输出按键变化值key_changed1,这样计数器就会被清零。假设没有key_changed1,我们将0xFFFFF/50M = 20.9715ms取样一次键值,并检测其下降沿,输出键值。但是由于抖动的原因,采用高频时钟i_clk采样的key_samp1就有可能导致key_changed1为一,进而使得低频的20.9715ms采样延时执行。也就是说,只有按键处于稳定的状态时,key_samp2才被正常采样,才有可能检测到key_changed2。 参考资料 1. 特权.基于verilog按键消抖设计 http://group.ednchina.com/1375/21132.aspx