原创 第四十五课 PS\2之二 鼠标+LCD1602显示

2009-6-12 21:07 3691 11 17 分类: MCU/ 嵌入式

第四十五课 PS\2之二 鼠标+LCD1602显示


参考资料:pdf


主机到设备的通讯<?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字节数据包格式



位移计数器是一个92的补码整数。它的最高位作为符号位出现在位移数据包的第一个字节里。这些计数器在鼠标读取输入发现有位移时被更新。这些值是自从最后一次发送位移数据包给主机后位移的累计量(即最后一次包发给主机后位移计数器被复位)。位移计数器可表示的值的范围是-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



 

文章评论6条评论)

登录后参与讨论

用户195584 2010-2-26 09:22

博主您好,冒昧向您请教个问题。本人最近在学习arm,为了练手,凑巧也是用arm开发板模拟PS2鼠标,但是受阻,具体问题请看我的这篇求助帖,烦请不吝赐教,多谢。 http://bbs.ednchina.com/ShowTopic.aspx?id=162844

用户1360014 2009-12-9 16:50

学习了,不过以前写的是PS键盘程序

用户1564109 2009-9-4 21:42

你可以把电路发给我一下,想学习一下

用户1564109 2009-8-21 10:14

那你可以帮我看看用PIC写的鼠标数据采集程序吧! #include //液晶显示 #include __CONFIG(HS&UNPROTECT&DUNPROT&PWRTDIS&BORDIS&WDTDIS&LVPDIS&DEBUGEN&WRTEN); #define uchar unsigned char #define uint unsigned int /*--------------------------------------------------------------*/ #define mouse_sda RB2 //鼠标数据线 #define mouse_clk RB0 //鼠标时钟线 /*--------------------------------------------------------------*/ //********************液晶功能控制引脚配置*************// #define LCD PORTD //LCD的数据口 #define RS RA2 //LCD数据/命令选择端(H/L) #define RW RA3 //LCD 读/写 选择端(H/L) #define EN RA5 //LCD使能控制端 //-----------------------------------------------------// //*****************鼠标功能控制参数********************// unsigned char n, m; //循环变量 unsigned int mouse_word; //接收字16bits unsigned char mouse_data[3];//接收字节缓冲区 unsigned char p2=0; bit parity1; //奇校验位 bit parity; //奇校验位 unsigned int move_x=100; //横坐标 unsigned int move_y=200; //纵坐标 //-------------------------液晶显示模块开始----------------------------// void init_sys(void){ ADCON1=0X07; INTCON=0X00; TRISD=0X00; PORTD=0X0FF; TRISA=0X00; PORTA=0X00; RA0=0; } void check_BF(void) //检查LCD状态函数 { unsigned char i; for(i=0;i<15;i++); //proteus中只能用延时了,检测BF位进行不了 } void write_inst(char inst) //写入指令函数 { RS=0; delay_ms(1); RW=0; delay_ms(1); EN=1; delay_ms(1); LCD=inst; delay_ms(1); EN=0; check_BF(); } void write_char(char character) //写入数据函数 { RS=1; delay_ms(1); RW=0; delay_ms(1); EN=1; delay_ms(1); delay_ms(1); LCD=character; delay_ms(1); EN=0; check_BF(); } //=====写入指定位置函数====== void display_str(unsigned char line,unsigned char location,unsigned char str)//line行,location列,str字符 { RS=0; delay_ms(1); RW=0; delay_ms(1); EN=1; delay_ms(1); if (line==0) //line=0,第一行 LCD=0x80+location; if (line==1) //line=1,第二行 LCD=0xC0+location; //1602-LCD的第二行首地址为:0xA8 EN=0; check_BF(); write_char(str); } void init_lcd(void) //液晶初始化函数 { write_inst(0x30); //设定功能 delay_ms(5); write_inst(0x30); //设定功能 delay_ms(5); write_inst(0x30); //设定功能 delay_ms(5); write_inst(0x38); //设定俩列 delay_ms(5); write_inst(0x08); //关闭显示功能 delay_ms(5); write_inst(0x01); //清除显示屏 delay_ms(5); write_inst(0x0c); //开启显示功能 delay_ms(5); } //输出一串数字到LCD void LCD_prints(unsigned char line,unsigned char location,unsigned int uiNumber)//line行,location列,uiNumber数字 { unsigned char ucaNumber[5],ucCount; ucaNumber[0]=(uiNumber/10000); ucaNumber[1]=(uiNumber/1000)%10; ucaNumber[2]=(uiNumber/100)%10; ucaNumber[3]=(uiNumber/10)%10; ucaNumber[4]=uiNumber%10; RS=0; delay_ms(1); RW=0; delay_ms(1); EN=1; delay_ms(1); if (line==0) //line=0,第一行 LCD=0x80+location; if (line==1) //line=1,第二行 LCD=0xC0+location; //1602-LCD的第二行首地址为:0xA8 EN=0; check_BF(); for(ucCount=0;ucCount<5;ucCount++) { write_char(ucaNumber[ucCount]+48); //从首位到末位逐一输出。 } } //-------------------------液晶显示模块结束-------------------------------------// //*-------------------------鼠标采集数据模块开始-------------------------------------// //外部中断INT0初始化 void INT1_init(void) { INTCON=0X00; GIE=1; //总中断 // RBPU=0; INTE=1;//外部中断 INTEDG=0;//下升沿的效 TRISB=0B00000101;//正常工作下时钟RB0和数据RB2均输入 INTF=0;//这三句是开外部中断 } /*--------------------------------------------------------------*/ //发送数据 //发送11位数据:1START-8DATA-1PARITY-1STOP //并接收一个应答位ack = 0 void mouse_write_dat(unsigned char dat) { unsigned char i; //循环变量 unsigned char p1=0;//数据位中1个数计数器,奇偶校验位判断标志 INTE = 0; //关闭外部中断 TRISB2=0;//时钟数据均输出 TRISB0=0; asm( "nop"); mouse_clk = 0; //拉低时钟线 delay_ms(110); //至少延时100us mouse_sda = 0; //发送起始位 mouse_clk = 1; //释放时钟线 mouse_sda = 1; //释放数据线 for(i = 0; i < 8; i++) { //至少要在25us内完成发送一位 TRISB0=1;//时钟输入 asm( "nop"); while(!mouse_clk); //等待设备把时钟线拉高 mouse_sda =(bit)(dat& 0x01);//先发送最低位 dat >>= 1; //下降沿写入数据 /*if(mouse_sda==1){ p1=p1+1; }//测试数据包中1的个数 //校验计算 if(p1%2==1){ parity=0; p1=0;} else{ parity=1; p1=0; }*/ while(mouse_clk); //等待设备把时钟线拉低 } while(!mouse_clk); //等待设备把时钟线拉高 // mouse_sda = parity; //发送奇校验位 mouse_sda = 0; //发送奇校验位 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); //等待设备释放数据线 INTE = 1; //打开外部中断INTE TRISB2=1;//数据输入 asm( "nop");RA0=1; } /*--------------------------------------------------------------*/ //奇校验 /*bit check_parity(void) { if(p2%2==1){ parity1=0; p2=0; } else{ parity1=1; p2=0; } if(parity1 == (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()) //奇校验成功 if(1) { mouse_word = 0; //清接收数据字 m++; if(m == 3) { 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坐标加 m = 0;} //读出三字节数据 } } //外部中断INTE服务 //接收11位数据:1START-8DATA-1PARITY-1STOP void interrupt INTE_ISR(void) { INTF=0;//进入中断,清理中断标志 //RA0=1; mouse_word >>= 1; //先向右空移一位 if(mouse_sda) {mouse_word |= 0x0400; p2=p2+1;} //先接收最低位 n++; if(n == 11) {mouse_read_dat(); n = 0;} //接收完成则读出数据 } /*--------------------------------------------------------------*/ //鼠标初始化 void mouse_init(void) { INT1_init(); //外部中断INTE初始化 mouse_write_dat(0xf4); //发送使能位 //while(1) {LCD_prints(1,3,move_y);} while(mouse_data[0] != 0xfa); //等待鼠标应答 mouse_data[0] = 0; //清应答数据 m = 0; //重新定义接收数据指针 } //*-------------------------鼠标采集数据模块结束-------------------------------------// //*------------------------主函数----------------------------------------------------// void main(void) { init_sys(); init_lcd(); LCD_prints(0,3,1000); LCD_prints(1,3,1000); display_str(0,0,'x'); display_str(0,1,':'); display_str(1,0,'y'); display_str(1,1,':'); mouse_init(); //鼠标初始化 while(1){ LCD_prints(0,3,move_x); //LCD_prints(1,3,move_y); delay_ms(50); } } 谢谢

