精华内容
下载资源
问答
  • verilog按键消抖

    2018-07-06 15:31:49
    verilog按键消抖代码,已完成封装,可以直接调用。支持动态设置上升沿和下降沿触发,可以通过一个bit来设置具体工作方式,仿真和实际测试均可正常使用。
  • verilog 按键消抖

    2018-09-15 17:06:26
    verilog代码晶振波形一般是正弦波或者方波,当输出波形是方波时,一般上升沿比较,且包含了较多的高频信号,这个时候就要保证测试的带宽足够,理论值是带宽是被测信号频率的2倍,实际测试方波时带宽应该是被测信号...
  • verilog按键消抖经典

    2014-08-16 14:41:29
    verilog按键消抖经典程序,采用的边缘检测消抖,边缘检测按键。。
  • Verilog 按键消抖的一些分析和想法

    多人点赞 2019-11-25 18:04:09
    最近在网上看了下 Verilog 按键消抖方面的设计,有一些个人的想法,做一些分析和记录; 电路板上,通常会提供若干按键,每个按键下赋予了不同的含义,按键的含义由具体的场景来定义; 打个比方,一组电路板上的...

    最近在网上看了下 Verilog 按键消抖方面的设计,有一些个人的想法,做一些分析和记录;

    电路板上,通常会提供若干按键,每个按键下赋予了不同的含义,按键的含义由具体的场景来定义;

    打个比方,一组电路板上的按键定义如下所示:

    在这个例子中,可以看到,硬件原理图中提供了 5 个信号:

    KEY_UP

    KEY_DOWN

    KEY_LEFT

    KEY_RIGHT

    KEY_ENTER

    当 S2~S6 没有被按下的时候,I/O 管脚被通过上拉电阻到 Vcc,即逻辑 1;

    当 S2~S6 被按下的时候,这些 I/O 信号直接到 GND,即逻辑 0;

    即,按下按键,读 I/O 状态为 0;否则为 1;

    当然,这是理想情况,但是现实却很骨感,我们把这个简单的按键操作无限放大后,事实的真相是:按键的时候,可能会存在抖动因素,即需要判断是否是真的按下了按键?是否是因为抖动的因素,导致了误报?这就是按键去抖;

    其实按键去抖比较常见,一个比较简易的判断抖动的算法是:如果发现按下了,那么间隔一个很短的时间,再去采集该 I/O 的状态,发现还是按下的状态,那么就认为是真的按键,否则认为是抖动;这里,间隔的这个很短的时间,一般情况可以取 20ms;那么在翻译一下,发现 I/O 状态改变后,隔 20ms,再次采样 I/O 状态,如果发现当前的状态与之前的状态一致,那么说明,I/O 状态改变是不争的事实!

    对应的,如果在单片机上,发现有按键按下,那么可以起一个 20ms 的 Timer,到期后再次检测按键即可;

    那么 FPGA 上,纯硬件逻辑应该如何应对呢?这就是本章需要了解的部分;

    这里分析了几种实现方案:

    方案一

    具体实现上,有一些技巧,参考了一些设计,具体的实现如下所示(这里分了几个部分,每个部分逐步讲述):

    1、定义一个 20ms 的计数器,不断的从 0 ~ 20ms 进行计数

    2、20 ms 到期后,将 key 数据进行采样并缓存到 key 0,并在下一个时钟周期,将 key 0 同步到内部缓存的 key 1 中;

    3、key 0 与 key 1 有一个时钟周期的差别,利用这一点,进行判断,是否 key 值变化了

     

    Part 1

    输入部分,有 4 个按键,分别控制 4 个 LED,按下其中一个,对应的 LED 亮,再次按下,LED 灭

    输入时钟频率 25MHz,输入有复位;

    首先实现的是 20ms 的计数器,这个不多说了,Verilog 逻辑如下:

    module keyscan(
        input clk,
        input n_rst,
        input [3:0] key,
        output [3:0] led
        );
    
        // Input Clock is 25MHz, so in order to get 20ms 
        // The Counter should be 500,000 - 1
        reg [19:0] cnt;
    always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt <= 20'd0;
        else if (cnt == 20'd499999) cnt <= 20'd0;
        else cnt <= cnt + 1'b1;

     

    Part 2

    当计数器到达 20ms 的时候,将按键的 I/O 信息进行采样,存储到内部 key_sample_new[3:0] 寄存器:

        // Sample Key input every 20ms
        reg [3:0] key_sample_new;
    always @(posedge clk or negedge n_rst)
        if(!n_rst) key_sample_new <= 4'b0000;
        else if(cnt == 20'd499999) key_sample_new <= key;

     

    Part 3

    用时钟,同步新/旧的采样数据,并且通过新旧的按键对比出到底是哪个被按下了:

        // Sync the latest one to old one
        reg [3:0] key_sample_last;
    always @(posedge clk or negedge n_rst)
        if(!n_rst) key_sample_last <= 4'b0000;
        else if(cnt == 20'd499999) key_sample_last <= key_sample_new;
    
        // latch one clock cycle and check which key have been press down
        wire [3:0] key_pressed = key_sample_last[3:0] & (~key_sample_new[3:0]);

    我们按照时间顺序来进行流程分析:

    1、首先,clk 的 20ms 的计数器到期了,会将当前的 key 的 I/O 状态进行采样到 key_sample_new[3:0],注意,此刻的 key_sample_last[3:0] 还是上一次的值,并没发生变化,因为此刻的数据刚刚打入 key_sample_new[3:0];

    2、组合逻辑生效 key_pressed 信号会根据当前 20ms 拿到的最新的 I/O 状态 key_sample_new[3:0] 取反和上一次的 key_sample_last[3:0] 进行与操作,这里多说一下:

    wire [3:0] key_pressed = key_sample_last[3:0] & (~key_sample_new[3:0]);

    如果对应的 bit 之前为 1 (也就是 last 中为 1),新采样拿到的数据为 0(也就是 new 中为 0),那么 key_pressed 中对应的 bit 被置为 1,否则,其他任何情况,key_pressed 中对应的 bit 都为 0;换句话来说,只有检测到对应的 bit 由 1 -> 0 的过程,那么就说明被按下了,并将其记录到 key_pressed 中;

    3、下一个 clk 的上升沿到来的时候,key_sample_new[3:0] 被同步到了 key_sample_last[3:0] 中,也就是说,如果有真实的按下按键的话,这两个内部寄存器的值,只有一个时钟周期是不一样的;利用这个时钟周期,将数据存储到了 key_pressed;数据完成了同步后,key_sample_last[3:0] 中就代表了上一次的采样数据了;

     

    Part 4

    既然按下按键的信息被存储到了 key_pressed 中,那么控制 LED 就靠它了:

    reg [3:0] temp_led;
    always @(posedge clk or negedge n_rst) 
        if(!n_rst) temp_led <= 4'b1111;
        else begin
            if ( key_pressed[0] ) temp_led[0] <= ~temp_led[0];
            if ( key_pressed[1] ) temp_led[1] <= ~temp_led[1];
            if ( key_pressed[2] ) temp_led[2] <= ~temp_led[2];
            if ( key_pressed[3] ) temp_led[3] <= ~temp_led[3];
        end
    
        assign led[0] = temp_led[0];
        assign led[1] = temp_led[1];
        assign led[2] = temp_led[2];
        assign led[3] = temp_led[3];
    
    endmodule
    

    总的来说,这种方案就是将 20ms 前获取到的 key 值(key_sample_last[3:0]) 和 20ms 拿到的新的 key 值(key_sample_new[3:0]) 进行按位来对比,判断是否有被置位的情况;

    典型的情况是:

    当然,还可能存在一些比较极限的情况,就是 20ms 到期的时候,保存最新的 key 值的时候,正好处于抖动期间;

    极限的真实按下按键键的情况:

    如果此次是真实的按键,而且正好采样时刻 B 位于抖动期间,如果采集到了 0,那么判断条件生效,将会认为有按键按下,如果采集到的是 1,那么相当于认为当前还是未按下的状态,并且会在 C 点判断出已经按键,显然,这种 Case 没有问题;

     

    如果只有抖动,没有按键的情况,如果此次采集到的 key 是 1,那么相安无事,如果采集到的是 0,那么判断就会失效;所以方案一是存在失效风险的;

     

    方案二

    针对方案一的缺陷,可以设计成为采样 cnt 一直累加,但是一旦检测到 key 有下降沿的时候,立即重置 cnt 到 0,重新计数;

    // Solution 2
        parameter SAMPLE_RATE=6'd10;
    // ------------- Key negedge detect Logic Start -------------
        reg [3:0]key_0;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) key_0 <= 4'b1111;
        else key_0 <= key;
    
        reg [3:0]key_1;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) key_1 <= 4'b1111;
        else key_1 <= key_0;
    
        // Check key negedge
        wire [3:0]key_neg = key_1[3:0] & (~key_0[3:0]);
    // ------------- Key negedge detect Logic End -------------
    
    // ------------- 4 cnt Logic Start -------------
        reg [3:0] cnt_k0;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt_k0 <= 4'd0;
        else if(key_neg[0]) cnt_k0 <= 4'd0;
        else if(cnt_k0 == SAMPLE_RATE) cnt_k0 <= 4'd0;
        else cnt_k0 <= cnt_k0 + 1'b1;
        
        reg [3:0] cnt_k1;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt_k1 <= 4'd0;
        else if(key_neg[1]) cnt_k1 <= 4'd0;
        else if(cnt_k1 == SAMPLE_RATE) cnt_k1 <= 4'd0;
        else cnt_k1 <= cnt_k1 + 1'b1;
    
        reg [3:0] cnt_k2;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt_k2 <= 4'd0;
        else if(key_neg[2]) cnt_k2 <= 4'd0;
        else if(cnt_k2 == SAMPLE_RATE) cnt_k2 <= 4'd0;
        else cnt_k2 <= cnt_k2 + 1'b1;
    
        reg [3:0] cnt_k3;
     always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt_k3 <= 4'd0;
        else if(key_neg[3]) cnt_k3 <= 4'd0;
        else if(cnt_k3 == SAMPLE_RATE) cnt_k3 <= 4'd0;
        else cnt_k3 <= cnt_k3 + 1'b1;
    // ------------- 4 cnt Logic End -------------
    
        // Sample Key input every SAMPLE_RATE
        reg [3:0] key_sample_new;
    always @(posedge clk or negedge n_rst)
        if(!n_rst) key_sample_new <= 4'b1111;
        else begin
            if(cnt_k0 == SAMPLE_RATE) key_sample_new[0] <= key[0];
            if(cnt_k1 == SAMPLE_RATE) key_sample_new[1] <= key[1];
            if(cnt_k2 == SAMPLE_RATE) key_sample_new[2] <= key[2];
            if(cnt_k3 == SAMPLE_RATE) key_sample_new[3] <= key[3];
        end
    
        // Sync the latest one to old one
        reg [3:0] key_sample_last;
    always @(posedge clk or negedge n_rst)
        if(!n_rst) key_sample_last <= 4'b1111;
        else key_sample_last <= key_sample_new;
    
        // latch one clock cycle and check which key have been press down
        wire [3:0] key_pressed = key_sample_last[3:0] & (~key_sample_new[3:0]);
        
    reg [3:0] temp_led;
    always @(posedge clk or negedge n_rst) 
        if(!n_rst) temp_led <= 4'b0000;
        else begin
            if ( key_pressed[0] ) temp_led[0] <= ~temp_led[0];
            if ( key_pressed[1] ) temp_led[1] <= ~temp_led[1];
            if ( key_pressed[2] ) temp_led[2] <= ~temp_led[2];
            if ( key_pressed[3] ) temp_led[3] <= ~temp_led[3];
        end
    
        assign led[0] = temp_led[0];
        assign led[1] = temp_led[1];
        assign led[2] = temp_led[2];
        assign led[3] = temp_led[3];

    首先使用一个边缘检测电路,检测到 key 的下降沿,一旦发现下降沿,那么 cnt 立马置 0,这样就万无一失了吧?

    这种方案的确是比方案一稳一些了,但是在还是有问题:

    当 cnt 计数正好计数满的时候,此刻来下降沿了,此刻判断失效!

     

    方案三

    针对方案一和方案二的问题,那么是否可以这样设计,计数器开始就别工作了,等到下降沿检测好了,在开始进入工作状态,在计数的过程中如果发现有上升沿,那么重新计数,如果一直计数到满(20ms)为止,那么判定,此次是真的按下了!

    上述想法可以使用状态机进行设计,刚刚开始,处于 IDLE 状态,通过边沿检测电路来检测 key 的下降沿;

    当检测到 key 下降沿后,进入 SAMPLING 采样状态,一旦发现采样过程有上升沿,那么返回到 IDLE,如果计数器满足后,判定为真实按下,进入 DOWN 状态,并输出相应的输出!

    这样就能够解决上面两个方案中遇到的问题啦,Verilog 如下(为了仿真,采样时间定在了 10 个 clk,并且只针对了一个 key):

    `timescale 1ns / 1ps
    //
    // Company: 
    // Engineer:       StephenZhou
    // 
    // Create Date:    10:02:46 11/25/2019 
    // Design Name: 
    // Module Name:    keyscan 
    // Project Name: 
    // Target Devices: SP6
    // Tool versions: 
    // Description: 
    //
    // Dependencies: 
    //
    // Revision: 0.01
    // Revision 0.01 - File Created
    // Additional Comments: 
    //
    //
    
    module keyscan(
        input clk,
        input n_rst,
        input key,
        output reg led
        );
    
        // Key posedge and negedge detective logic
        reg key_in_0;
        reg key_in_1;
        always @(posedge clk or negedge n_rst)
        if(!n_rst) begin
            key_in_0 <= 1'b1;
            key_in_1 <= 1'b1;
        end
        else begin
            key_in_0 <= key;
            key_in_1 <= key_in_0;
        end    
    
        wire key_posedge;
        wire key_negedge;
    
        assign key_posedge = key_in_0 & (~key_in_1);
        assign key_negedge = key_in_1 & (~key_in_0);
        
        // State Machine
        reg [2:0] state;
        parameter IDLE =     3'b001;
        parameter SAMPLING = 3'b010;
        parameter DOWN =     3'b100;
    
        reg en_cnt;
        reg led_pressed;
    
        always @(posedge clk or negedge n_rst)
        if(!n_rst) begin
            state  <= IDLE;
            en_cnt <= 1'b0;
            //led    <= 1'b0;
            led_pressed    <= 1'b0;
        end
        else begin
            case(state)
                IDLE : begin
                    if(key_negedge) begin
                        state  <= SAMPLING;
                        en_cnt <= 1'b1;
                        //led    <= 1'b0;
                        led_pressed <= 1'b0;
                    end
                    else begin
                        state  <= IDLE;
                        en_cnt <= 1'b0;
                        //led    <= 1'b0;
                        led_pressed <= 1'b0;
                    end
                end
                SAMPLING : begin
                    if(key_posedge) begin
                        state  <= IDLE;
                        en_cnt <= 1'b0;
                        led_pressed <= 1'b0;
                    end
                    else begin
                        if(cnt_full) begin
                            state  <= DOWN;
                            en_cnt <= 1'b0;
                            led_pressed <= 1'b0;
                        end
                        else begin
                            state  <= SAMPLING;
                            en_cnt <= 1'b1;
                            led_pressed <= 1'b0;
                        end
                    end
                end
                DOWN : begin
                    state  <= IDLE;
                    en_cnt <= 1'b0;
                    led_pressed <= 1'b1;
                end
                default : begin
                    state  <= IDLE;
                    en_cnt <= 1'b0;
                    led_pressed <= 1'b0;                
                end
           endcase
        end
    
        // Counter
        reg [3:0] cnt;
        always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt <= 4'b0000;
        else if (en_cnt) cnt <= cnt + 1'b1;
        else cnt <= 4'b0000;
        
        // Counter Full Logic
        reg cnt_full;
        parameter SAMP_CNT = 4'd10;
        always @(posedge clk or negedge n_rst)
        if(!n_rst) cnt_full <= 1'b0;
        else if(cnt == SAMP_CNT) cnt_full <= 1'b1;
        else cnt_full <= 1'b0;
    
        // Check posedge of led and keep output
        always @(posedge clk or negedge n_rst)
        if(!n_rst) led <= 1'b0;
        else if(led_pressed) led <= ~led;
        //else led <= 1'b0;
    
    endmodule

    代码中均有注释,还是来解释一下:

    1、首先使用 key_in_0 和 key_in_1 进行下降沿和上升沿检测电路(Verilog 边沿检测电路

    2、定义状态机,独热码,三个状态 IDLE、SAMPLING、DOWN,检测下降沿状态为 IDLE,一旦有下降沿,则进入 SAMPLING,在此状态下,如果有上升沿,那么认为是抖动,返回 IDLE,继续检测下降沿,同时计数器停止计时

    3、在 SAMPLING 状态下计时器到期,那么认为,稳定时间到,则认为有按键按下,并走到 DOWN 状态;

    4、DOWN 状态,认为已经检测 OK,那么 led_pressed 赋值为高,led 亮

    testbench 为:

    `timescale 1ns / 1ps
    
    
    // Company: 
    // Engineer:    StephenZhou
    //
    // Create Date:   13:44:47 11/22/2019
    // Design Name:   keyscan
    // Module Name:   D:/Xlinx_ISE_Projects/keyscan/tb/key_scan_tb.v
    // Project Name:  test
    // Target Device:  
    // Tool versions:  
    // Description: 
    //
    // Verilog Test Fixture created by ISE for module: keyscan
    //
    // Dependencies:
    // 
    // Revision:
    // Revision 0.01 - File Created
    // Additional Comments:
    // 
    
    
    module key_scan_tb;
    
    	// Inputs
    	reg clk;
    	reg n_rst;
    	reg key;
    
    	// Outputs
    	wire led;
    
    	// Instantiate the Unit Under Test (UUT)
    	keyscan uut (
    		.clk(clk), 
    		.n_rst(n_rst), 
    		.key(key), 
    		.led(led)
    	);
    
        // Clock Generator freq @20
        always #5 clk = ~clk;
    
    	initial begin
    		// Initialize Inputs
    		clk = 0;
    		n_rst = 0;
    		key = 1;
    
    		// Wait 100 ns for global reset to finish
    		#100;
            
    		// Add stimulus here
            // Release Reset signal
            n_rst = 1;
            
            // Wait 100
            #100;
            
            // Key Input Signal Jitter
            #20 key = 1'b0;
            #15 key = 1'b1;
            #20 key = 1'b0;
            #5  key = 1'b1;
            #15 key = 1'b0;
            #15 key = 1'b1;
            #10 key = 1'b0;
            #5  key = 1'b1;
            
            #400;
            // Key Input Signal Jitter
            #18 key = 1'b0;
            //#30 key[0] = 1'b0;
            #15 key = 1'b1;
            #20 key = 1'b0;
            #5  key = 1'b1;
            #15 key = 1'b0;
            #15 key = 1'b1;
            #10 key = 1'b0;
            #5  key = 1'b1;
    
    
            // Real Push down
            #10 key = 1'b0;
            // Up key
            #200 key = 1'b1;
    	end
          
    endmodule

    仿真波形全貌为:

    第一个抖动,并没有认为是按键,

    放大第一个逻辑为:

     

    第二个是的确按下了,所以逻辑正确,放大第二段逻辑:

     

    展开全文
  • 基于verilog按键消抖设计

    千次阅读 2013-09-10 15:13:14
    基于verilog按键消抖设计   关于键盘的基础知识,我就以下面的一点资料带过,因为这个实在是再基础不过的东西了。然后我引两篇我自己的博文,都是关于按键消抖的,代码也正是同目录下project里的。这两篇博文都...

                                                                           基于verilog按键消抖设计


    关 于键盘的基础知识,我就以下面的一点资料带过,因为这个实在是再基础不过的东西了。然后我引两篇我自己的博文,都是关于按键消抖的,代码也正是同目录下project里的。这两篇博文都是ednchina的博客精华,并且在其blog首页置顶多日,我想对大家会很有帮助的。
     
    键盘的分类
          键盘分编码键盘和非编码键盘。键盘上闭合键的识别由专用的硬件编码器实现,并产生键编码号或键值的称为编码键盘,如计算机键盘。而靠软件编程来识别的称为非编码键盘。
    在单片机组成的各种系统中,用的最多的是非编码键盘。也有用到编码键盘的。非编码键盘有分为:独立键盘和行列式(又称为矩阵式)键盘。
    按键在闭合和断开时,触点会存在抖动现象:  

           从上面的图形我们知道,在按键按下或者是释放的时候都会出现一个不稳定的抖动时间的,那么如果不处理好这个抖动时间,我们就无法处理好按键编码,所以如何才能有效的消除按键抖动呢?让下面的两篇博文日志给你答案吧。
     
     
     
    经典的verilog键盘扫描程序
     
           拿到威百仕( VibesIC )的板子后就迫不及待的开始我的学习计划,从最基础的分频程序开始,但看到这个键盘扫描程序后,直呼经典,有相见恨晚的感觉,还想说一句:威百仕( VibesIC ),我很看好你!WHY?待我慢慢道来,这个程序的综合后是0error,0warning。想想自己编码的时候那个warning是满天飞,现在才明白HDL设计有那么讲究了,代码所设计的不仅仅是简单的逻辑以及时序的关系,更重要的是你要在代码中不仅要表现出每一个寄存器,甚至每一个走线。想想我写过的代码,只注意到了前者,从没有注意过后者,还洋洋自得以为自己也算是个高手了,现在想来,实在惭愧啊!学习学习在学习,这也重新激发了我对HDL设计的激情,威百仕给了我一个方向,那我可要开始努力喽!
           废话说了一大堆,看程序吧:(本代码经过ise7.1i综合并下载到SP306板上验证通过)
    //当三个独立按键的某一个被按下后,相应的LED被点亮;再次按下后,LED熄灭,按键控制LED亮灭
     
    `timescale 1ns/1ns
     
    module keyscan(
        clk,      
        rst_n,
        sw1_n,
        sw2_n,
        sw3_n,
        //output
        led_d3,
        led_d4,
        led_d5
        );
     
      input   clk;           //主时钟信号,48MHz
      input   rst_n; //复位信号,低有效
      input   sw1_n,sw2_n,sw3_n; //三个独立按键,低表示按下
      output  led_d3,led_d4,led_d5; //发光二极管,分别由按键控制
     
      // ---------------------------------------------------------------------------
     
      reg [19:0]  cnt;      //计数寄存器
      always @ (posedge clk  or negedge rst_n)
        if (!rst_n)           //异步复位
          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};
         
      // ---------------------------------------------------------------------------
     
      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_d5 = d1 ? 1'b1 : 1'b0;              //LED翻转输出
      assign led_d3 = d2 ? 1'b1 : 1'b0;
      assign led_d4 = d3 ? 1'b1 : 1'b0;
     
    endmodule
     
           也许初看起来这段代码似乎有点吃力,好多的always好多的wire啊,而我们通常用得最多的判断转移好像不是主流。的确是这样,一个好的verilog代码,用多个always语句来分摊一个大的always来执行,会使得综合起来更快,这也是接前两篇日志说到代码优化的一个值得学习的方面。其次是wire连线很多,你要是仔细研究代码,不难发现所有的锁存器的连线关系编程者都考虑到了,这样就不会平白无故的生成意想不到的寄存器了,这也是一个优秀代码的必备要素。
           上面说的是代码风格,下面就看程序的编程思想吧。前两个always语句里其实是做了一个20ms的计数,每隔20ms就会读取键值,把这个键值放到寄存器low_sw中,接下来的一个always语句就是把low_sw的值锁存到low_sw_r里,这样以来,low_sw和low_sw_r就是前后两个时钟周期里的键值了,为什么要这样呢?看下一个语句吧: 
    wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);
           仔细分析,你会发现当没有键按下时,low_sw=low_sw_r=3’b111,此时的led_ctrl=3’b000;只有当low_sw和low_sw_r的某一位分别为0和1时,才可能使led_ctrl的值改变(也就是把led_ctrl的某一位拉高)。那么这意味着当键值由1跳变到0时才可能把led_ctrl拉高。回顾前面的20ms赋键值,也就是说每20ms内如果出现按键被按下,那么有一个时钟周期里led_ctrl是会被拉高的,而再看后面的程序,led_ctrl的置高就使得相应的LED灯的亮灭做一次改变,这就达到了目的。

    展开全文
  • FPGA Verilog按键消抖

    千次阅读 2017-12-15 10:16:24
    硬件: 锆石A4开发板,Altera EP4CE10F17C8 ...原理 : 机械式按键大概有20ms的抖动,所以检测到按键变化后等20ms再判断一次,如果键值相同才视为有效; 源码: module key_debounced(keyVal,key,clock,r

    硬件: 锆石A4开发板,Altera EP4CE10F17C8

    语言: Verilog HDL

    原理 :  机械式按键大概有20ms的抖动,所以检测到按键变化后等20ms再判断一次,如果键值相同才视为有效;


    顶级模块 按键切换LED显示

    module Key(led,key,clock,reset);
    
    input clock,reset;
    input  [7:0] key;
    output [7:0] led;
    
    wire   [7:0] keyVal;//消除抖动后的按键值
    reg    [7:0] led;  //控制显示的寄存器
    reg    [7:0] led_next;
    
    //消抖
    KeyDebounced kd1(.keyVal(keyVal),.key(key),.clock(clock),.reset(reset));
    
    //时序电路
    always @(posedge clock,negedge reset)
      begin
        if(!reset)
          led <= 8'h0;
        else
          led <= led_next;
      end
    
    //组合电路 给显示寄存器leds赋值
    always @(keyVal)
      led_next = led ^ keyVal;
    
    endmodule


    按键消抖模块

    module KeyDebounced(keyVal,key,clock,reset);
    
    parameter KeyCnt = 8;//默认8个按键
    parameter TIME   = 20'd999_999;//50Mhz 0.02us 20ms 10^6-1
    
    input clock,reset;
    input  [7:0] key;
    output [7:0] keyVal;//输出稳定的键值
    
    reg [19:0]       time_cnt;
    reg [19:0]       time_cnt_next;
    reg [KeyCnt-1:0] key_reg;
    reg [KeyCnt-1:0] key_reg_next;
    
    //时序电路 给定时器赋值
    always @(posedge clock,negedge reset)
      begin
        if(!reset)
          time_cnt <= 20'h0;
        else
          time_cnt <= time_cnt_next;
      end
    
    //组合电路 实现定时器
    always @(*)
      begin
        if(time_cnt == TIME)
          time_cnt_next = 20'h0;
        else
          time_cnt_next =time_cnt + 20'h1;
      end
    
    //时序电路 给按键寄存器赋值
    always @(posedge clock,negedge reset)
      begin
        if(!reset)
          key_reg <= 8'h0;
        else 
          key_reg <= key_reg_next;
      end
    
    //组合电路 每隔一个定时器周期接受依次按键的值
    always @(*)
      begin
        if(time_cnt == TIME)
          key_reg_next = key;
        else
          key_reg_next <= key_reg;
      end
     
    assign keyVal = key_reg & (~key_reg_next);
     
    endmodule


    展开全文
  • 参考:正点原子(http://www.openedv.com)——开拓者... RTL原理图: top 程序: ...//author: bronceyang ...//功能:按键消抖;按键控制蜂鸣器的叫停 module beep_key( input sys_clk, input sys_rst_n, ...

    参考:正点原子(http://www.openedv.com)——开拓者FPGA教程

    RTL原理图:

    top 程序:

    
    
    //author: bronceyang
    //time  : 2020年4月16日
    //version: 1.0
    //功能:按键消抖;按键控制蜂鸣器的叫停
    
    
    module beep_key(
    
     input sys_clk,
     input sys_rst_n,
     input key,
     
     output beep
    
    );
    //parameter define
    
    // reg define
    
    
    //wire define 
    wire key_value;
    wire key_flag;
    
    //********************************************
    //             main()
    //*********************************************
    
    //例化按键消抖模块
    key_debounce u_key_debounce(
    	.sys_clk(sys_clk),
    	.sys_rst_n(sys_rst_n),
    	.key(key),
    	
    	.key_value(key_value),
    	.key_flag(key_flag)
    
    );
    
    //例化按键控制蜂鸣器
    beep_control  u_beep_control(
    	.sys_clk(sys_clk),
    	.sys_rst_n(sys_rst_n),
    	.key_flag(key_flag),
    	.key_value(key_value),
    	
    	.beep(beep)
    
    );
    
    
    endmodule
    
    

    按键消抖程序:

    //按键消抖模块
    
    
    module key_debounce(
    	input sys_clk,
    	input sys_rst_n,
    	input key,
    	
    	output reg key_value,
    	output reg key_flag
    
    );
    
    
    //parameter define
    parameter CNT_20MS=32'd1_000_000;
    
    //reg define
    reg [31:0] cnt_delay;
    reg key_reg;
    
    //wire define
    
    //*****************************************
    //      main
    //****************************************
    
    
    //检测到按键有变化时立刻赋值20ms,不变化时开始倒数计时
    always@(posedge sys_clk or negedge sys_rst_n)begin
      if (!sys_rst_n)begin
    		cnt_delay<=32'd0;
    		key_reg<=1'b1;
    		end
      else begin
    		key_reg<=key;
    		if(key_reg!=key)            //只要检测到按键变化就重新赋值20ms
    			cnt_delay<=CNT_20MS;
    		else begin                   //按键稳定时,开始计数20ms
    			if (cnt_delay>32'd0)
    				cnt_delay<=cnt_delay-1'b1;
    			else 
    				cnt_delay<=cnt_delay;
    			end
      end
       
    end	
    //消抖结果输出
    always @(posedge sys_clk or negedge sys_rst_n)begin
    	if(!sys_rst_n)begin
    	key_value<=1'b1;    //复位状态
    		key_flag<=1'b0;
    		end
    	else  begin
    		if (cnt_delay==32'd1)begin   //按键稳定了20ms,已经消抖
    			key_value<=key;      
    		   key_flag<=1'b1;
    			end
    		else begin
    			key_value<=key_value;    
    		   key_flag<=1'b0;
    			end
       end
    end
    
    	
    
    endmodule

    控制蜂鸣器输出:

    
    
    //按键消抖结果控制蜂鸣器
    
    module beep_control(
       input sys_clk,
    	input sys_rst_n,
    	input key_value,
    	input key_flag,
    	
    	output reg beep
    
    );
    
    //wire define
    
    wire key_en;  //按键检测结果
    
    assign key_en=key_flag && (~key_value);
    
    //控制输出
    always@(posedge sys_clk or negedge sys_rst_n)begin
    	if (!sys_rst_n)
    		beep<=1'b0;
    	else if (key_en)
    		beep<=~beep;
    end
    	
    	endmodule

     

     

    展开全文
  • 我的方法是通过计时来消抖,通过一个计数器,当按键输入有变化时,计数器清零,否则就累加,直到加到一个预定值,就认为按键稳定,输出按键值,这样就得到了没有抖动的按键值。 module key( input clk, input key_...
  • 本文是对特权同学的按键消抖实验的作的一个随笔,主要是从自己的角度来理解按键消抖。 消抖,顾名思义就是对抖动的情况进行消去,这种抖动的情况包括外界非人为而 产生的抖动,比如所在平面的震动、周边的按键对其...
  • 1、如下声明: a、按键状态:默认态低电平,按键按下为高电平 b、本文假设抖动时间恰好为20ms!.../******************按键消抖**********************/ //时序电路,按键按下状态 always @ (posedge C...
  • 13.FPGA_Verilog 按键消抖之按下检测

    千次阅读 2018-08-10 09:17:30
    描述: 按键抖动5ms之内,设10ms为抖动时间。cnt控制:key_in为高的时候cnt为0, key_in为高的时候cnt计数,cnt...flag信号即为按键消抖之后的信号。 时序图: module key_disappears_shakes( input wire sclk ...
  • 时序图 module key_disappears_shakes( input wire sclk , input wire rst_n , input wire key , o...
  • 二、 消抖模块Verilog 三段式状态机 这个逻辑很简单,一段式就能写完,用三段式可能有点浪费资源,但是为了养成一个编码习惯吧,复杂的状态机用三段式就很清晰,易读性增强。 module btn_jitter( input clk, .
  • Verilog按键消抖检测的实现

    千次阅读 2020-03-30 09:12:46
    Verilog按键消抖是FPGA学习时的一个入门教程,为避免眼高手低,还是再次分析与记录一下。此处着重介绍按键消抖的基本原理,对按键消抖与检测的关键技术进行分析,并进行功能仿真。 一、按键消抖基本原理 1、按键消...
  • Verilog中的按键消抖

    千次阅读 2019-05-21 00:58:59
    Verilog 按键消抖模块 module key_filter(Clk,Rst_n,key_in,key_flag,key_state); input Clk; input Rst_n; input key_in; output reg key_flag; output reg key_state; localparam //定义四个状态 ...
  • 基于verilog按键消抖源代码设计,消除了按下、抬起时的抖动以及外界环境导致的低电平期间的毛刺、抖动。
  • 按键消抖verilog

    2015-05-05 11:14:57
    按键消抖 verilog
  • Verilog按键消抖控制LED灯的点亮和熄灭 在学习FPGA的基础阶段会有按键控制LED灯亮灭的实验,其中避免不了要对按键进行消抖处理,还包括LED的控制模块,和顶层例化模块。本文包括详细的源代码和注释。 按键消抖部分的...
  • 基于Verilog语言的按键消抖模块,比传统的延时消抖更为精细,无论是长按,还是只按一下都可以检测,对于低频时钟接入也支持
  • verilog按键扫描及消抖

    2018-12-21 16:47:03
    一个verilog按键的扫描程序,还可以实现消抖,可以使用。
  • 按键消抖电路的程序,可以实现按键消抖功能
  • epm240开发板Verilog例程按键消抖实验工程文件源码+说明文档.zip
  • verilog按键消抖的理解

    千次阅读 2019-09-17 10:22:05
    按键在按下时会产生抖动,释放时也会产生抖动,所以在设计键盘扫描程序时必须考虑按键消抖,我们一般只考虑按下时的抖动,而放弃对释放时抖动的消抖。抖动时间一般为20ms左右。按下的最终结果是低电平。 按键去...

空空如也

空空如也

1 2 3 4 5 ... 10
收藏数 182
精华内容 72
关键字:

verilog按键消抖