专注电子技术学习与研究
当前位置:单片机教程网 >> MCU设计实例 >> 浏览文章

Verilog HDL-键盘编程

作者:佚名   来源:本站原创   点击数:  更新时间:2010年08月30日   【字体:

今天看了一下Verilog HDL的按键程序,收获如下:

1、HDL硬件描述语言和一般的软件编程语言有着很大的差别,它跟硬件是息息相关的,什么样的HDL就有什么样的硬件。要学会查看QII里面的RTL视图,这个就是比较优略的地方。

2、Verliog HDL跟C的语法比较相近,但描述的方式有很大的不同,Verilog 里面跟多的是alawys块,这个跟VHDL的进程有异曲同工之处啊。C跟时序不相关的多,但HDL跟时序太密切了,如果忘了这一点,理解程序就很麻烦了。

3、阻塞赋值跟非阻塞赋值也得深入的学习,往往会多出一个D触发器来。

4、虽然每个alawys块是并行的,但是上下两个寄存器有联系(<=)之后就差了一个时间周期,就像今天看的按键程序,刚开始看的云里雾里的,后来注意到了这一点,豁然慨然了。


     自从加入EDN助学—FPGA/CPLD助学小组之后,下载了许多特权同学的视频教材,今天又把特权同学的按键程序看了一遍,终于有突破式的理解了。那么首先把程序黏贴如下:
 




`timescale 1ns / 1ps
//说明:当三个独立按键的某一个被按下后,相应的LED被点亮;
//  再次按下后,LED熄灭,按键控制LED亮灭

module sw_debounce(
      clk,rst_n,
   sw1_n,sw2_n,sw3_n,
      led_d1,led_d2,led_d3
      );

input   clk; //主时钟信号,50MHz
input   rst_n; //复位信号,低有效
input   sw1_n,sw2_n,sw3_n;  //三个独立按键,低表示按下
output  led_d1,led_d2,led_d3; //发光二极管,分别由按键控制

//------------------------第一段--------------------------------------------------
reg[2:0] key_rst;  

always @(posedge clk  or negedge rst_n)
    if (!rst_n) key_rst <= 3'b111;
    else key_rst <= {sw3_n,sw2_n,sw1_n};

 

reg[2:0] key_rst_r;       //每个时钟周期的上升沿将low_sw信号锁存到low_sw_r中

always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) key_rst_r <= 3'b111;
    else key_rst_r <= key_rst;
   
//当寄存器key_rst由1变为0时,key_an的值变为高,维持一个时钟周期 
wire[2:0] key_an = key_rst_r & ( ~key_rst);

//---------------------第二段-----------------------------------------------------
reg[19:0]  cnt; //计数寄存器

always @ (posedge clk  or negedge rst_n)
    if (!rst_n) cnt <= 20'd0; //异步复位
 else if(key_an) 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中  cnt == 20'hfffff
      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_d3 = d1 ? 1'b1 : 1'b0;  //LED翻转输出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;
  
endmodule

然后就开始我的备忘了。

1、第一段程序中的key_rst和key_rst_r寄存器分别存储着差一个时钟周期的按键值, 这条语句 wire[2:0] key_an = key_rst_r & ( ~key_rst)的功能是检测一个脉冲,也就是说当寄存器key_rst由1变为0时,key_an的值变为高,维持一个时钟周期 ,这样就检测到了一个时钟周期的脉冲。

2、第二段程序与第一段的程序有点相似,前两个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灯的亮灭做一次改变,这就达到了目的。

3、在单片机的按键检测程序里,当按键按下后,然后延时20MS,再判断是否按下,这样就起到了按键去抖动的作用,而在FPGA中,HDL语言直接对应着实际的硬件电路,没有单片机里的循环扫描的概念。
 

关闭窗口

相关文章