<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 牛人業(yè)話(huà) > 小梅哥和你一起深入學(xué)習(xí)FPGA之PS2鍵盤(pán)驅(qū)動(dòng)

          小梅哥和你一起深入學(xué)習(xí)FPGA之PS2鍵盤(pán)驅(qū)動(dòng)

          作者: 時(shí)間:2015-08-18 來(lái)源:網(wǎng)絡(luò) 收藏

            五、 代碼分析

          本文引用地址:http://www.ex-cimer.com/article/278903.htm

            這里,解碼的關(guān)鍵是接口的時(shí)鐘信號(hào),該時(shí)鐘為異步時(shí)鐘,我們需要通過(guò)邊沿檢測(cè)的方式來(lái)檢測(cè)其下降沿,以便根據(jù)下降沿的個(gè)數(shù)來(lái)確定每個(gè)時(shí)鐘我們因該做什么,邊沿檢測(cè)的電路,前面幾個(gè)實(shí)驗(yàn)已經(jīng)講過(guò)很多次了,這里便不再做過(guò)多的解釋?zhuān)N上代碼即可:

            以下是代碼片段:

            reg _Clk_Tmp0,_Clk_Tmp1,PS2_Clk_Tmp2,PS2_Clk_Tmp3;

            wire nedge_PS2_Clk; /*PS2從機(jī)時(shí)鐘下降沿檢測(cè)標(biāo)志信號(hào)*/

            always @ (posedge Clk or negedge Rst_n)

            if(!Rst_n) begin

            PS2_Clk_Tmp0 <= 1'b0;

            PS2_Clk_Tmp1 <= 1'b0;

            PS2_Clk_Tmp2 <= 1'b0;

            PS2_Clk_Tmp3 <= 1'b0;

            end

            else begin

            PS2_Clk_Tmp0 <= PS2_Clk;

            PS2_Clk_Tmp1 <= PS2_Clk_Tmp0;

            PS2_Clk_Tmp2 <= PS2_Clk_Tmp1;

            PS2_Clk_Tmp3 <= PS2_Clk_Tmp2;

            end

            /*-------獲取PS時(shí)鐘信號(hào)的下降沿-------------*/

            assign nedge_PS2_Clk = !PS2_Clk_Tmp0 & !PS2_Clk_Tmp1 & PS2_Clk_Tmp2 & PS2_Clk_Tmp3;

            一個(gè)PS2的數(shù)據(jù)包總共由11位組成,因此會(huì)有11個(gè)時(shí)鐘下降沿,因此我們必須對(duì)下降沿的個(gè)數(shù)準(zhǔn)確計(jì)數(shù),才能保證我們能夠解碼得到正確的數(shù)據(jù),這里,使用我們的PS2時(shí)鐘下降沿標(biāo)志信號(hào)來(lái)使能我們的計(jì)數(shù)器自加,當(dāng)計(jì)數(shù)器加到11后,表示一個(gè)數(shù)據(jù)包接收完成,將計(jì)數(shù)器清零,等待下一個(gè)下降沿的到來(lái),相關(guān)代碼如下:

            以下是代碼片段:

            /*------------PS2時(shí)鐘下降沿個(gè)數(shù)計(jì)數(shù)器-----------------------*/

            always @(posedge Clk or negedge Rst_n)

            if(!Rst_n)

            Cnt1 <= 4'd0;

            else if(Cnt1 == 4'd11)

            Cnt1 <= 4'd0;

            else if(nedge_PS2_Clk)

            Cnt1 <= Cnt1 + 1'b1;

            接下來(lái),就是根據(jù)時(shí)鐘下降沿的計(jì)數(shù)個(gè)數(shù),來(lái)讀取對(duì)應(yīng)位的數(shù)據(jù)了,因?yàn)椴捎昧朔亲枞x值的方式,因此,PS2時(shí)鐘下降沿到來(lái)時(shí),此時(shí)Cnt1執(zhí)行自加1操作,但同時(shí)如果也來(lái)用Cnt1的值來(lái)確定數(shù)據(jù)位數(shù),就一定會(huì)造成錯(cuò)誤,因?yàn)榇藭r(shí),Cnt1的加1操作并沒(méi)有執(zhí)行,而是會(huì)在下一個(gè)時(shí)鐘上升沿到來(lái)之時(shí)才變,因此,為了保證我們使用的Cnt1的數(shù)據(jù)是已經(jīng)更新了的,我們需要在Cnt1已經(jīng)變化之后再來(lái)使用其值做判斷,即在PS2時(shí)鐘下降沿檢測(cè)成功后,滯后一個(gè)系統(tǒng)時(shí)鐘周期后再來(lái)讀取PS2_Din上的值,比較簡(jiǎn)單的操作方式就是將PS2時(shí)鐘下降沿檢測(cè)標(biāo)志信號(hào)再用寄存器打一拍,對(duì)應(yīng)代碼如下:

            以下是代碼片段:

            always @(posedge Clk)nedge_PS2_Clk_Shift <= nedge_PS2_Clk;

            可能這里相對(duì)比較難以理解,希望大家結(jié)合仿真結(jié)果自學(xué)揣摩體會(huì)。

            接下來(lái)就是根據(jù)Cnt1的計(jì)數(shù)值來(lái)讀取每一位的數(shù)據(jù)了,這部分代碼很簡(jiǎn)單,如下所示:

            以下是代碼片段:

            /*--------------讀取8位數(shù)據(jù)位---------------*/

            always @ (posedge Clk or negedge Rst_n)

            if(!Rst_n)

            Data_tmp <= 8'd0;

            else if(nedge_PS2_Clk_Shift) begin

            case(Cnt1)

            4'd2:Data_tmp[0] <= PS2_Din;

            4'd3:Data_tmp[1] <= PS2_Din;

            4'd4:Data_tmp[2] <= PS2_Din;

            4'd5:Data_tmp[3] <= PS2_Din;

            4'd6:Data_tmp[4] <= PS2_Din;

            4'd7:Data_tmp[5] <= PS2_Din;

            4'd8:Data_tmp[6] <= PS2_Din;

            4'd9:Data_tmp[7] <= PS2_Din;

            default:Data_tmp <= Data_tmp;

            endcase

            end

            else

            Data_tmp <= Data_tmp;

            通過(guò)以上操作,我們就能正確的解碼PS2鍵盤(pán)發(fā)送過(guò)來(lái)的每一個(gè)字節(jié)的數(shù)據(jù)了,但是,這些數(shù)據(jù)代表了什么呢,如果是斷碼標(biāo)志,或者是長(zhǎng)碼標(biāo)志,我們又該如何進(jìn)行操作呢,這里,小梅哥先貼上我的處理代碼:

            以下是代碼片段:

            always @ (posedge Clk or negedge Rst_n)

            if(!Rst_n) begin

            Break_r <= 1'b0;

            Key_Valve <= 10'd0;

            Key_Flag <= 1'b0;

            Long_Code_r <= 1'b0;

            end

            else if(Cnt1 == 4'd11) begin

            if(Data_tmp == 8'hE0) /*判斷是否為長(zhǎng)碼*/

            Long_Code_r <= 1'b1; /*將長(zhǎng)碼標(biāo)志置1*/

            else if(Data_tmp == 8'hF0) /*判斷是否為斷碼*/

            Break_r <= 1'b1; /*將斷碼標(biāo)志置1*/

            else begin /*檢測(cè)到的數(shù)據(jù)為通碼*/

            Key_Valve <= {Break_r,Long_Code_r,Data_tmp};/*將長(zhǎng)碼標(biāo)志、斷碼標(biāo)志和解碼到的按鍵碼輸出*/

            Key_Flag <= 1'b1; /*產(chǎn)生解碼成功標(biāo)志信號(hào)*/

            Long_Code_r <= 1'b0; /*清零長(zhǎng)碼標(biāo)志*/

            Break_r <= 1'b0; /*清零斷碼標(biāo)志*/

            end

            end

            else begin

            Key_Valve <= Key_Valve;

            Key_Flag <= 1'b0;

            Break_r <= Break_r;

            Long_Code_r <= Long_Code_r;

            end

            這里,小梅哥使用了兩個(gè)標(biāo)志寄存器,當(dāng)檢測(cè)數(shù)據(jù)完成后,即Cnt1=11時(shí),就對(duì)解碼到到數(shù)據(jù)進(jìn)行判斷,如果Data_tmp == 8'hE0,則解碼到到數(shù)據(jù)為長(zhǎng)碼(擴(kuò)展碼)標(biāo)志,此時(shí)便將長(zhǎng)碼標(biāo)志寄存器置1,如果Data_tmp == 8'hF0,則解碼到到數(shù)據(jù)為斷碼標(biāo)志,此時(shí)便將斷碼標(biāo)志寄存器置1。然后,當(dāng)解碼到其他數(shù)據(jù)(如單字節(jié)通碼或雙子節(jié)通碼的第二個(gè)字節(jié))后,便將解碼到的數(shù)據(jù)連同斷碼和長(zhǎng)碼標(biāo)志寄存器的狀態(tài)輸出,并給出按鍵檢測(cè)成功標(biāo)志(Key_Flag置1)。

            六、 仿真分析

            為了對(duì)小梅哥設(shè)計(jì)的PS2鍵盤(pán)解碼驅(qū)動(dòng)進(jìn)行驗(yàn)證,小梅哥編寫(xiě)了一個(gè)Testbench來(lái)模擬鍵盤(pán)發(fā)送數(shù)據(jù),通過(guò)觀察鍵盤(pán)解碼驅(qū)動(dòng)的輸出來(lái)驗(yàn)證該解碼模塊的正確性,關(guān)于模擬鍵盤(pán)發(fā)送數(shù)據(jù),在一份介紹PS2協(xié)議的手冊(cè)中有如下描述:

            我推薦仿真鍵盤(pán)/鼠標(biāo)采用下面的過(guò)程發(fā)送一字節(jié)的數(shù)據(jù)到主機(jī):

            1) 等待Clock線(xiàn)為高電平, 即等待主機(jī)釋放Clock線(xiàn);

            2) 延時(shí)50us;

            3) 判斷Clock線(xiàn)是否為高電平?

            No―― 跳到第1步;

            4) Data線(xiàn)是否為高電平?

            No―― 放棄(跳到從主機(jī)讀取字節(jié)的程序中) 。

            5) 延遲20us,輸出起始位(0) , 然后延遲20us, 再拉低Clock線(xiàn)保持40us后釋放Clock線(xiàn), 形成一個(gè)脈沖;

            6) 延時(shí)20us, 測(cè)試Clock線(xiàn)是否為高電平?No―― 跳到第1步;

            7) 輸出第1個(gè)數(shù)據(jù)位, 然后延時(shí)20us, 再拉低Clock線(xiàn)保持40us后釋放Clock線(xiàn), 形成一個(gè)脈沖;

            8) 重復(fù)6-7步發(fā)送剩下的7個(gè)數(shù)據(jù)位和校驗(yàn)位;

            9) 延時(shí)20us, 測(cè)試Clock線(xiàn)是否為高電平?

            No―― 跳到第1步;

            因此,我們的模擬鍵盤(pán)發(fā)送數(shù)據(jù)的過(guò)程只需要依照上面的流程來(lái)即可,這里貼上小梅哥編寫(xiě)的testbench:

            以下是代碼片段:

            `timescale 1ns/1ns

            module PS2_Key_Board_Driver_tb;

            reg Clk;/*system clock*/

            reg Rst_n;/*復(fù)位信號(hào)*/

            reg PS2_Din;/*PS2鍵盤(pán)數(shù)據(jù)線(xiàn)*/

            reg PS2_Clk;/*PS2鍵盤(pán)時(shí)鐘線(xiàn)*/

            wire Key_Flag;/*解碼得到鍵值標(biāo)志信號(hào)*/

            wire [9:0] Key_Valve;/*解碼結(jié)果,其中最高位為通/斷碼識(shí)別位,0為通碼,1為斷碼,低八位為碼值*/

            PS2_Key_Board_Driver u1(

            .Clk(Clk),

            .Rst_n(Rst_n),

            .PS2_Din(PS2_Din),

            .PS2_Clk(PS2_Clk),

            .Key_Flag(Key_Flag),

            .Key_Valve(Key_Valve)

            );

            initial begin

            Clk = 1;

            Rst_n = 0;

            PS2_Din = 1;

            PS2_Clk = 1;

            #200;

            Rst_n = 1;

            Key_Event(8'h1A); /* Z */

            #400;

            Key_Event(8'h35); /* X */

            #800;

            Key_Event(8'h44); /* O */

            #1320;

            Key_Event(8'h4D); /* P */

            #2560;

            Key_Event(8'h24); /* E */

            #1230;

            Key_Event(8'h31); /* N */

            #20000;

            Long_Key_Event(8'h70); /* "INSERT" */

            #400;

            Long_Key_Event(8'h6c); /* "HOME" */

            #800;

            Long_Key_Event(8'h7d); /* "PAGE UP" */

            #1320;

            Long_Key_Event(8'h71); /* "DELETE" */

            #2560;

            Long_Key_Event(8'h69); /* "END" */

            #1230;

            Long_Key_Event(8'h7a); /* "PAGE DOWN" */

            #2000000;

            $stop;

            end

            /*---------生成工作時(shí)鐘-----------*/

            always #10 Clk = ~Clk;

            /*----任務(wù):以PS2協(xié)議發(fā)送一個(gè)字節(jié)的數(shù)據(jù)-----*/

            task Send_data;

            input [7:0]Data;

            begin

            PS2_Din = 0; /*發(fā)送起始位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[0];/*發(fā)送第0位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[1];/*發(fā)送第1位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[2];/*發(fā)送第2位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[3];/*發(fā)送第3位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[4];/*發(fā)送第4位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[5];/*發(fā)送第5位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[6];/*發(fā)送第6位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = Data[7];/*發(fā)送第7位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = 0;/*暫時(shí)忽略校驗(yàn)位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            #20000;PS2_Din = 1;/*停止位*/

            #20000;PS2_Clk = 0;

            #40000;PS2_Clk = 1;

            end

            endtask

            /*-----任務(wù):模擬按下按鍵的操作------*/

            task press_key;

            input [7:0]Key_Number;

            begin

            Send_data(Key_Number);

            #50000;

            end

            endtask

            /*-----任務(wù):模擬釋放按鍵的操作------*/

            task release_key;

            input [7:0]Key_Number;

            begin

            Send_data(8'hF0);

            #50000;

            Send_data(Key_Number);

            #50000;

            end

            endtask

            /*----任務(wù):模擬一次短碼的按下和釋放操作-----*/

            task Key_Event;

            input [7:0]Key_Number;

            begin

            press_key(Key_Number);

            #30000;

            release_key(Key_Number);

            end

            endtask

            /*----任務(wù):模擬一次長(zhǎng)碼的按下和釋放操作-----*/

            task Long_Key_Event;

            input [7:0]Key_Number;

            begin

            press_key(8'he0);

            #30000;

            press_key(Key_Number);

            #30000;

            press_key(8'he0);

            #30000;

            release_key(Key_Number);

            end

            endtask

            endmodule

            testbench中使用了一個(gè)主任務(wù)來(lái)模擬鍵盤(pán)的數(shù)據(jù)發(fā)送,并使用了其他幾個(gè)基于此任務(wù)的任務(wù)來(lái)模擬按鍵按下、按鍵釋放、普通按鍵按下+釋放、擴(kuò)展按鍵按下+釋放的過(guò)程,通過(guò)模擬進(jìn)行部分按鍵的按下和釋放操作,來(lái)觀察解碼模塊的結(jié)果,便可獲知解碼是否成功。

            以下為小梅哥的仿真結(jié)果,與我發(fā)送的數(shù)據(jù)一致,因此表明我的PS2解碼是成功的。

            

           

            七、 下板驗(yàn)證

            這里,小梅哥在至芯科技ZX2的板子上驗(yàn)證通過(guò),如下圖:

            其中,第三個(gè)數(shù)碼管,為0表示普通按鍵通碼,為2表示普通按鍵斷碼,為1表示擴(kuò)展按鍵通碼,為3表示擴(kuò)展按鍵斷碼。

            

           

            

           

            

          fpga相關(guān)文章:fpga是什么


          蜂鳴器相關(guān)文章:蜂鳴器原理

          上一頁(yè) 1 2 下一頁(yè)

          關(guān)鍵詞: FPGA PS2

          評(píng)論


          相關(guān)推薦

          技術(shù)專(zhuān)區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();