这儿所用的鼠标是PS/2协议的鼠标,测验鼠标为电脑一般光电鼠标(以下简称从机),有一个滚轮,三个按键等。所用编程言语为单片机C言语。用AT89S52作为接纳方(以下简称主机),首要担任:接纳从机送给主机的信息包并处理、用LCD1602作为显现屏并实时显现位移计数和按键信息,开始无论如何也无法驱动滚轮,经过努力总算完成了这一使命。如下图所示:
相对来说,主机的程序比较易写,可是,主机(AT89S52)处理这些信息仍是适当费劲,这时代码的履行功率就十分值得注意,假如设置鼠标作业在stream形式,即便AT89S52用24Mhz的晶振也会经常呈现数据处理异常。所以最好仍是让鼠标作业在remote形式,祥细请参阅《ps2技能参阅》。
我的初衷是将鼠标的数据作为完成2D定位的根据,也便是说,将鼠标当作一智能小车,
经过无线读取鼠标的位移计数来完成定位。惋惜所得的计数误差太大,比方,将鼠标从A点移到B点,再回到A点,此刻的计数值并不是开始在A点时的计数值。后来在论坛里发现有人从前也有过我这种主意,而他所用的是激光鼠标,相同也是计数误差过大而无法完成定位。
咱们先要知道现存的一共有两类鼠标,一类便是所谓的2D(二维)鼠标,它便是咱们平常用的那种没有滚轮的鼠标,因为这种鼠标在位移上只要X与Y两个方向,所以称之为2D(二维)鼠标;还有一类便是现在比较常见的3D(三维)鼠标,它们中心存在有一个滚轮,而这个滚轮会发生一个额定的Z位移量,因而,它在位移上有X、Y、Z三个方向,所以又称之为3D(三维)鼠标。下面,咱们就来看看这两类鼠标发给主机的数据包有什么不同。下面,咱们先来看看二维鼠标。
第1个数据包 |
位0:左键按下标志位,为1表明左键被按下。 位1:右键按下标志位,为1表明右键被按下。 位2:中键按下标志位,为1表明中键被按下。 位3:保存位,总是为1。 位4:X符号标志位,为1表明X位移量为负。 位5:Y符号标志位,为1表明Y位移量为负。 位6:X溢出标志位,为1表明X位移量溢出了。 位7:Y溢出标志位,为1表明Y位移量溢出了。 |
第2个数据包X位移量 | |
|
三维鼠标数据包中榜首个数据包每位的意义与二维鼠标数据包中榜首个数据包中每位意义完全相同,仅有不同的就在于它每次会多发送一个数据包,即第4个数据包,这个数据包包含了Z的位移量,同X、Y位移量相同的是,它们都是以补码表明的。不过与X及Y位移量不同的是,Z位移量是4位的,其间最高位(第四位)是符号位,因而,Z位移量的有用的规模为:-8~7。而X与Y的位移量是9位的,最高一位(第9位)是符号位,这个符号位在榜首个数据包中表明,故,X与Y的位移量的有用规模为:-256~255。
看到这儿,你或许有疑问了,体系是怎样来知道到我究竟应当接纳3个数据包仍是接纳4个数据包的呢?三维鼠标的规范是由微软拟定的,开始,这种三维的鼠标只作业在规范的PS/2形式下,假如你想让它作业在三维形式下,你需要用0xF3这个设置鼠标采样率的指令,按如下的次序进行操作:
1.设置鼠标采样率为200
2.设置鼠标采样率为100
3.设置鼠标采样率为80
这之后,假如你的鼠标是个三维鼠标,那么,它将转到三维形式下进行作业,这个时分,主机向它发送0xF2(取得鼠标类型ID)指令,你的作业在三维形式下的鼠标将向主机回来它的类型ID,但假如你的鼠标不支撑三维形式,即假如你的鼠标仅仅一个二维鼠标,它回来给主机的类型ID将是0,这样,主机就可以知道现在你用的鼠标是什么类型的鼠标,并由此知道应当承受3个仍是4个数据包了。本试验将只操作规范的二维鼠标,假如你有爱好,你可以对程序进行改动,以让它支撑三维鼠标。
下图是PS2鼠标位移数据包格局:
尽管不能完成定位,但最少我又学多了一种通信协议。以下是程序的一切源代码:
在”main.c”文件中:
#include
#include
#include”LCD1602.h”
#include
#define uchar unsigned char
#define sint signed int
#define uint unsigned int
#include”鼠标测验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符号标志位,为1表明x位移量为负
move_x-=(256-a1);//x坐标减
else
move_x+=a1;//x坐标加
if(fy) //位6:y符号标志位,为1表明y位移量为负
move_y-=(256-a2);//y坐标减
else
move_y+=a2;//y坐标加
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);//鼠标上电后在500ms左右就会发给主机0xaa和0x00
mouse_to_host();//假如没有接纳这两个字节,或许鼠标一次上电后,
mouse_to_host();//不能正常初始化成功或许可以用加长廷时来替代接纳
init_lcd();//初始化1602
delay100;//这个廷时适当重要,不然或许在1602中有乱码呈现
write_command(0x80);//定位光标在榜首行
write_bytes(“Initializing….”);
write_command(0x80+0x40);//定位光标在第二行
write_bytes(” Please wait! “);
while(init_mouse());//初始化鼠标
deal_recive_data();//处理初始化鼠标时回来给主机的部分数据,用以作调试
write_command(0x80);
write_bytes(deal_1);//显现初始化鼠标时回来给主机的部分数据,用以作调试
write_command(0x80+0x40);
write_bytes(deal_2);//显现初始化鼠标时回来给主机的部分数据,用以作调试
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形式中,主机每发送一个0xeb指令,从机
mouse_to_host();//将应对0xfa,之后便是数据包
a0=mouse_to_host();//榜首个数据包
fr=a0&0x02;//右键
fm=a0&0x04;//中键
fl=a0&0x01;//左键
fx=a0&0x10;//x的符号位
fy=a0&0x20;//y的符号位
a1=mouse_to_host();//第二个数据包 x位移量
a2=mouse_to_host();//第三个数据包 y位移量
a3=mouse_to_host();//第四个数据包 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个数据包
位0:左键按下标志位,为1表明左键被按下。
位1:右键按下标志位,为1表明右键被按下。
位2:中键按下标志位,为1表明中键被按下。
位3:保存位,总是为1。
位4:X符号标志位,为1表明X位移量为负。
位5:Y符号标志位,为1表明Y位移量为负。
位6:X溢出标志位,为1表明X位移量溢出了。
位7:Y溢出标志位,为1表明Y位移量溢出了。
三维鼠标数据包中榜首个数据包每位的意义与
二维鼠标数据包中榜首个数据包中每位意义完全相同,
仅有不同的就在于它每次会多发送一个数据包,
即第4个数据包,这个数据包包含了Z的位移量,
同X、Y位移量相同的是,它们都是以补码表明的。
不过与X及Y位移量不同的是,Z位移量是4位的,
其间最高位(第四位)是符号位,因而,Z位移量的有用的规模为:-8~7。
而X与Y的位移量是9位的,最高一位(第9位)是符号位,
这个符号位在榜首个数据包中表明,
故,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)//发送指令
{
RS=0;
P0=command;
LCDEN=1;
delay(3);
LCDEN=0;
RS=1;
}
void write_dat(char dat)//发送单个字节
{
RS=1;
P0=dat;
LCDEN=1;
delay(1);
LCDEN=0;
}
void init_lcd()//初始化1602
{
RW=0;
delay(5);
write_command(0x38);//设置作业方法
delay(5);
write_command(0x0f);//设置显现、光标和闪耀开、关
delay(5);
write_command(0x06);//设置光标、画面移动方法
delay(5);
write_command(0x80);//设置光标方位
delay(5);
}
void write_bytes(char *ch)//发送字符串
{
while(*ch)
write_dat(*ch++);
}
在”鼠标测验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;//寄存横坐标
signed int move_y=00000;//寄存纵坐标
signed int move_z=00000; //一共接纳到的字节总数
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]=” “; //用来寄存初始化鼠标时鼠标回来的信息
unsigned char idata deal_2[20]=” “;
uchar idata ret_ini_dat[18]=0; //直接寻址片内数据存储区,可拜访片内悉数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;//发送奇偶校验位
while(CLK!=1);
while(CLK!=0);
SDA=1;
while(CLK!=1);
while(CLK!=0);
ACK=SDA;//接纳应对位
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)//假如查验成功则回来接纳到的数据,不然回来0
{
recv=temp;
return temp;
}
return 0;
}
//用0xf0替代相邻的0xc8,0x03可使鼠标进入remote形式,默以为stream形式
uchar code num[15]={0xf3,0xc8,0xf3,0x64, //0xc8 200/sec,0x64 100/sec
0x50,0xc8,0xf2, //0x50 80/sec,0xf2读设备类型
0xf3,0xC8,0xf2,0XF0, //0x0a 10/sec,0xf2读设备类型,0x03滚轮分辨率8count/mm
0xe6,0xf3,0x28,0xf4};//0XE6 设置缩放比率为1:1,0×28 40/sec
//(0xe8,0xxx)设置滚轮分辨率,/0xe8,0x03/
/*
uchar code num[13]={0xf3,0xc8,0xf3,0x64,//
0xf3,0x50,0xf2,0xe8,0x03,,
0xe6,0xf3,0x28,0xf4};//
*///微软支撑第4 和第5 键的Intellimouse 的驱动
/*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); //复位指令,鼠标接连回来三个字节
ret_ini_dat[0]=mouse_to_host();//鼠标回来0xfa
ret_ini_dat[1]=mouse_to_host();//鼠标回来0xaa
ret_ini_dat[2]=mouse_to_host();//鼠标回来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()//处理初始化鼠标时回来给主机的部分数据,用以作调试
{//处理成十六进制和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;
}
}