在线看毛片网站电影-亚洲国产欧美日韩精品一区二区三区,国产欧美乱夫不卡无乱码,国产精品欧美久久久天天影视,精品一区二区三区视频在线观看,亚洲国产精品人成乱码天天看,日韩久久久一区,91精品国产91免费

<menu id="6qfwx"><li id="6qfwx"></li></menu>
    1. <menu id="6qfwx"><dl id="6qfwx"></dl></menu>

      <label id="6qfwx"><ol id="6qfwx"></ol></label><menu id="6qfwx"></menu><object id="6qfwx"><strike id="6qfwx"><noscript id="6qfwx"></noscript></strike></object>
        1. <center id="6qfwx"><dl id="6qfwx"></dl></center>

            新聞中心

            EEPW首頁 > 嵌入式系統(tǒng) > 設(shè)計應(yīng)用 > 如何用51單片機(jī)接收鼠標(biāo)的“三軸位移”與按鍵信息

            如何用51單片機(jī)接收鼠標(biāo)的“三軸位移”與按鍵信息

            作者: 時間:2016-11-17 來源:網(wǎng)絡(luò) 收藏
            這里所用的鼠標(biāo)是PS/2協(xié)議的鼠標(biāo),測試鼠標(biāo)為電腦普通光電鼠標(biāo)(以下簡稱從機(jī)),有一個滾輪,三個按鍵等。所用編程語言為單片機(jī)C語言。用AT89S52作為接收方(以下簡稱主機(jī)),主要負(fù)責(zé):接收從機(jī)送給主機(jī)的信息包并處理、用LCD1602作為顯示屏并實(shí)時顯示位移計數(shù)和按鍵信息,最初無論如何也無法驅(qū)動滾輪,經(jīng)過努力終于完成了這一任務(wù)。如下圖所示:

            相對來說,主機(jī)的程序比較易寫,但是,主機(jī)(AT89S52)處理這些信息還是相當(dāng)吃力,這時代碼的執(zhí)行效率就非常值得注意,如果設(shè)置鼠標(biāo)工作在stream模式,即使AT89S52用24Mhz的晶振也會經(jīng)常出現(xiàn)數(shù)據(jù)處理失常。所以最好還是讓鼠標(biāo)工作在remote模式,祥細(xì)請參考《ps2技術(shù)參考》。
            我的初衷是將鼠標(biāo)的數(shù)據(jù)作為實(shí)現(xiàn)2D定位的依據(jù),也就是說,將鼠標(biāo)當(dāng)作一智能小車,
            通過無線讀取鼠標(biāo)的位移計數(shù)來實(shí)現(xiàn)定位??上玫挠嫈?shù)偏差太大,比如,將鼠標(biāo)從A點(diǎn)移到B點(diǎn),再回到A點(diǎn),此時的計數(shù)值并不是當(dāng)初在A點(diǎn)時的計數(shù)值。后來在論壇里發(fā)現(xiàn)有人曾經(jīng)也有過我這種想法,而他所用的是激光鼠標(biāo),同樣也是計數(shù)偏差過大而無法實(shí)現(xiàn)定位。

            我們先要知道現(xiàn)存的總共有兩類鼠標(biāo),一類就是所謂的2D(二維)鼠標(biāo),它就是我們平常用的那種沒有滾輪的鼠標(biāo),由于這種鼠標(biāo)在位移上只有X與Y兩個方向,所以稱之為2D(二維)鼠標(biāo);還有一類就是現(xiàn)在比較常見的3D(三維)鼠標(biāo),它們中間存在有一個滾輪,而這個滾輪會產(chǎn)生一個額外的Z位移量,因此,它在位移上有X、Y、Z三個方向,所以又稱之為3D(三維)鼠標(biāo)。下面,我們就來看看這兩類鼠標(biāo)發(fā)給主機(jī)的數(shù)據(jù)包有什么不同。下面,我們先來看看二維鼠標(biāo)。
            第1個數(shù)據(jù)包


            位0:左鍵按下標(biāo)志位,為1表示左鍵被按下。
            位1:右鍵按下標(biāo)志位,為1表示右鍵被按下。
            位2:中鍵按下標(biāo)志位,為1表示中鍵被按下。
            位3:保留位,總是為1。
            位4:X符號標(biāo)志位,為1表示X位移量為負(fù)。
            位5:Y符號標(biāo)志位,為1表示Y位移量為負(fù)。
            位6:X溢出標(biāo)志位,為1表示X位移量溢出了。
            位7:Y溢出標(biāo)志位,為1表示Y位移量溢出了。
            第2個數(shù)據(jù)包X位移量


            第3個數(shù)據(jù)包Y位移量


            第4個數(shù)據(jù)包Z位移量
            三維鼠標(biāo)數(shù)據(jù)包中第一個數(shù)據(jù)包每位的含義與二維鼠標(biāo)數(shù)據(jù)包中第一個數(shù)據(jù)包中每位含義完全相同,唯一不同的就在于它每次會多發(fā)送一個數(shù)據(jù)包,即第4個數(shù)據(jù)包,這個數(shù)據(jù)包包含了Z的位移量,同X、Y位移量相同的是,它們都是以補(bǔ)碼表示的。不過與X及Y位移量不同的是,Z位移量是4位的,其中最高位(第四位)是符號位,因此,Z位移量的有效的范圍為:-8~7。而X與Y的位移量是9位的,最高一位(第9位)是符號位,這個符號位在第一個數(shù)據(jù)包中表示,故,X與Y的位移量的有效范圍為:-256~255。
            看到這里,你或許有疑問了,系統(tǒng)是怎么來知道到我到底應(yīng)當(dāng)接收3個數(shù)據(jù)包還是接收4個數(shù)據(jù)包的呢?三維鼠標(biāo)的標(biāo)準(zhǔn)是由微軟制定的,最初,這種三維的鼠標(biāo)只工作在標(biāo)準(zhǔn)的PS/2模式下,如果你想讓它工作在三維模式下,你需要用0xF3這個設(shè)置鼠標(biāo)采樣率的命令,按如下的順序進(jìn)行操作:
            1.設(shè)置鼠標(biāo)采樣率為200
            2.設(shè)置鼠標(biāo)采樣率為100
            3.設(shè)置鼠標(biāo)采樣率為80
            這之后,如果你的鼠標(biāo)是個三維鼠標(biāo),那么,它將轉(zhuǎn)到三維模式下進(jìn)行工作,這個時候,主機(jī)向它發(fā)送0xF2(獲得鼠標(biāo)類型ID)命令,你的工作在三維模式下的鼠標(biāo)將向主機(jī)返回它的類型ID,但如果你的鼠標(biāo)不支持三維模式,即如果你的鼠標(biāo)只是一個二維鼠標(biāo),它返回給主機(jī)的類型ID將是0,這樣,主機(jī)就能夠知道現(xiàn)在你用的鼠標(biāo)是什么類型的鼠標(biāo),并由此知道應(yīng)當(dāng)接受3個還是4個數(shù)據(jù)包了。本實(shí)驗(yàn)將只操作標(biāo)準(zhǔn)的二維鼠標(biāo),如果你有興趣,你可以對程序進(jìn)行改動,以讓它支持三維鼠標(biāo)。
            下圖是PS2鼠標(biāo)位移數(shù)據(jù)包格式:


            雖然不能實(shí)現(xiàn)定位,但最少我又學(xué)多了一種通信協(xié)議。以下是程序的所有源代碼:
            在"main.c"文件中:
            #include
            #include
            #include"LCD1602.h"
            #include
            #define uchar unsigned char
            #define sint signed int
            #define uint unsigned int
            #include"鼠標(biāo)測試2.h"
            void display()
            {
            signed int nx=move_x,ny=move_y,nz=move_z;
            uchar length=0;
            if(move_x<0) {nx=-move_x;xy[2]=-;}
            else
            xy[2]= ;
            for(length=7;length>2;length--)
            {
            xy[length]=nx%10+48;
            nx/=10;
            }
            if(move_y<0) {ny=-move_y;xy[10]=-;}
            else
            xy[10]= ;
            for(length=15;length>10;length--)
            {
            xy[length]=ny%10+48;
            ny/=10;
            }
            if(move_z<0){nz=-move_z;lmr[10]=-;}
            else
            lmr[10]= ;
            for(length=15;length>10;length--)
            {
            lmr[length]=nz%10+48;
            nz/=10;
            }
            write_command(0x80);
            write_bytes(xy);
            write_command(0x80+0x40);
            write_bytes(lmr);
            }
            uchar fx=0,fy=0,fz=0,a0=0,a1=0,a2=0,a3=0,fl=0,fm=0,fr=0;
            //uchar fxf=0,fyf=0;
            void deal_data()
            {
            if(fx) //位5:x符號標(biāo)志位,為1表示x位移量為負(fù)
            move_x-=(256-a1);//x坐標(biāo)減
            else
            move_x+=a1;//x坐標(biāo)加
            if(fy) //位6:y符號標(biāo)志位,為1表示y位移量為負(fù)
            move_y-=(256-a2);//y坐標(biāo)減
            else
            move_y+=a2;//y坐標(biāo)加
            if(fz)
            move_z-=(16-(a3&0x0f));
            else
            move_z+=(a3&0x07);
            if(fr)//如果點(diǎn)下右鍵
            {lmr[4]=R;return;}
            else if(fm)//如果點(diǎn)下中鍵
            {lmr[4]=M;return;}
            else if(fl)//如果點(diǎn)下左鍵
            {lmr[4]=L;return;}
            else
            {lmr[4]=N;return;}
            }
            void main()
            {
            SDA=1;CLK=1;
            delay(500);//鼠標(biāo)上電后在500ms左右就會發(fā)給主機(jī)0xaa和0x00
            mouse_to_host();//如果沒有接收這兩個字節(jié),可能鼠標(biāo)一次上電后,
            mouse_to_host();//不能正常初始化成功或者可以用加長廷時來代替接收
            init_lcd();//初始化1602
            delay100;//這個廷時相當(dāng)重要,否則可能在1602中有亂碼出現(xiàn)
            write_command(0x80);//定位光標(biāo)在第一行
            write_bytes("Initializing....");
            write_command(0x80+0x40);//定位光標(biāo)在第二行
            write_bytes(" Please wait! ");
            while(init_mouse());//初始化鼠標(biāo)
            deal_recive_data();//處理初始化鼠標(biāo)時返回給主機(jī)的部分?jǐn)?shù)據(jù),用以作調(diào)試
            write_command(0x80);
            write_bytes(deal_1);//顯示初始化鼠標(biāo)時返回給主機(jī)的部分?jǐn)?shù)據(jù),用以作調(diào)試
            write_command(0x80+0x40);
            write_bytes(deal_2);//顯示初始化鼠標(biāo)時返回給主機(jī)的部分?jǐn)?shù)據(jù),用以作調(diào)試
            write_command(0x80+0x40);
            delay(500);
            write_bytes(" Mouse Normal ");
            delay(500);

            write_command(0x80);
            write_bytes("Test PS/2 mouse.");
            write_command(0x80+0x40);
            write_bytes("Copyright-11-28-");
            while(1)
            {
            host_to_mouse(0xeb);//在remote模式中,主機(jī)每發(fā)送一個0xeb命令,從機(jī)
            mouse_to_host();//將應(yīng)答0xfa,之后就是數(shù)據(jù)包
            a0=mouse_to_host();//第一個數(shù)據(jù)包
            fr=a0&0x02;//右鍵
            fm=a0&0x04;//中鍵
            fl=a0&0x01;//左鍵
            fx=a0&0x10;//x的符號位
            fy=a0&0x20;//y的符號位

            a1=mouse_to_host();//第二個數(shù)據(jù)包 x位移量
            a2=mouse_to_host();//第三個數(shù)據(jù)包 y位移量
            a3=mouse_to_host();//第四個數(shù)據(jù)包 z位移量
            fz=a3&0x08;//z的符號位
            /*fxf=a0&0x40+0x30;
            fyf=a0&0x80+0x30;
            lmr[6]=fxf;
            lmr[7]=fyf;*/
            deal_data(); //將x,y,z,fl,fr,fm加入字符串中
            display();//加入之后再一次性刷新顯示
            }
            }
            /*
            第1個數(shù)據(jù)包
            位0:左鍵按下標(biāo)志位,為1表示左鍵被按下。
            位1:右鍵按下標(biāo)志位,為1表示右鍵被按下。
            位2:中鍵按下標(biāo)志位,為1表示中鍵被按下。
            位3:保留位,總是為1。
            位4:X符號標(biāo)志位,為1表示X位移量為負(fù)。
            位5:Y符號標(biāo)志位,為1表示Y位移量為負(fù)。
            位6:X溢出標(biāo)志位,為1表示X位移量溢出了。
            位7:Y溢出標(biāo)志位,為1表示Y位移量溢出了。
            三維鼠標(biāo)數(shù)據(jù)包中第一個數(shù)據(jù)包每位的含義與
            二維鼠標(biāo)數(shù)據(jù)包中第一個數(shù)據(jù)包中每位含義完全相同,
            唯一不同的就在于它每次會多發(fā)送一個數(shù)據(jù)包,
            即第4個數(shù)據(jù)包,這個數(shù)據(jù)包包含了Z的位移量,
            同X、Y位移量相同的是,它們都是以補(bǔ)碼表示的。
            不過與X及Y位移量不同的是,Z位移量是4位的,
            其中最高位(第四位)是符號位,因此,Z位移量的有效的范圍為:-8~7。
            而X與Y的位移量是9位的,最高一位(第9位)是符號位,
            這個符號位在第一個數(shù)據(jù)包中表示,
            故,X與Y的位移量的有效范圍為:-256~255。*/

            在"LCD1602.h"文件中:
            #define uint unsigned int
            #define uchar unsigned char
            sbit RS=P2^0; //寄存器選擇位,將RS位定義為P2.0引腳
            sbit RW=P2^1; //讀寫選擇位,將RW位定義為P2.1引腳
            sbit LCDEN=P2^2; //使能信號位,將E位定義為P2.2引腳
            void delay(uint z)
            {
            uint x,y;
            for(x=z;x>0;x--)
            for(y=110;y>0;y--);
            }
            void write_command(char command)//發(fā)送命令
            {
            RS=0;
            P0=command;
            LCDEN=1;
            delay(3);
            LCDEN=0;
            RS=1;
            }
            void write_dat(char dat)//發(fā)送單個字節(jié)
            {
            RS=1;
            P0=dat;
            LCDEN=1;
            delay(1);
            LCDEN=0;
            }
            void init_lcd()//初始化1602
            {
            RW=0;
            delay(5);
            write_command(0x38);//設(shè)置工作方式
            delay(5);
            write_command(0x0f);//設(shè)置顯示、光標(biāo)和閃爍開、關(guān)
            delay(5);
            write_command(0x06);//設(shè)置光標(biāo)、畫面移動方式
            delay(5);
            write_command(0x80);//設(shè)置光標(biāo)位置
            delay(5);
            }
            void write_bytes(char *ch)//發(fā)送字符串
            {
            while(*ch)
            write_dat(*ch++);
            }
            在"鼠標(biāo)測試2.h"文件中:
            #include
            #define delay10 {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}//延時10us
            #define delay100 {delay10 delay10 delay10 delay10 delay10 delay10 delay10 delay10 delay10 delay10;}
            sbit SDA=P3^2; //P3^3 //int0號中斷(本程序不用中斷接收方式)
            sbit CLK=P3^3;
            bit pp=0,ACK=0;
            uchar recv=0;
            signed int move_x=00000;//存放橫坐標(biāo)
            signed int move_y=00000;//存放縱坐標(biāo)
            signed int move_z=00000; //總共接收到的字節(jié)總數(shù)
            unsigned char data xy[16]= "x: y: "; //2 10
            unsigned char data lmr[16]= "key:N z: "; //5 10
            unsigned char idata deal_1[20]=" "; //用來存放初始化鼠標(biāo)時鼠標(biāo)返回的信息
            unsigned char idata deal_2[20]=" ";
            uchar idata ret_ini_dat[18]=0; //間接尋址片內(nèi)數(shù)據(jù)存儲區(qū),可訪問片內(nèi)全部RAM空間(256bytes)

            void host_to_mouse(uchar cmd)
            {
            uchar i;
            CLK=0;
            delay100;
            delay100;
            ACC=cmd;
            pp=~P;//獲得奇偶校驗(yàn)位
            SDA=0;
            CLK=1;
            for(i=0;i<8;i++)
            {
            while(CLK!=0);
            SDA=cmd&0x01;
            cmd>>=1;
            while(CLK!=1);
            }
            while(CLK!=0);
            SDA=pp;//發(fā)送奇偶校驗(yàn)位
            while(CLK!=1);
            while(CLK!=0);
            SDA=1;
            while(CLK!=1);
            while(CLK!=0);
            ACK=SDA;//接收應(yīng)答位
            while(CLK!=1);
            }
            uchar mouse_to_host()
            {
            uchar i,temp=0;
            while(CLK!=0);//等待低電平
            while(SDA!=0);
            while(CLK!=1);//等待高電平
            for(i=0;i<8;i++)
            {
            temp>>=1;
            while(CLK!=0);
            if(SDA==1)
            temp=0x80|temp;
            while(CLK!=1);
            }
            while(CLK!=0);
            pp=SDA;//接收奇偶校驗(yàn)位
            while(CLK!=1);
            while(CLK!=0);
            while(CLK!=1);
            ACC=temp;
            if(~P==pp)//如果檢驗(yàn)成功則返回接收到的數(shù)據(jù),否則返回0
            {
            recv=temp;
            return temp;
            }
            return 0;
            }
            //用0xf0代替相鄰的0xc8,0x03可使鼠標(biāo)進(jìn)入remote模式,默認(rèn)為stream模式
            uchar code num[15]={0xf3,0xc8,0xf3,0x64, //0xc8 200/sec,0x64 100/sec
            0x50,0xc8,0xf2, //0x50 80/sec,0xf2讀設(shè)備類型
            0xf3,0xC8,0xf2,0XF0, //0x0a 10/sec,0xf2讀設(shè)備類型,0x03滾輪分辨率8count/mm
            0xe6,0xf3,0x28,0xf4};//0XE6 設(shè)置縮放比率為1:1,0x28 40/sec
            //(0xe8,0xxx)設(shè)置滾輪分辨率,/0xe8,0x03/
            /*
            uchar code num[13]={0xf3,0xc8,0xf3,0x64,//
            0xf3,0x50,0xf2,0xe8,0x03,,
            0xe6,0xf3,0x28,0xf4};//
            *///微軟支持第4 和第5 鍵的Intellimouse 的驅(qū)動
            /*uchar code num[17]={0xf3,0xc8,0xf3,0x64,
            0xf3,0x50,0xf2,0xf3,
            0xc8,0xf3,0xc8,0xf3,
            0xc8,0xf3,0x50,0xf2,0x04};*/
            bit init_mouse()
            {
            uchar i=0;
            bit good=1;
            for(i=0;i<3;i++)
            {
            host_to_mouse(0xff); //復(fù)位命令,鼠標(biāo)連續(xù)返回三個字節(jié)
            ret_ini_dat[0]=mouse_to_host();//鼠標(biāo)返回0xfa
            ret_ini_dat[1]=mouse_to_host();//鼠標(biāo)返回0xaa
            ret_ini_dat[2]=mouse_to_host();//鼠標(biāo)返回0x00
            }
            for(i=0;i<15;i++)
            {
            host_to_mouse(num[i]);
            ret_ini_dat[i+3]=mouse_to_host();
            }
            return good=0;
            }
            void deal_recive_data()//處理初始化鼠標(biāo)時返回給主機(jī)的部分?jǐn)?shù)據(jù),用以作調(diào)試
            {//處理成十六進(jìn)制和ASCII碼
            uchar i=0,j=0,xx=0;
            for(i=0;i<10;i++)
            {
            xx=ret_ini_dat[i];
            if(((xx>>4)&0x0f)>=0x00 && ((xx>>4)&0x0f)<=0x09)
            deal_1[j++]=((xx>>4)&0x0f)+0x30;
            else
            deal_1[j++]=((xx>>4)&0x0f)+55;
            if((xx&0x0f)>=0x00 && (xx&0x0f)<=0x09)
            deal_1[j++]=(xx&0x0f)+0x30;
            else
            deal_1[j++]=(xx&0x0f)+55;
            }
            j=0;
            for(i=10;i<20;i++)
            {
            xx=ret_ini_dat[i];
            if(((xx>>4)&0x0f)>=0x00 && ((xx>>4)&0x0f)<=0x09)
            deal_2[j++]=((xx>>4)&0x0f)+0x30;
            else
            deal_2[j++]=((xx>>4)&0x0f)+55;
            if((xx&0x0f)>=0x00 && (xx&0x0f)<=0x09)
            deal_2[j++]=(xx&0x0f)+0x30;
            else
            deal_2[j++]=(xx&0x0f)+55;
            }
            }


            評論


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

            關(guān)閉