第四十五课 PS\2之二 鼠标+LCD1602显示
主机到设备的通讯<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
被发送的包有点不同于主机到设备通讯过程,首先PS/2设备总是产生时钟信号。如果主机要发送数据它必须首先把时钟和数据线设置为请求发送状态,如下:
通过下拉时钟线至少100微秒来抑制通讯通过下拉数据线来应用请求发送然后释放时钟设备应该在不超过10毫秒的间隔内就要检查这个状态,当设备检测到这个状态它将开始产生时钟信号并且时钟脉冲标记下输入八个数据位和一个停止位,主机仅当时钟线为低的时候改变数据线,而数据在时钟脉冲的上升沿被锁存,这在发生在设备到主机通讯的过程中正好相反,在停止位发送后设备要应答接收到的字节就把数据线拉低并产生最后一个时钟脉冲,如果主机在第11个时钟脉冲后不释放数据线,设备将继续产生时钟脉冲直到数据线被释放,然后设备将产生一个错误,主机可以在第11个时钟脉冲应答位前中止一次传送,只要下拉时钟线至少100微秒,要使得这个过程易于理解主机必须按下面的步骤发送数据到PS/2设备
1)把时钟线拉低至少100微秒
2)把数据线拉低
3)释放数据线
4)等待设备把时钟线拉低
5)设置/复位数据线发送第一个数据位
6)等待设备把时钟拉高
7)等待设备把时钟拉低
8)重复5-7步 发送剩下的7个数据位和校验位
9)释放数据线
10)等待设备把数据线拉低
11)等待设备把时钟线拉低
12)等待设备释放数据线和时钟线
设备产生的信号注意应答位时序的改变,数据改变发生在时钟线为高的时候,不同于其它11位是当它为低的时候,在主机最初把数据线拉低后,设备开始产生时钟脉冲的时间必须步大于15ms,数据包被发送的时间必须不大于2ms,如果这两个条件不满足,主机将产生一个错误,在包收到后主机为了处理数据立刻把时钟线拉低来抑制通讯,如果主机发送的命令要求有一个回应,这个回应必须在主机释放时钟线后20ms之内被收到,如果没有收到,则主机产生一个错误,在设备到主机通讯的情况中时钟改变后的5微秒内不应该发生数据改变的情况,如果你要仿真一个鼠标或键盘我推荐你按如下的过程从主机读入数据,在你的主程序中至少每10毫秒检测数据线是否为低,如果数据线已被主机拉低则从主机读取一个字节
1)等待时钟线为高
2)数据线仍然为低吗
不有错误发生放弃
3)读入8个数据位\在读入这些位后
4)读入校验位>测试时钟线数否被主机拉低
5)读入停止位/这就意味着放弃这次传送
6)数据线仍旧为0吗
是保持时钟直到数据1然后产生一个错误
7)输出应答位
8)检查校验位
如果校验位不正确则产生一个错误
9)延迟45微秒给主机时间抑制下次的传送
按如下次序读取每位8个数据位检验位和停止位
1)延迟20微秒
2)把时钟拉低
3)延迟40微秒
4)释放时钟
5)延迟20微秒
7)读数据线
按如下次序发送应答位
1)延迟15微秒
2)把数据线拉低
3)延迟5微秒
4)把时钟线拉低
5)延迟40微秒
6)释放时钟线
7)延迟5微秒
8)释放数据线
设备到主机的通信
标准的PS/2鼠标发送位移和按键信息给主机采用如下的3字节数据包格式:
位移计数器是一个9位2的补码整数。它的最高位作为符号位出现在位移数据包的第一个字节里。这些计数器在鼠标读取输入发现有位移时被更新。这些值是自从最后一次发送位移数据包给主机后位移的累计量(即最后一次包发给主机后位移计数器被复位)。位移计数器可表示的值的范围是-255到+255,如果超过了范围,相应的溢出位就被设置,并且在复位前,计数器不会增减。正如我前面提及的一旦位移数据包成功地发送给主机,位移计数器就会复位,同样鼠标在收到主机不是Resend 0xFE命令外的其他命令,计数器也会复位。
程序讲解:
头文件PS_mouse.H --------------------------------------*/
//File: PS/2-Mouse头文件
//Date: 2009-4-15
//Time: 20:29
/*--------------------------------------------------------------*/
//防止被重复定义引用
#ifndef __PS_mouse_H__
#define __PS_mouse_H__
/*--------------------------------------------------------------*/
sbit mouse_sda = P3^2; //鼠标数据线
sbit mouse_clk = P3^3; //鼠标时钟线
/*--------------------------------------------------------------*/
//数据定义
unsigned char n, m; //循环变量
unsigned int mouse_word; //接收字16bits
unsigned char mouse_data[3];//接收字节缓冲区
unsigned int move_x; //横坐标
unsigned int move_y; //纵坐标
bit mouse_left; //左键
bit mouse_right; //右键
bit mouse_middle; //中键
/*--------------------------------------------------------------·*/
//函数声明
void INT1_init(void); //外部中断INT0初始化
void delay120us(void); //延时120us 函数定义
void mouse_write_dat(unsigned char dat); //发送数据
void mouse_read_dat(void); //读出数据
void mouse_data_process(void); //数据处理
void mouse_init(void); //鼠标初始化
/*--------------------------------------------------------------*/
//外部中断INT0初始化
void INT1_init(void)
{
EA = 1; //总中断
EX1 = 1; //外部中断
PX1 = 1; //中断优先级
IT1 = 0; //低电平触发
}
/*--------------------------------------------------------------*/
//延时120us 函数定义
void delay120us(void)
{
unsigned char i, j;
for(i = 23; i > 0; i--)
for(j = 1; j > 0; j--);
}
/*--------------------------------------------------------------*/
//发送数据
//发送11位数据:1START-8DATA-1PARITY-1STOP
//并接收一个应答位ack = 0
void mouse_write_dat(unsigned char dat)
{
unsigned char i; //循环变量
bit parity; //奇校验位
EX1 = 0; //关闭外部中断
ACC = dat; //存入累加器A,得到P(为偶校验)
parity = ~P; //获得奇校验位
mouse_clk = 0; //拉低时钟线
delay120us(); //至少延时100us
mouse_sda = 0; //发送起始位
mouse_clk = 1; //释放时钟线
mouse_sda = 1; //释放数据线
for(i = 0; i < 8; i++)
{ //至少要在25us内完成发送一位!!!
while(!mouse_clk); //等待设备把时钟线拉高
mouse_sda =(bit)(dat& 0x01);//先发送最低位
dat >>= 1; //下降沿写入数据
while(mouse_clk); //等待设备把时钟线拉低
}
while(!mouse_clk); //等待设备把时钟线拉高
mouse_sda = parity; //发送奇校验位
while(mouse_clk); //等待设备把时钟线拉低
while(!mouse_clk); //等待设备把时钟线拉高
mouse_sda = 1; //发送停止位
while(mouse_clk); //等待设备把时钟线拉低
while(!mouse_clk); //等待设备把时钟线拉高
while(mouse_sda); //等待接收应答位(总是为0)
while(mouse_clk); //等待设备把时钟线拉低
while(!mouse_clk); //等待设备释放时钟线
while(!mouse_sda); //等待设备释放数据线
EX1 = 1; //打开外部中断INT0
}
/*--------------------------------------------------------------*/
//奇校验
bit check_parity(void)
{
ACC = mouse_data[m];
if(~P == (bit)(mouse_word & 0x0200)) return 1; //奇校验成功则返回1
else return 0; //奇校验失败则返回0
}
/*--------------------------------------------------------------*/
//读出3字节数据
void mouse_read_dat(void)
{
mouse_data[m] = (unsigned char)(mouse_word >> 1); //去掉最高两位和最后一位
if(check_parity()) //奇校验成功
{
mouse_word = 0; //清接收数据字
m++; if(m == 3) {mouse_data_process(); m = 0;} //读出三字节数据
}
}
/*--------------------------------------------------------------*/
//数据处理
void mouse_data_process(void)
{ //水平移动
if(mouse_data[0] & 0x10) move_x -= 256 - mouse_data[1]; //x坐标减
else move_x += mouse_data[1]; //x坐标加
//垂直移动
if(mouse_data[0] & 0x20) move_y -= 256 - mouse_data[2]; //y坐标减
else move_y += mouse_data[2]; //y坐标加
if(mouse_data[0] & 0x01) mouse_left = 1; //左键
else mouse_left = 0;
if(mouse_data[0] & 0x02) mouse_right = 1; //右键
else mouse_right = 0;
if(mouse_data[0] & 0x04) mouse_middle = 1; //中键
else mouse_middle = 0;
}
/*--------------------------------------------------------------*/
//外部中断INT0服务
//接收11位数据:1START-8DATA-1PARITY-1STOP
void INT1_intservice(void) interrupt 2
{
mouse_word >>= 1; //先向右空移一位
if(mouse_sda) mouse_word |= 0x0400; //先接收最低位
n++; if(n == 11) {mouse_read_dat(); n = 0;} //接收完成则读出数据
}
/*--------------------------------------------------------------*/
//鼠标初始化
void mouse_init(void)
{
INT1_init(); //外部中断INT0初始化
mouse_write_dat(0xf4); //Enable Data Reporting
while(mouse_data[0] != 0xfa); //等待鼠标应答
mouse_data[0] = 0; //清应答数据
m = 0; //重新定义接收数据指针
}
/*--------------------------------------------------------------*/
#endif
演示视频:http://v.youku.com/v_show/id_XOTI0MTM5OTY=.html
相关程序:https://static.assets-stash.eet-china.com/album/old-resources/2009/6/12/bb2dc287-e220-4c5a-98d5-9f12cf4f274c.rar
用户195584 2010-2-26 09:22
用户1360014 2009-12-9 16:50
用户1564109 2009-9-4 21:42
用户1564109 2009-8-21 10:14
用户222124 2009-8-18 14:24
用户1564109 2009-8-18 12:32