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

          新聞中心

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

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

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

          相對來說,主機的程序比較易寫,但是,主機(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ù)作為實現(xiàn)2D定位的依據(jù),也就是說,將鼠標(biāo)當(dāng)作一智能小車,
          通過無線讀取鼠標(biāo)的位移計數(shù)來實現(xiàn)定位。可惜所得的計數(shù)偏差太大,比如,將鼠標(biāo)從A點移到B點,再回到A點,此時的計數(shù)值并不是當(dāng)初在A點時的計數(shù)值。后來在論壇里發(fā)現(xiàn)有人曾經(jīng)也有過我這種想法,而他所用的是激光鼠標(biāo),同樣也是計數(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ā)給主機的數(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位移量相同的是,它們都是以補碼表示的。不過與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)行工作,這個時候,主機向它發(fā)送0xF2(獲得鼠標(biāo)類型ID)命令,你的工作在三維模式下的鼠標(biāo)將向主機返回它的類型ID,但如果你的鼠標(biāo)不支持三維模式,即如果你的鼠標(biāo)只是一個二維鼠標(biāo),它返回給主機的類型ID將是0,這樣,主機就能夠知道現(xiàn)在你用的鼠標(biāo)是什么類型的鼠標(biāo),并由此知道應(yīng)當(dāng)接受3個還是4個數(shù)據(jù)包了。本實驗將只操作標(biāo)準(zhǔn)的二維鼠標(biāo),如果你有興趣,你可以對程序進(jìn)行改動,以讓它支持三維鼠標(biāo)。
          下圖是PS2鼠標(biāo)位移數(shù)據(jù)包格式:


          雖然不能實現(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)//如果點下右鍵
          {lmr[4]=R;return;}
          else if(fm)//如果點下中鍵
          {lmr[4]=M;return;}
          else if(fl)//如果點下左鍵
          {lmr[4]=L;return;}
          else
          {lmr[4]=N;return;}
          }
          void main()
          {
          SDA=1;CLK=1;
          delay(500);//鼠標(biāo)上電后在500ms左右就會發(fā)給主機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ǐn)?shù)據(jù),用以作調(diào)試
          write_command(0x80);
          write_bytes(deal_1);//顯示初始化鼠標(biāo)時返回給主機的部分?jǐn)?shù)據(jù),用以作調(diào)試
          write_command(0x80+0x40);
          write_bytes(deal_2);//顯示初始化鼠標(biāo)時返回給主機的部分?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模式中,主機每發(fā)送一個0xeb命令,從機
          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位移量相同的是,它們都是以補碼表示的。
          不過與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;//獲得奇偶校驗位
          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ā)送奇偶校驗位
          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;//接收奇偶校驗位
          while(CLK!=1);
          while(CLK!=0);
          while(CLK!=1);
          ACC=temp;
          if(~P==pp)//如果檢驗成功則返回接收到的數(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ǐ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)閉
          看屁屁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); })();