第三十八章 PS2鼠标实验
PS/2作为电脑的标准输入接口,用于鼠标键盘等设备。PS/2只需要一个简单的接口(2个IO口),就可以外扩鼠标、键盘等,是单片机理想的输入外扩方式。
ALIENTEK战舰STM32开发板也自带了一个PS/2接口,可以用来驱动标准的鼠标、键盘等外设,也可以用来驱动一些PS/2接口的小键盘,条码扫描枪等。在本章中,我们将向大家介绍,如何在ALIENTEK战舰STM32开发板上,通过PS/2接口来驱动电脑鼠标。本章分为如下几个部分:
38.1 PS/2简介
38.2 硬件设计
38.3 软件设计
38.4 下载验证
38.1 PS/2简介 PS/2是电脑上常见的接口之一,用于鼠标、键盘等设备。一般情况下,PS/2接口的鼠标为绿色,键盘为紫色。
PS/2接口是输入装置接口,而不是传输接口。所以PS2口根本没有传输速率的概念,只有扫描速率。在Windows环境下,ps/2鼠标的采样率默认为60次/秒,USB鼠标的采样率为120次/秒。较高的采样率理论上可以提高鼠标的移动精度。
物理上的PS/2端口可有2种,一种是5脚的,一种是六脚的。下面给出这两种PS/2接口的引脚定义图,如图38.1.1所示:
图38.1.1 PS/2引脚定义图
从图38.1.1可以看出,不管是5脚还是6脚的PS/2接头,都是有4根有用的线连接:时钟线、数据线、电源线、地线。PS/2设备的电源是5V的,而数据线和时钟线均是集电极开路的,这两根信号线都需要接一个上拉电阻(开发板上使用的是10K)。
PS/2 鼠标和键盘遵循一种双向同步串行协议,换句话说每次数据线上发送一位数据并且每在时钟线上发一个脉冲就被读入。键盘/鼠标可以发送数据到主机,而主机也可以发送数据到设备,但主机总是在总线上有优先权,它可以在任何时候抑制来自于键盘/鼠标的通讯,只要把时钟拉低即可。
从设备到主机的数据在时钟信号的下降沿被主机读取,而从主机到设备的数据在时钟信号的上升沿被设备读取。不论通信方向如何,时钟总是由设备产生的,最大的时钟频率为33Khz,大多数设备工作在10~20Khz。
鼠标键盘,采用的是一种每帧包含11/12位的串行协议,这些位的含义如表38.1.1所示:
表38.1.2 鼠标/键盘帧数据格式
表38.1.2中校验位的含义是:如果数据位中包含偶数个1,则校验位为1;如果数据位中包含奇数个1,则校验位为0。数据位中的1的个数加上校验位总为奇数(奇校验),用于数据侦错。当主机发送数据给键盘/鼠标的时候,设备会发送一个握手信号来应答数据已经被收到了,该位不会出现在设备到主机的通信中。
设备到主机的通信过程:
正常情况下数据线和时钟线都是高电平,当键盘/鼠标有数据要发送时,它先检测时钟线,确认时钟线是高电平。如果不是,则是主机抑制了通信,设备必须缓冲任何要发送的数据,直到重新获得总线的控制权(键盘有16 字节的缓冲区而鼠标的缓冲区仅存储最后一个要发送的数据包)。如果时钟线是高电平,设备就可以开始传送数据了。
设备到主机的数据在时钟线的下降沿被主机读入,如图38.1.2所示:
图38.1.2 设备到主机通信时序图
主机可以在设备发送数据的时候拉低时钟线来来放弃当前数据的传送。
主机到设备的通信过程:
主机到设备的通信与设备到主机的通信有点不同,因为PS/2的时钟总是由设备产生的,如果主机要发送数据,则它必须首先把时钟线和数据线设置为请求发送状态。请求发送状态通过如下过程实现:
1.拉低时钟线至少100us以抑制通信。
2.拉低数据线,以应用“请求发送”,然后释放时钟线。
设备在不超过10ms的时间内就会检测这个状态,当设备检测到这个状态后,它将开始产生时钟信号,并且在设备提供的时钟脉冲驱动下输入八个数据位和一个停止位。主机仅当时钟线为低的时候改变数据线,而数据在时钟脉冲的上升沿被锁存,这与发生在设备到主机通讯的过程中正好相反。
主机到设备的通信时序图如图38.1.3所示:
图38.1.3 主机到设备通信时序图
以上简单介绍了PS/2协议的通信过程,更多的介绍请参考《PS/2技术参考》一文。本章我们要驱动一个PS/2鼠标,所以接下来简单介绍一下PS/2鼠标的相关信息。
标准的PS/2鼠标支持下面的输入:X(左右)位移、Y(上下)位移、左键、中键和右键。但是我们目前用到鼠标大都还有滚轮,有的还有更多的按键,这就是所谓的Intellimouse。它支持5个鼠标按键和三个位移轴(左右、上下和滚轮)。
标准的鼠标有两个计数器保持位移的跟踪:X 位移计数器和Y 位移计数器。可存放9 位的2 进制补码,并且每个计数器都有相关的溢出标志。它们的内容连同三个鼠标按钮的状态一起以三字节移动数据包的形式发送给主机,位移计数器表示从最后一次位移数据包被送往主机后所发生的位移量。
标准PS/2鼠标发送唯一和按键信息以3字节的数据包格式发给主机,三个数据包的意义如图38.1.4所示:
图38.1.4 标准鼠标位移数据包格式
位移计数器是一个9位2的补码整数,其最高位作为符号位出现在位移数据包的第一个字节里。这些计数器在鼠标读取输入发现有位移时被更新。这些值是自从最后一次发送位移数据包给主机后位移的累计量(即最后一次包发给主机后位移计数器被复位位移计数器可表示的值的范围是-255 到+255)。如果超过了范围,相应的溢出位就会被置位,并在复位之前,计数器不会再增减。
而所谓的Intellimouse,因为多了2个按键和一个滚轮,所以Intellimouse的一个位移数据包由4个字节组成,如图38.1.5所示:
图38.1.5 Intellimouse鼠标位移数据包格式
Z0-Z3是2的补码,用于表示从上次数据报告以来滚轮的位移量。有效范围从-8 到+7,第四键如果按下,则4th Btn位被置位,如果没有按下,则4th Btn位为0。第五键也与此类似。
鼠标的介绍我们就简单的介绍到这里,详细的说明请参考光盘《PS/2技术参考》第三章 PS/2鼠标接口(第36页)。
38.2 硬件设计 本章实验功能简介:开机的时候先检测是否有鼠标接入,如果没有/检测错误,则提示错误代码。只有在检测到PS/2鼠标之后才开始后续操作,当检测到鼠标之后,就在LCD上显示鼠标位移数据包的内容,并转换为坐标值,在LCD上显示,如果有按键按下,则会提示按下的是哪个按键。同样我们也是用LED0来指示程序正在运行。
所要用到的硬件资源如下:
1) 指示灯DS0
2) TFTLCD模块
3) PS/2鼠标
本章需要用到一个PS/2接口的鼠标,大家得自备一个。下面我来看一看开发板上的PS/2接口与STM32的连接电路,如图38.2.1所示:
图38.2.1 PS/2接口与STM32的连接电路图
可以看到,PS/2接口与STM32的连接仅仅2个IO口,其中PS_CLK连接在PC11上面,而PS_DAT则连接在PC10上面,这两个口和SDIO_D2和SDIO_D3复用了,所以在SDIO使用的时候,就不能使用PS/2设备了,这个在使用的时候大家要注意一下。
38.3 软件设计 打开上一章的工程,首先在HARDWARE文件夹下新建PS2和MOUSE两个文件夹。在PS2文件夹里面新建ps2.c和ps2.h两个文件。然后在MOUSE文件夹下新建mouse.c和mouse.h两个文件。并将这个两个文件夹加入头文件包含路径。
打开ps2.c,输入如下代码:
#include "ps2.h"
#include "usart.h"
//PS2产生的时钟频率在10~20Khz(最大33K)
//高/低电平的持续时间为25~50us之间.
//PS2_Status当前状态标志
//[7]:接收到一次数据;[6]:校验错误;[5:4]:当前工作的模式;[3:0]:收到的数据长度;
u8 PS2_Status=CMDMODE; //默认为命令模式
u8 PS2_DATA_BUF[16]; //ps2数据缓存区
//位计数器
u8 BIT_Count=0;
//中断15~10处理函数
//每11个bit,为接收1个字节
//每接收完一个包(11位)后,设备至少会等待50ms再发送下一个包
//只做了鼠标部分,键盘部分暂时未加入
void EXTI15_10_IRQHandler(void)
{
static u8 tempdata=0;
static u8 parity=0;
if(EXTI->PR&(1<<11))//中断11产生了相应的中断
{
EXTI->PR=1<<11; //清除LINE11上的中断标志位
if(BIT_Count==0) {parity=0; tempdata=0;}
BIT_Count++;
if(BIT_Count>1&&BIT_Count<10)//这里获得数据
{
tempdata>>=1;
if(PS2_SDA)
{
tempdata|=0x80;
parity++;//记录1的个数
}
}else if(BIT_Count==10)//得到校验位
{
if(PS2_SDA)parity|=0x80;//校验位为1
}
if(BIT_Count==11)//接收到1个字节的数据了
{
BIT_Count=parity&0x7f;//取得1的个数
if(((BIT_Count%2==0)&&(parity&0x80))||((BIT_Count%2==1)&&(parity&0x80)
==0)) //奇偶校验OK
{
//PS2_Status|=1<<7;//标记得到数据
BIT_Count=PS2_Status&0x0f;
PS2_DATA_BUF[BIT_Count]=tempdata;//保存数据
if(BIT_Count<15)PS2_Status++; //数据长度加1
BIT_Count=PS2_Status&0x30; //得到模式
switch(BIT_Count)
{
case CMDMODE://命令模式下,每收到一个字节都会产生接收完成
PS2_Dis_Data_Report();//禁止数据传输
PS2_Status|=1<<7; //标记得到数据
break;
case KEYBOARD: break;
case MOUSE:
if(MOUSE_ID==0)//标准鼠标,3个字节
{
if((PS2_Status&0x0f)==3)
{
PS2_Status|=1<<7;//标记得到数据
PS2_Dis_Data_Report();//禁止数据传输
}
}else if(MOUSE_ID==3)//扩展鼠标,4个字节
{
if((PS2_Status&0x0f)==4)
{
PS2_Status|=1<<7;//标记得到数据
PS2_Dis_Data_Report();//禁止数据传输
}
}
break;
}
}else
{
PS2_Status|=1<<6;//标记校验错误
PS2_Status&=0xf0;//清除接收数据计数器
}
BIT_Count=0;
}
}
}
//禁止数据传输
//把时钟线拉低,禁止数据传输
void PS2_Dis_Data_Report(void)
{
PS2_Set_Int(0); //关闭中断
PS2_SET_SCL_OUT();//设置SCL为输出
PS2_SCL_OUT=0; //抑制传输
}
//使能数据传输
//释放时钟线
void PS2_En_Data_Report(void)
{
PS2_SET_SCL_IN(); //设置SCL为输入
PS2_SET_SDA_IN(); //SDA IN
PS2_SCL_OUT=1; //上拉
PS2_SDA_OUT=1;
PS2_Set_Int(1); //开启中断
}
//PS2中断屏蔽设置
//en:1,开启;0,关闭;
void PS2_Set_Int(u8 en)
{
EXTI->PR=1<<11; //清除LINE11上的中断标志位
if(en)EXTI->IMR|=1<<11;//不屏蔽line11上的中断
else EXTI->IMR&=~(1<<11);//屏蔽line11上的中断
}
//等待PS2时钟线sta状态改变
//sta:1,等待变为1;0,等待变为0;
//返回值:0,时钟线变成了sta;1,超时溢出;
u8 Wait_PS2_Scl(u8 sta)
{
u16 t=0;
sta=!sta;
while(PS2_SCL==sta)
{
delay_us(1); t++;
if(t>16000)return 1;//时间溢出 (设备会在10ms内检测这个状态)
}
return 0;//被拉低了
}
//在发送命令/数据之后,等待设备应带,该函数用来获取应答
//返回得到的值
//返回0,且PS2_Status.6=1,则产生了错误
u8 PS2_Get_Byte(void)
{
u16 t=0;
u8 temp=0;
while(1)//最大等待55ms
{
t++; delay_us(10);
if(PS2_Status&0x80)//得到了一次数据
{
temp=PS2_DATA_BUF[PS2_Status&0x0f-1];
PS2_Status&=0x70;//清除计数器,接收到数据标记
break;
}else if(t>5500||PS2_Status&0x40)break;//超时溢出/接收错误
}
PS2_En_Data_Report();//使能数据传输
return temp;
}
//发送一个命令到PS2.
//返回值:0,无错误,其他,错误代码
u8 PS2_Send_Cmd(u8 cmd)
{
u8 i;
u8 high=0;//记录1的个数
PS2_Set_Int(0); //屏蔽中断
PS2_SET_SCL_OUT();//设置SCL为输出
PS2_SET_SDA_OUT();//SDA OUT
PS2_SCL_OUT=0;//拉低时钟线
delay_us(120);//保持至少100us
PS2_SDA_OUT=0;//拉低数据线
delay_us(10);
PS2_SET_SCL_IN();//释放时钟线,这里PS2设备得到第一个位,开始位
PS2_SCL_OUT=1;
if(Wait_PS2_Scl(0)==0)//等待时钟拉低
{
for(i=0;i<8;i++)
{
if(cmd&0x01){ PS2_SDA_OUT=1; high++;}
else PS2_SDA_OUT=0;
cmd>>=1;
//这些地方没有检测错误,因为这些地方不会产生死循环
Wait_PS2_Scl(1);//等待时钟拉高 发送8个位
Wait_PS2_Scl(0);//等待时钟拉低
}
if((high%2)==0)PS2_SDA_OUT=1;//发送校验位 10
else PS2_SDA_OUT=0;
Wait_PS2_Scl(1); //等待时钟拉高 10位
Wait_PS2_Scl(0); //等待时钟拉低
PS2_SDA_OUT=1; //发送停止位 11
Wait_PS2_Scl(1);//等待时钟拉高 11位
PS2_SET_SDA_IN();//SDA in
Wait_PS2_Scl(0);//等待时钟拉低
if(PS2_SDA==0)Wait_PS2_Scl(1);//等待时钟拉高 12位
else {PS2_En_Data_Report();return 1;}//发送失败
}else{PS2_En_Data_Report();return 2; }//发送失败
PS2_En_Data_Report();
return 0; //发送成功
}
//PS2初始化
void PS2_Init(void)
{
RCC->APB2ENR|=1<<4; //使能PORTC时钟
GPIOC->CRH&=0XFFFF00FF;//PC10,11设置成输入
GPIOC->CRH|=0X00008800;//PC10,11设置成输出
GPIOC->ODR|=3<<10;
Ex_NVIC_Config(GPIO_C,11,FTIR);//将line11映射到PC.11,下降沿触发.
MY_NVIC_Init(1,2,EXTI15_10_IRQChannel,2);//分配到第二组,抢占2,响应3
}
该部分为底层的PS/2协议驱动程序,采用中断接收PS/2设备产生的时钟信号,然后解析。
保存ps2.c文件,并加入到HARDWARE组下,然后打开ps2.h,在该文件里面输入如下代码:
#ifndef __PS2_H
#define __PS2_H
#include "delay.h"
#include "sys.h"
#define PS2_SCL PCin(11) //PC11
#define PS2_SDA PCin(10) //PC10
//PS2输出
#define PS2_SCL_OUT PCout(11) //PC11
#define PS2_SDA_OUT PCout(10) //PC10
//设置PS2_SCL输入输出状态.
#define PS2_SET_SCL_IN() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=0X00008000;}
#define PS2_SET_SCL_OUT() {GPIOC->CRH&=0XFFFF0FFF;GPIOC->CRH|=0X00003000;}
//设置PS2_SDA输入输出状态.
#define PS2_SET_SDA_IN() {GPIOC->CRH&=0XFFFFF0FF;GPIOC->CRH|=0X00000800;}
#define PS2_SET_SDA_OUT() {GPIOC->CRH&=0XFFFFF0FF;GPIOC->CRH|=0X00000300;}
#define MOUSE 0X20 //鼠标模式
#define KEYBOARD 0X10 //键盘模式
#define CMDMODE 0X00 //发送命令
//PS2_Status当前状态标志
//[5:4]:当前工作的模式;[7]:接收到一次数据
//[6]:校验错误;[3:0]:收到的数据长度;
extern u8 PS2_Status; //定义为命令模式
extern u8 PS2_DATA_BUF[16]; //ps2数据缓存区
extern u8 MOUSE_ID;
void PS2_Init(void);
u8 PS2_Send_Cmd(u8 cmd);
void PS2_Set_Int(u8 en);
u8 PS2_Get_Byte(void);
void PS2_En_Data_Report(void);
void PS2_Dis_Data_Report(void);
#endif
保存此部分代码,然后打开mouse.c,输入如下代码:
#include "mouse.h"
#include "usart.h"
#include "lcd.h"
u8 MOUSE_ID;//用来标记鼠标ID
PS2_Mouse MouseX;
//处理MOUSE的数据
void Mouse_Data_Pro(void)
{
MouseX.x_pos+=(signed char)PS2_DATA_BUF[1];
MouseX.y_pos+=(signed char)PS2_DATA_BUF[2];
MouseX.z_pos+=(signed char)PS2_DATA_BUF[3];
MouseX.bt_mask=PS2_DATA_BUF[0]&0X07;//取出掩码
}
//初始化鼠标
//返回:0,初始化成功
//其他:错误代码
//CHECK OK 2010/5/2
u8 Init_Mouse(void)
{
u8 t;
PS2_Init();
delay_ms(800); //等待上电复位完成
PS2_Status=CMDMODE; //进入命令模式
t=PS2_Send_Cmd(PS_RESET); //复位鼠标
if(t!=0)return 1;
t=PS2_Get_Byte();
if(t!=0XFA)return 2;
t=0;
while((PS2_Status&0x80)==0) //等待复位完毕
{
t++;
delay_ms(10);
if(t>50)return 3;
}
PS2_Get_Byte();//得到0XAA
PS2_Get_Byte();//得到ID 0X00
//进入滚轮模式的特殊初始化序列
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 4; //传输失败
PS2_Send_Cmd(0XC8); //采样率200
if(PS2_Get_Byte()!=0XFA)return 5; //传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 6; //传输失败
PS2_Send_Cmd(0X64); //采样率100
if(PS2_Get_Byte()!=0XFA)return 7; //传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 8; //传输失败
PS2_Send_Cmd(0X50); //采样率80
if(PS2_Get_Byte()!=0XFA)return 9; //传输失败
//序列完成
PS2_Send_Cmd(GET_DEVICE_ID); //读取ID
if(PS2_Get_Byte()!=0XFA)return 10; //传输失败
MOUSE_ID=PS2_Get_Byte(); /得到MOUSE ID
PS2_Send_Cmd(SET_SAMPLE_RATE); //再次进入设置采样率
if(PS2_Get_Byte()!=0XFA)return 11; //传输失败
PS2_Send_Cmd(0X0A); //采样率10
if(PS2_Get_Byte()!=0XFA)return 12; //传输失败
PS2_Send_Cmd(GET_DEVICE_ID); //读取ID
if(PS2_Get_Byte()!=0XFA)return 13; //传输失败
MOUSE_ID=PS2_Get_Byte(); //得到MOUSE ID
PS2_Send_Cmd(SET_RESOLUTION); //设置分辨率
if(PS2_Get_Byte()!=0XFA)return 14; //传输失败
PS2_Send_Cmd(0X03); //8点/mm
if(PS2_Get_Byte()!=0XFA)return 15; //传输失败
PS2_Send_Cmd(SET_SCALING11); //设置缩放比率为1:1
if(PS2_Get_Byte()!=0XFA)return 16; //传输失败
PS2_Send_Cmd(SET_SAMPLE_RATE); //设置采样率
if(PS2_Get_Byte()!=0XFA)return 17; //传输失败
PS2_Send_Cmd(0X28);//40
if(PS2_Get_Byte()!=0XFA)return 18; //传输失败
PS2_Send_Cmd(EN_DATA_REPORT); //使能数据报告
if(PS2_Get_Byte()!=0XFA)return 19; //传输失败
PS2_Status=MOUSE;//进入鼠标模式
return 0;//无错误,初始化成功
}
该部分仅2个函数,Init_Mouse用于初始化鼠标,让鼠标进入Intellimouse模式,里面的初始化序列完全按照《PS/2技术参考》里面介绍的来设计。另外一个函数就是将收到的数据简单处理一下。保存mouse.c,然后打开mouse.h,输入如下内容:
#ifndef __MOUSE_H
#define __MOUSE_H
#include "ps2.h"
//HOST->DEVICE的命令集
#define PS_RESET 0XFF //复位命令 回应0XFA
……//省略部分指令
//#define RESEND 0XFE //再次发送
//鼠标结构体
typedef struct
{
short x_pos;//横坐标
short y_pos;//纵坐标
short z_pos;//滚轮坐标
u8 bt_mask;//按键标识,bit2中间键;bit1,右键;bit0,左键
} PS2_Mouse;
extern PS2_Mouse MouseX;
extern u8 MOUSE_ID;//鼠标ID,0X00,表示标准鼠标(3字节);0X03表示扩展鼠标(4字节)
u8 Init_Mouse(void);
void Mouse_Data_Pro(void);
#endif
该部分代码定义了一个鼠标结构体,用于存放鼠标相关的数据,并对鼠标的相关命令进行了宏定义(部分被省略),保存此部分代码。最后,打开test.c文件,修改代码如下:
//显示鼠标的坐标值
//x,y:在LCD上显示的坐标位置
//pos:坐标值
void Mouse_Show_Pos(u16 x,u16 y,short pos)
{
if(pos<0)
{
LCD_ShowChar(x,y,'-',16,0); //显示负号
pos=-pos; //转为正数
}else LCD_ShowChar(x,y,' ',16,0); //去掉负号
LCD_ShowNum(x+8,y,pos,5,16); //显示值
}
int main(void)
{
u8 t; u8 errcnt=0;
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,9600); //串口初始化为9600
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
usmart_dev.init(72); //初始化USMART
KEY_Init(); //按键初始化
NRF24L01_Init(); //初始化NRF24L01
POINT_COLOR=RED;//设置字体为红色
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"Mouse TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2012/9/13");
while(Init_Mouse()) //检查鼠标是否在位.
{
LCD_ShowString(60,130,200,16,16,"Mouse Error"); delay_ms(400);
LCD_Fill(60,130,239,130+16,WHITE); delay_ms(100);
}
LCD_ShowString(60,130,200,16,16,"Mouse OK");
LCD_ShowString(60,150,200,16,16,"Mouse ID:");
LCD_ShowNum(132,150,MOUSE_ID,3,16);//填充模式
POINT_COLOR=BLUE;
LCD_ShowString(30,170,200,16,16,"BUF[0]:");
LCD_ShowString(30,186,200,16,16,"BUF[1]:");
LCD_ShowString(30,202,200,16,16,"BUF[2]:");
if(MOUSE_ID==3)LCD_ShowString(30,218,200,16,16,"BUF[3]:");
LCD_ShowString(90+30,170,200,16,16,"X POS:");
LCD_ShowString(90+30,186,200,16,16,"Y POS:");
LCD_ShowString(90+30,202,200,16,16,"Z POS:");
if(MOUSE_ID==3)LCD_ShowString(90+30,218,200,16,16,"BUTTON:");
t=0;
while(1)
{
if(PS2_Status&0x80)//得到了一次数据
{
LCD_ShowNum(56+30,170,PS2_DATA_BUF[0],3,16);//填充模式
LCD_ShowNum(56+30,186,PS2_DATA_BUF[1],3,16);//填充模式
LCD_ShowNum(56+30,202,PS2_DATA_BUF[2],3,16);//填充模式
if(MOUSE_ID==3)LCD_ShowNum(56+30,218,PS2_DATA_BUF[3],3,16);
//填充模式
Mouse_Data_Pro();//处理数据
Mouse_Show_Pos(146+30,170,MouseX.x_pos); //X坐标
Mouse_Show_Pos(146+30,186,MouseX.y_pos); //Y坐标
if(MOUSE_ID==3)Mouse_Show_Pos(146+30,202,MouseX.z_pos); //滚轮位置
if(MouseX.bt_mask&0x01)LCD_ShowString(146+30,218,200,16,16,"LEFT");
else LCD_ShowString(146+30,218,200,16,16," ");
if(MouseX.bt_mask&0x02)LCD_ShowString(146+30,234,200,16,16,"RIGHT");
else LCD_ShowString(146+30,234,200,16,16," ");
if(MouseX.bt_mask&0x04)LCD_ShowString(146+30,250,200,16,16,
"MIDDLE");
else LCD_ShowString(146+30,250,200,16,16," ");
PS2_Status=MOUSE;
PS2_En_Data_Report();//使能数据报告
}else if(PS2_Status&0x40)
{
errcnt++; PS2_Status=MOUSE;
LCD_ShowNum(86+30,234,errcnt,3,16);//填充模式
}
t++;
delay_ms(1);
if(t==200){ t=0; LED0=!LED0;}
}
}
此部分,除了main函数,我们还编写了Mouse_Show_Pos函数,用于在指定位置显示鼠标坐标值,并支持负数显示,通过该函数,可以方便我们显示鼠标坐标数据。至此,PS/2鼠标实验的软件设计部分就结束了。
38.4 下载验证 在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,可以看到LCD显示如图38.4.1所示内容(假定PS/2鼠标已经接上,并且初始化成功):
图38.4.1 PS/2鼠标实验显示结果
[
本帖最后由 正点原子 于 2013-3-27 23:04 编辑 ]