專注電子技術(shù)學(xué)習(xí)與研究
當(dāng)前位置:單片機(jī)教程網(wǎng) >> MCU設(shè)計(jì)實(shí)例 >> 瀏覽文章

Verilog HDL-鍵盤編程

作者:佚名   來源:本站原創(chuàng)   點(diǎn)擊數(shù):  更新時(shí)間:2010年08月30日   【字體:

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

1、HDL硬件描述語言和一般的軟件編程語言有著很大的差別,它跟硬件是息息相關(guān)的,什么樣的HDL就有什么樣的硬件。要學(xué)會(huì)查看QII里面的RTL視圖,這個(gè)就是比較優(yōu)略的地方。

2、Verliog HDL跟C的語法比較相近,但描述的方式有很大的不同,Verilog 里面跟多的是alawys塊,這個(gè)跟VHDL的進(jìn)程有異曲同工之處啊。C跟時(shí)序不相關(guān)的多,但HDL跟時(shí)序太密切了,如果忘了這一點(diǎn),理解程序就很麻煩了。

3、阻塞賦值跟非阻塞賦值也得深入的學(xué)習(xí),往往會(huì)多出一個(gè)D觸發(fā)器來。

4、雖然每個(gè)alawys塊是并行的,但是上下兩個(gè)寄存器有聯(lián)系(<=)之后就差了一個(gè)時(shí)間周期,就像今天看的按鍵程序,剛開始看的云里霧里的,后來注意到了這一點(diǎn),豁然慨然了。


     自從加入EDN助學(xué)—FPGA/CPLD助學(xué)小組之后,下載了許多特權(quán)同學(xué)的視頻教材,今天又把特權(quán)同學(xué)的按鍵程序看了一遍,終于有突破式的理解了。那么首先把程序黏貼如下:
 



`timescale 1ns / 1ps
//說明:當(dāng)三個(gè)獨(dú)立按鍵的某一個(gè)被按下后,相應(yīng)的LED被點(diǎn)亮;
//  再次按下后,LED熄滅,按鍵控制LED亮滅

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

input   clk; //主時(shí)鐘信號(hào),50MHz
input   rst_n; //復(fù)位信號(hào),低有效
input   sw1_n,sw2_n,sw3_n;  //三個(gè)獨(dú)立按鍵,低表示按下
output  led_d1,led_d2,led_d3; //發(fā)光二極管,分別由按鍵控制

//------------------------第一段--------------------------------------------------
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;       //每個(gè)時(shí)鐘周期的上升沿將low_sw信號(hào)鎖存到low_sw_r中

always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) key_rst_r <= 3'b111;
    else key_rst_r <= key_rst;
   
//當(dāng)寄存器key_rst由1變?yōu)?時(shí),key_an的值變?yōu)楦撸S持一個(gè)時(shí)鐘周期 
wire[2:0] key_an = key_rst_r & ( ~key_rst);

//---------------------第二段-----------------------------------------------------
reg[19:0]  cnt; //計(jì)數(shù)寄存器

always @ (posedge clk  or negedge rst_n)
    if (!rst_n) cnt <= 20'd0; //異步復(fù)位
 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;       //每個(gè)時(shí)鐘周期的上升沿將low_sw信號(hào)鎖存到low_sw_r中

always @ ( posedge clk  or negedge rst_n )
    if (!rst_n) low_sw_r <= 3'b111;
    else low_sw_r <= low_sw;
   
//當(dāng)寄存器low_sw由1變?yōu)?時(shí),led_ctrl的值變?yōu)楦�,維持一個(gè)時(shí)鐘周期 
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  //某個(gè)按鍵值變化時(shí),LED將做亮滅翻轉(zhuǎn)
        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翻轉(zhuǎn)輸出
assign led_d2 = d2 ? 1'b1 : 1'b0;
assign led_d1 = d3 ? 1'b1 : 1'b0;
  
endmodule

然后就開始我的備忘了。

1、第一段程序中的key_rst和key_rst_r寄存器分別存儲(chǔ)著差一個(gè)時(shí)鐘周期的按鍵值, 這條語句 wire[2:0] key_an = key_rst_r & ( ~key_rst)的功能是檢測(cè)一個(gè)脈沖,也就是說當(dāng)寄存器key_rst由1變?yōu)?時(shí),key_an的值變?yōu)楦�,維持一個(gè)時(shí)鐘周期 ,這樣就檢測(cè)到了一個(gè)時(shí)鐘周期的脈沖。

2、第二段程序與第一段的程序有點(diǎn)相似,前兩個(gè)always語句里其實(shí)是做了一個(gè)20ms的計(jì)數(shù),每隔20ms就會(huì)讀取鍵值,把這個(gè)鍵值放到寄存器low_sw中,接下來的一個(gè)always語句就是把low_sw的值鎖存到low_sw_r里,這樣以來,low_sw和low_sw_r就是前后兩個(gè)時(shí)鐘周期里的鍵值了,為什么要這樣呢?看下一個(gè)語句吧: 

wire [2:0] led_ctrl = low_sw_r[2:0] & ( ~low_sw[2:0]);

       仔細(xì)分析,你會(huì)發(fā)現(xiàn)當(dāng)沒有鍵按下時(shí),low_sw=low_sw_r=3’b111,此時(shí)的led_ctrl=3’b000;只有當(dāng)low_sw和low_sw_r的某一位分別為0和1時(shí),才可能使led_ctrl的值改變(也就是把led_ctrl的某一位拉高)。那么這意味著當(dāng)鍵值由1跳變到0時(shí)才可能把led_ctrl拉高。回顧前面的20ms賦鍵值,也就是說每20ms內(nèi)如果出現(xiàn)按鍵被按下,那么有一個(gè)時(shí)鐘周期里led_ctrl是會(huì)被拉高的,而再看后面的程序,led_ctrl的置高就使得相應(yīng)的LED燈的亮滅做一次改變,這就達(dá)到了目的。

3、在單片機(jī)的按鍵檢測(cè)程序里,當(dāng)按鍵按下后,然后延時(shí)20MS,再判斷是否按下,這樣就起到了按鍵去抖動(dòng)的作用,而在FPGA中,HDL語言直接對(duì)應(yīng)著實(shí)際的硬件電路,沒有單片機(jī)里的循環(huán)掃描的概念。
 

關(guān)閉窗口

相關(guān)文章