新聞中心

        EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 單片機(jī)驅(qū)動(dòng)PS/2鍵盤

        單片機(jī)驅(qū)動(dòng)PS/2鍵盤

        作者: 時(shí)間:2016-11-09 來源:網(wǎng)絡(luò) 收藏
        PS/2簡介

        PS/2設(shè)備有主從之分,主設(shè)備采用Female插座,從設(shè)備采用Male插頭.現(xiàn)在廣泛使用的PS/2鍵盤鼠標(biāo)均在從設(shè)備方式下工作.PS/2接口的時(shí)鐘
        與數(shù)據(jù)線都是集電極開路結(jié)構(gòu),必須外接上拉電阻(一般上拉電阻設(shè)置在主設(shè)備中).主從設(shè)備之間數(shù)據(jù)通信采用雙向同步串行方式傳輸,時(shí)鐘信號(hào)由從設(shè)備產(chǎn)生.

        本文引用地址:http://www.104case.com/article/201611/317657.htm

        1.1 從設(shè)備到主設(shè)備的通信
        當(dāng)從設(shè)備向主設(shè)備發(fā)送數(shù)據(jù)時(shí),首先檢查時(shí)鐘線,以確認(rèn)時(shí)鐘線是否為高電平.如果是高電平,從設(shè)備就可以開始傳輸數(shù)據(jù);反之,從設(shè)備要等待獲得總線的控制權(quán),才能開始傳輸數(shù)據(jù).傳輸?shù)拿恳粠?1位組成,發(fā)送時(shí)序及每一位的含義如圖2所示.



        每一幀數(shù)據(jù)中開始位總是為0,數(shù)據(jù)校驗(yàn)采用奇校驗(yàn)方式,停止位始終為1.從設(shè)備到主設(shè)備通信時(shí),從設(shè)備總是在時(shí)鐘線為高時(shí)改變數(shù)據(jù)線狀態(tài),主設(shè)備在時(shí)鐘下降沿讀人數(shù)據(jù)線狀態(tài).

        1.2 主設(shè)備到從設(shè)備的通信
        主設(shè)備與從設(shè)備進(jìn)行通信時(shí),主設(shè)備首先將時(shí)鐘線和數(shù)據(jù)線設(shè)置為“請(qǐng)求發(fā)送”狀態(tài),具體方式為:首先下拉時(shí)鐘線至少100us抑制通信,然后下拉數(shù)據(jù)線“請(qǐng)求發(fā)送”,最后釋放時(shí)鐘線.在此過程中,從設(shè)備在不超過10us的間隔內(nèi)必須檢查這個(gè)狀態(tài),當(dāng)設(shè)備檢測到這個(gè)狀態(tài)時(shí),它將開始產(chǎn)生時(shí)鐘信號(hào).此時(shí)數(shù)據(jù)傳輸?shù)拿恳粠?2位構(gòu)成,其時(shí)序和每一位含義如圖3所示.



        與從設(shè)備到主設(shè)備通信相比,其每幀數(shù)據(jù)多了一個(gè)ACK位.這是從設(shè)備應(yīng)答接收到字節(jié)的應(yīng)答位,由從設(shè)備通過拉低數(shù)據(jù)線產(chǎn)生,應(yīng)答位ACK總
        是為0.主設(shè)備到從設(shè)備通信過程中,主設(shè)備總是在時(shí)鐘線為低電平時(shí)改變數(shù)據(jù)線的狀態(tài),從設(shè)備在時(shí)鐘上升沿讀人數(shù)據(jù)線狀態(tài).

        2.1 PS/2鍵盤的編碼
        目前,PC機(jī)使用的PS/2鍵盤都默認(rèn)采用第2套掃描碼集.掃描碼有兩種不同的類型:“通碼(make code)”和“斷碼(break code)”.當(dāng)一個(gè)鍵被按下或持續(xù)按住時(shí),鍵盤會(huì)將該鍵的通碼發(fā)送給主機(jī);而當(dāng)一個(gè)鍵被釋放時(shí),鍵盤會(huì)將該鍵的斷碼發(fā)送給主機(jī).根據(jù)鍵盤按鍵掃描碼的不同,可將按鍵分為3類:
        第1類按鍵 通碼為一個(gè)字節(jié),斷碼為0xF0+通碼形式.如A鍵,其通碼為0x1C;斷碼為0xF0 0x1C.
        第2類按鍵 通碼為兩字節(jié)0xE0+0xXX形式,斷碼為0xE0+0xF0+0xXX形式.如Right Ctrl鍵,其通碼為0xE0 0x14;斷碼為0xE0 0xF0 0x14.
        第3類特殊按鍵 有兩個(gè),Print Screen鍵,其通碼為0xE0 0x12 0xE0 0x7C;斷碼為0xE0 0xF0 0x7C 0xE0 0xF0 0x12.Pause鍵,其通碼為0xE1 0x14 0x77 0xE1 0xF0 0xl4 0xF0 0x77;斷碼為空.
        組合按鍵掃描碼的發(fā)送是按照按鍵發(fā)生的次序,如按下面順序按左Shift十A鍵:① 按下左Shift鍵;② 按下A鍵;③ 釋放A鍵;④ 釋放左Shift鍵,那么計(jì)算機(jī)上接收到的一串?dāng)?shù)據(jù)為0x12 0x1C 0xF0 0x1C 0xF0 0x12.


        2.2 PS/2鍵盤的命令集
        主機(jī)可通過向PS/2鍵盤發(fā)送命令對(duì)鍵盤進(jìn)行設(shè)置或者獲得鍵盤的狀態(tài)等操作.每發(fā)送一個(gè)字節(jié),主機(jī)都會(huì)從鍵盤獲得一個(gè)應(yīng)答0xFA(“重發(fā)
        resend”和“回應(yīng)echo”命令例外).驅(qū)動(dòng)程序在鍵盤初始化過程中所用的指令:0xED,主機(jī)在該命令后跟隨發(fā)送一個(gè)參數(shù)字節(jié),用于指示鍵盤上Num Lock,Caps Lock,Scroll Lock Led的狀態(tài);0xF3,主機(jī)在這條命令后跟隨發(fā)送一個(gè)字節(jié)參數(shù)定義鍵盤機(jī)打的速率和延時(shí);0xF4,用于當(dāng)主機(jī)發(fā)送0xF5禁止鍵盤后,重新使能鍵盤.

        連接電路圖:

        設(shè)計(jì)程序思路:

        采用狀態(tài)機(jī)思想來解碼一幀數(shù)據(jù),定義四個(gè)狀態(tài):PS_IDLE 、PS_START、PS_PARITY、PS_STOP

        PS_IDLE狀態(tài)接受起始碼,并判斷起始碼是否有效;PS_START狀態(tài)接受8位數(shù)據(jù);PS_PARITY狀態(tài)接受奇偶校驗(yàn)位;PS_STOP接受停止為,并奇偶校驗(yàn)數(shù)據(jù),同時(shí)處理shift按鍵以及斷碼問題;

        定義結(jié)構(gòu)體

        typedef struct ps2_frame
        {
        uchar state ;//狀態(tài)
        uchar data ;//數(shù)據(jù)
        uchar temp ;//用于移位
        uchar parity ;//奇偶校驗(yàn)位
        uchar count ;//1的位數(shù)由于奇偶校驗(yàn)
        uchar ready ;//一幀數(shù)據(jù)接受完畢
        uchar shift ;//shift鍵是否按下
        uchar down ;//按鍵是否彈起
        } ps2_frame ;

        下面是程序:

        頭文件


        #include "main.h"

        #define PS_DATA RA1
        #define PS_CLK RB0

        #define PS_IDLE 0x01
        #define PS_START 0x02
        //#define PS_DATA 0x03
        #define PS_PARITY 0x04
        #define PS_STOP 0x05
        //#define PS_ACK
        typedef struct ps2_frame
        {
        uchar state ;//狀態(tài)
        uchar data ;//數(shù)據(jù)
        uchar temp ;//用于移位
        uchar parity ;//奇偶校驗(yàn)位
        uchar count ;//1的位數(shù)由于奇偶校驗(yàn)
        uchar ready ;//一幀數(shù)據(jù)接受完畢
        uchar shift ;//shift鍵是否按下
        uchar down ;//按鍵是否彈起
        } ps2_frame ;

        void init_ps2() ;
        uchar ps_decoding(uchar data,uchar shift) ;
        #endif

        初始化和解碼子程序以及解碼表

        #include "ps2.h"

        const uchar unshifted[][2]=//shift鍵沒按下譯碼表
        {
        0x0e,`,
        0x15,q,
        0x16,1,
        0x1a,z,
        0x1b,s,
        0x1c,a,
        0x1d,w,
        0x1e,2,
        0x21,c,
        0x22,x,
        0x23,d,
        0x24,e,
        0x25,4,
        0x26,3,
        0x29, ,
        0x2a,v,
        0x2b,f,
        0x2c,t,
        0x2d,r,
        0x2e,5,
        0x31,n,
        0x32,b,
        0x33,h,
        0x34,g,
        0x35,y,
        0x36,6,
        0x39,,,
        0x3a,m,
        0x3b,j,
        0x3c,u,
        0x3d,7,
        0x3e,8,
        0x41,,,
        0x42,k,
        0x43,i,
        0x44,o,
        0x45,0,
        0x46,9,
        0x49,.,
        0x4a,/,
        0x4b,l,
        0x4c,;,
        0x4d,p,
        0x4e,-,
        0x52,/,
        0x54,[,
        0x55,=,
        0x5b,],
        0x5d,//,
        0x61,<,
        0x69,1,
        0x6b,4,
        0x6c,7,
        0x70,0,
        0x71,.,
        0x72,2,
        0x73,5,
        0x74,6,
        0x75,8,
        0x79,+,
        0x7a,3,
        0x7b,-,
        0x7c,*,
        0x7d,9,
        0,0
        };
        const uchar shifted[][2]= //shift鍵按下譯碼表
        {
        0x0e,~,
        0x15,Q,
        0x16,!,
        0x1a,Z,
        0x1b,S,
        0x1c,A,
        0x1d,W,
        0x1e,@,
        0x21,C,
        0x22,X,
        0x23,D,
        0x24,E,
        0x25,$,
        0x26,#,
        0x29, ,
        0x2a,V,
        0x2b,F,
        0x2c,T,
        0x2d,R,
        0x2e,%,
        0x31,N,
        0x32,B,
        0x33,H,
        0x34,G,
        0x35,Y,
        0x36,^,
        0x39,L,
        0x3a,M,
        0x3b,J,
        0x3c,U,
        0x3d,&,
        0x3e,*,
        0x41,<,
        0x42,K,
        0x43,I,
        0x44,O,
        0x45,),
        0x46,(,
        0x49,>,
        0x4a,?,
        0x4b,L,
        0x4c,:,
        0x4d,P,
        0x4e,_,
        0x52,",
        0x54,{,
        0x55,+,
        0x5b,},
        0x5d,|,
        0x61,>,
        0x69,1,
        0x6b,4,
        0x6c,7,
        0x70,0,
        0x71,.,
        0x72,2,
        0x73,5,
        0x74,6,
        0x75,8,
        0x79,+,
        0x7a,3,
        0x7b,-,
        0x7c,*,
        0x7d,9,
        0,0
        };

        void init_ps2()
        {
        ADCON1=0X07;//A口為普通IO
        TRISA1=1 ;
        INTCON=0 ;
        INTEDG=1 ;
        INTE=1 ;
        PEIE=1 ;
        GIE=1 ;
        }

        uchar ps_decoding(uchar data,uchar shift)
        {
        uchar temp ,i=0;
        if(shift)
        {
        while(i!=255)
        {
        if(shifted[i][0]==data)
        {
        temp=shifted[i][1] ;
        break ;
        }
        i++ ;
        }
        }
        else
        {
        while(i!=255)
        {
        if(unshifted[i][0]==data)
        {
        temp=unshifted[i][1] ;
        break ;
        }
        i++ ;
        }
        }
        return temp ;
        }

        主程序:


        #include "main.h"
        #include "t232.h"
        #include "ps2.h"

        ps2_frame ps_frame ;
        void interrupt main_int()
        {
        if(INTF)//下降沿觸發(fā)
        {
        GIE=0 ;
        INTF=0 ;
        switch(ps_frame.state)
        {
        case PS_IDLE :
        if(!PS_DATA)
        {
        ps_frame.ready=0 ;
        ps_frame.state=PS_START ;
        ps_frame.temp = 1 ;
        ps_frame.count = 0 ;
        ps_frame.data=0 ;
        }
        else
        ps_frame.state=PS_IDLE ;
        break ;
        case PS_START :
        if(PS_DATA)
        {
        ps_frame.data=ps_frame.data|ps_frame.temp ;
        ps_frame.count++ ;
        }
        ps_frame.temp=ps_frame.temp<<1 ;
        if(!ps_frame.temp)
        ps_frame.state=PS_PARITY ;
        break ;
        case PS_PARITY :
        ps_frame.parity=PS_DATA ;
        ps_frame.state=PS_STOP ;
        break ;
        case PS_STOP :
        if(PS_DATA)
        {
        if(ps_frame.parity)
        {
        if(ps_frame.count%2==0)
        ps_frame.ready=1 ;

        }
        else
        {
        if(ps_frame.count%2==1)
        ps_frame.ready=1 ;
        }
        switch(ps_frame.data)//處理通碼和斷碼
        {
        case 0xF0 :
        ps_frame.down=0 ;
        ps_frame.ready=0 ;
        break ;
        case 0x12 :
        if(!ps_frame.down)
        ps_frame.shift=0 ;
        else
        ps_frame.shift=1 ;
        ps_frame.ready=0 ;
        break ;
        case 0x59 :
        if(!ps_frame.down)
        ps_frame.shift=0 ;
        else
        ps_frame.shift=1 ;
        ps_frame.ready=0 ;
        break ;
        default :
        if(!ps_frame.down)
        {
        ps_frame.ready=0 ;
        ps_frame.down=1 ;
        }
        break ;
        }
        }

        ps_frame.state = PS_IDLE ;
        break ;
        default :
        break ;
        }
        }
        GIE=1 ;
        }

        void init_all()
        {
        init_232() ;
        init_ps2() ;
        ps_frame.state = PS_IDLE ;
        ps_frame.shift=0 ;
        ps_frame.down = 0 ;
        }
        void main()
        {
        const char str[]= "hello world !" ;
        uchar temp ;
        init_all() ;

        send_str(str) ;//測試串口
        while(1)
        {
        if(ps_frame.ready)
        {
        temp=ps_decoding(ps_frame.data,ps_frame.shift) ;

        put_char(temp) ;
        ps_frame.ready=0 ;
        }
        }
        }



        評(píng)論


        技術(shù)專區(qū)

        關(guān)閉
        主站蜘蛛池模板: 泗阳县| 杭锦旗| 治多县| 获嘉县| 方城县| 琼结县| 广安市| 任丘市| 荣成市| 额敏县| 辽阳县| 奉新县| 临夏县| 乌兰县| 灵寿县| 新和县| 肇州县| 鄂托克旗| 洪雅县| 图木舒克市| 蒙阴县| 灌阳县| 宁乡县| 汪清县| 扶余县| 镇宁| 涟源市| 淮阳县| 霍林郭勒市| 文水县| 益阳市| 密山市| 祥云县| 天台县| 柞水县| 南宫市| 孟州市| 腾冲县| 西和县| 沿河| 会昌县|