用户222124 2009-8-18 14:24

只要按照PS2接口的协议来应该都是可以的哈!!!

用户1564109 2009-8-18 12:32

你好,我最近也在弄PS2鼠标数据的采集,一直有个问题想请教,那个PS2鼠标是各个厂家的产品都可以拿来做吗?我想用PIC单片机动去采集,谢谢!!!!!!!!!!!!
相关推荐阅读
用户222124 2010-12-08 22:18
台达PLC监控
台达PLC监控网上有很多例子,自己也照着弄了个,挺方便的,分享下串口数据格式:COM1,9600,8,N,1  ...
用户222124 2010-06-27 16:36
基于NIOS II和FPGA无线小车控制系统的设计
   主控制台以Cyclone II系列的EP2C8Q208C8为核心,通过nRF24L01进行无线通讯,来控制以ATmega8为微处理器的两轮小车,可以实现小车按控制平台发送的控制信息来准确运动。所...
用户222124 2009-11-13 15:58
51开发板已经销售完了,谢谢大家的支持!
51开发板已经销售完了,谢谢大家的支持!...
用户222124 2009-09-28 16:13
51开发板十一大放送!!!赶快行动吧!!!
  51开发板十一大放送!  赶快行动吧!!!   开发板硬件介绍: http://group.ednchina.com/2031/24972.aspx  开发板实物图片总汇: http://grou...
用户222124 2009-09-13 19:02
nios II 实验五 数码管显示数字钟(定时器实现 外部中断调节)
#include "system.h"                      //包含基本的硬件描述信息#include "altera_avalon_timer_regs.h"    //定义内...
用户222124 2009-09-13 18:57
nios II 实验四 PIO外部中断
#include "system.h"                      //包含基本的硬件描述信息#include "altera_avalon_timer_regs.h"    //定义内...
EE直播间
更多
我要评论
6
11
关闭 站长推荐上一条 /3 下一条