【原創】FPGA開發手記(三) PS/2鍵盤

如下內容均以Xilinx的Nexys3做爲開發板
 
1.PS/2鍵盤簡介

雖然Nexys3開發板是利用USB接口搭載鍵盤,可是其原理與PS/2鍵盤徹底相同,如今就僅以PS/2鍵盤爲例講解如何將鍵盤搭載在開發板上。代碼程序均在Nexys3上通過測試。測試

PS/2標準鍵盤使用6個接口,各個接口定義以下:
 
1:DATA,數據信號
2:N.C.,不鏈接
3:GND,地
4:VCC,+5V電源
5:CLK,時鐘
6:N.C.,不鏈接

而對於USB鍵盤,有用的接口只有兩個CLK以及DATA,同時須要+5V供電。這點很重要,可是FPGA的CMOS最大供電是3.3V,如何解決這個問題筆者也是花費了一番周折,文章末會提到。spa

PS/2標準的數據幀格式與串口通信很類似,USB就是串口通信的格式。起始爲低電平,中止位爲高電平。若是數據位中1的個數是偶數,校驗位就爲1;若是數據位中1的個數是奇數,校驗位就爲0,一般本身使用咱們都無論校驗位。其實就是奇校驗。
                  圖1 數據幀格式
 
2.鍵盤掃描碼
 

當PC經過PS/2接口與從設備通訊時,老是利用降低沿讀取數據。鍵盤僅僅在數據線和時鐘線都爲高電平(即空閒態)時能夠把數據傳送給主機,一旦主機使用了總線,鍵盤在驅動總線前必須檢查主機當前是否仍在發送數據。爲了促進這一覈查,時鐘線將暫時做爲發送完成信號。一旦主機把時鐘線拉低,那麼鍵盤將使時鐘線變成高電平才能夠傳輸數據。設計

PS/2型鍵盤主要使用掃描碼來進行數據傳輸。每一個按鍵被按下時都會被分配固定的掃描碼,若是按鍵一直被按下,那麼該按鍵碼值將每隔100ms重複發送一次。當按鍵釋放後,一個關鍵碼F0發送後,該掃描碼纔會被髮送。當使用上檔鍵進行組合鍵時,也會產生上檔鍵的碼值,而且由主機決定究竟是哪個ASCII值。當組合鍵被釋放後,一個E0和F0關鍵碼發送後,按鍵掃描碼才被髮送。。大多數按鍵的掃描碼以下圖所示。
                    圖2 鍵盤掃描碼
 

主機也會向鍵盤發送數據。下面是主機可能發送的經常使用命令的清單。code

ED  使NumLock,CapsLock和ScrollLock LED燈發光。在接收到ED後鍵盤反饋FA碼,而後主機發送一個用來設置LED顯示狀態的字節:第0位控制ScrollLock置位,第1位控制NumLock置位,第2位控制CapsLock置位;blog

EE  用來進行鍵盤測試,當鍵盤接受EE後將反饋EE;接口

F3  設置掃描碼重複頻率,當鍵盤接受FA反饋F3後,主機將發送第二個字符用來設置重複頻率。ci

FE  從新發送。FE將使鍵盤從新發送最近的掃描碼。開發

FF  重置。重置鍵盤。get

 

3.源程序簡介class

PS/2鍵盤模塊接口定義:
 
首先,仍是利用移位寄存器的方式捕捉PS/2鍵盤時鐘線的降低沿。
reg ps2_clk_r0,ps2_clk_r1,ps2_clk_r2;
wire neg_ps2_clk;
    
always @(posedge clk or negedge rst_n) begin
if (!rst_n)  begin
        ps2_clk_r0 <= 1'b0;
        ps2_clk_r1 <= 1'b0;
        ps2_clk_r2 <= 1'b0;
    end
    else begin
        ps2_clk_r0 <= ps2_clk;
        ps2_clk_r1 <= ps2_clk_r0;
        ps2_clk_r2 <= ps2_clk_r1;
    end    
end // end always
    
assign neg_ps2_clk = ps2_clk_r2 & (~ps2_clk_r1);

neg_ps2_clk即爲PS/2時鐘信號降低沿的有效信號。而後,利用一個寄存器和計數器,存儲8位有效數據幀。

//------------------------------------------------------
// 接受來自PS/2鍵盤的數據存儲器
reg [7:0] ps2_byte_r;        // 來自PS/2的數據寄存器
reg [7:0] temp_data;            // 當前接受數據寄存器
reg [3:0] num;
    
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        num <= 4'd0;
        temp_data <= 8'd0;
    end
    else if (neg_ps2_clk) begin
        case (num)
            4'd0: begin
                num <= num + 1'b1;
            end
            4'd1: begin
                num <= num + 1'b1;
                temp_data[0] <= ps2_data;
            end
            4'd2: begin
                num <= num + 1'b1;
                temp_data[1] <= ps2_data;
            end
            4'd3: begin
                num <= num + 1'b1;
                temp_data[2] <= ps2_data;
            end
            4'd4: begin
                num <= num + 1'b1;
                temp_data[3] <= ps2_data;
            end
            4'd5: begin
                num <= num + 1'b1;
                temp_data[4] <= ps2_data;
            end
            4'd6: begin
                num <= num + 1'b1;
                temp_data[5] <= ps2_data;
            end
            4'd7: begin
                num <= num + 1'b1;
                temp_data[6] <= ps2_data;
            end
            4'd8: begin
                num <= num + 1'b1;
                temp_data[7] <= ps2_data;
            end
            4'd9: begin
                num <= num + 1'b1;
            end
            4'd10: begin
                num <= 4'b0;
            end
            default: num <= 4'd0;
        endcase
    end
end // end always 

經過當前按鍵狀態,判斷通碼仍是斷碼,並保存在ps2_byte_r的8位寄存器內。

//-------------------------------------
    reg key_f0;                // 鬆鍵標誌位
    reg ps2_state_r;        // 當前狀態,高電平表示有鍵按下
    
    always @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            key_f0 <= 1'b0;
            ps2_state_r <= 1'b0;
            ps2_byte_r <= 8'd0;
        end
        else if (num == 4'd10) begin    // 剛傳輸一個字節
            if (temp_data == 8'hf0)    begin // 斷碼的第一個字節
                key_f0 <= 1'b1;
            end    
            else begin
                if (key_f0) begin    // 存儲通碼
                    ps2_state_r <= 1'b1;
                    ps2_byte_r <= temp_data;
                    key_f0 <= 1'b0;
                end
                else begin
                    ps2_state_r <= 1'b0;
                    key_f0 <= 1'b0;
                end
            end    
        end
        else    ps2_state_r <= 1'b0;
    end // end always

利用case判斷不一樣的通碼,來決定字符的ASCII。

//---------------------------------------
    reg [7:0] ps2_ascii;    //接受相應的asciiI
    
    always @(posedge clk) begin
        case (ps2_byte_r)
            8'h15: ps2_ascii = 8'h51;    //Q
            8'h1d: ps2_ascii = 8'h57;    //W
            8'h24: ps2_ascii = 8'h45;    //E
            8'h2d: ps2_ascii = 8'h52;    //R
            8'h2c: ps2_ascii = 8'h54;    //T
            8'h35: ps2_ascii = 8'h59;    //Y
            8'h3c: ps2_ascii = 8'h55;    //U
            8'h43: ps2_ascii = 8'h49;    //I
            8'h44: ps2_ascii = 8'h4f;    //O
            8'h4d: ps2_ascii = 8'h50;    //P                      
            8'h1c: ps2_ascii = 8'h41;    //A
            8'h1b: ps2_ascii = 8'h53;    //S
            8'h23: ps2_ascii = 8'h44;    //D
            8'h2b: ps2_ascii = 8'h46;    //F
            8'h34: ps2_ascii = 8'h47;    //G
            8'h33: ps2_ascii = 8'h48;    //H
            8'h3b: ps2_ascii = 8'h4a;    //J
            8'h42: ps2_ascii = 8'h4b;    //K
            8'h4b: ps2_ascii = 8'h4c;    //L
            8'h1a: ps2_ascii = 8'h5a;    //Z
            8'h22: ps2_ascii = 8'h58;    //X
            8'h21: ps2_ascii = 8'h43;    //C
            8'h2a: ps2_ascii = 8'h56;    //V
            8'h32: ps2_ascii = 8'h42;    //B
            8'h31: ps2_ascii = 8'h4e;    //N
            8'h3a: ps2_ascii = 8'h4d;    //M
            default    ps2_ascii = 8'hfe;
        endcase    
    end
    
    assign ps2_byte = ps2_ascii;
    assign ps2_state = ps2_state_r;

以上就是程序代碼的主要部分。最重要的仍是管腳分配文件如何解決FPGA供電不足的問題。其實也很簡單,只是咱們平時不經常使用罷了。並且這也是官方標準ucf文件的一個錯誤。只須要將PS/2鍵盤的時鐘線與數據線上拉便可,詳細內容以下(只列舉PS/2鍵盤部分):

NET "ps2_clk" LOC = L12 | IOSTANDARD = LVCMOS33 | PULLUP;

NET "ps2_data" LOC = J13 | IOSTANDARD = LVCMOS33 | PULLUP;

至此,筆者也只是將PS/2鍵盤搭載到了開發板上。可是如何更加靈活的利用鍵盤以及VGA,還須要仔細的設計各個寄存器的功能以及用法。

如下Demo內容須要鏈接VGA接口,按動鍵盤的A~Z的任意鍵,LED燈會顯示對應的ASCII值。若按動A鍵,屏幕會顯示字符a。(由於沒有時間折騰字符提取軟件,rom比較小隻存了a的一個字符。喜歡的朋友也能夠增長不少其餘的字符。)若按動其餘鍵,屏幕只顯示一個白色矩形。

4.源程序

  PS2鍵盤源程序.rar

 

以上內容都可以在百度網盤下載 http://pan.baidu.com/share/link?shareid=940132753&uk=1092766566
相關文章
相關標籤/搜索