PS2协议网上的资料很多,这里不再细述,仅就通讯双方(以键盘和PC为例)交换信息的细节作分析。
1,PS2的基本常识主要包括:
1.1 PS2通信由CLK和DATA两个线构成
1.2 PS2的CLK不论PC->KeyBoard,还是PC<-KeyBoard,都是由KeyBoard提供
1.3 数据帧构成:起始位(1bit) + 数据位(8bit) + 奇校验位(1bit) + 停止位(1bit) + 应答位(1bit, 只在PC->KeyBoard中,KeyBoard产生)
1.4 CLK平时状态为高电平,低电平表示抑制通讯,说明PC现在处理不过来,需要KeyBoard将发送的数据暂时缓存
1.5 数据在CLK下降沿被采样
1.6 数据发送的时序如下:(a) 键盘发送时序;(b) 键盘接收时序
2, PC->KeyBoard中,KeyBoard怎么知道PC有数据要发给自己?
正常情况下,CLK和DATA两根线都是高电平。PC下拉CLK超过100us,再下拉DATA,然后释放CLK ; 即让KeyBoasd在CLK上升沿采样到DATA为低 。KeyBoard识别这个特征后,意识到PC有东西要传,然后主动提供时钟。
3, PC<-KeyBoard中,PC怎么知道KeyBoard有数据要发给自己?
PC等待CLK拉低,在下降沿时采样DATA 为低时 ,认为KeyBoard有数据传送。
4,PC没有作好接收的准备,怎么办?
PC如果暂时不能接收新的数据,就主动把CLK拉低了。KeyBoard看到CLK为低,会将数据暂存在buffer里的。
4,KeyBoard没有作好接收的准备,怎么办?
KeyBoard在CLK上升沿采样到DATA为高时,意识到PC有数据要发送,如果不能接收,就不提供时钟;即上升沿后的CLK为高电平,这时KeyBoard不要将CLK下拉,让PC的起始bit发不出来,使PC处理等待发送状态至直KeyBoard准备好可以接收,才提供时钟。
5,具体单片机实现代码
5.1 单片机-->PS/2键盘
unsigned char PS2_SendComm(unsigned char dat) { unsigned char i; unsigned char j; keyboard_CLK=0; /*拉低时钟线*/ j = CheckOut(dat); //求检验和 delay10us(10); keyboard_SDA=0; keyboard_CLK=1; /*释放时钟线*/ while(keyboard_CLK==1); /*等待设备拉低时钟线*/ for(i=0;i<8;i++) /*低位在前,一次发送8个数据位*/ { if((dat&DataTemp[i])!=0) keyboard_SDA=1; /*发送数据位*/ else keyboard_SDA=0; while(keyboard_CLK==0); /*等待设备释放时钟线*/ while(keyboard_CLK==1); /*等待设备拉低时钟线*/ } if(j==1) keyboard_SDA=1; else keyboard_SDA=0; /*发送校验位,奇校验*/ while(keyboard_CLK==0); while(keyboard_CLK==1); keyboard_SDA=1; /*发送停止位*/ while(keyboard_CLK==0); while(keyboard_CLK==1); keyboard_SDA=1; /*释放数据线*/ //这里好像好像少了while(keyboard_CLK==0);这句话吧~~~~ hochy 2010-8-5 while(keyboard_CLK==1);/*等待设备拉低时钟线*/ if(keyboard_SDA==1) j=1; //时钟下降沿接收数据 else j=0; //下降沿时读取从机ACK while(keyboard_CLK==0); return j; }
5.2 单片机<--PS/2键盘
unsigned char PS2_ReceData(void) { unsigned char rece_data = 0; unsigned char i; unsigned char checksum,temp; while(keyboard_CLK==1); //等待从机应答的第一个时钟下降沿出现 if (keyboard_SDA==0) //检查是否收到开始低电平,否则停止接收 { while(keyboard_CLK==0); //等待起始低电平结束 for(i=0;i<8;i++) { while(keyboard_CLK==1); //等待时钟下降沿,接收data0 rece_data >>= 1; if(keyboard_SDA==1) { rece_data |= 0x80; //时钟下降沿接收数据 } while(keyboard_CLK==0); //等待时钟低电平结束 } while(keyboard_CLK==1); //等待时钟下降沿,接收data0 if(keyboard_SDA==1) checksum=1; //时钟下降沿接收数据 else checksum=0; while(keyboard_CLK==0); //等待时钟低电平结束 while(keyboard_CLK==1); if (keyboard_SDA==0) return 0; while(keyboard_CLK==0); temp = CheckOut(rece_data); if ( temp != checksum) return 0; else return rece_data; } return 0; }
5.3 单片机中断接收PS/2键盘的消息
/************************************************** 外部中断1 ***************************************************/ void ReceiveData(void) interrupt 2 { EX1=0; BufferCount++; if(BufferCount == 1)//START位 { if(keyboard_SDA) { BufferCount = 0; } } else if(BufferCount > 1 && BufferCount < 10) //接收8位数据 { KeyboardData >>= 1; if(keyboard_SDA) { KeyboardData |= 0x80; } } else if(BufferCount == 10) { KeyboardTemp=keyboard_SDA; } else if(BufferCount == 11) //11位数据接收完毕 { KeyboardOk=1; //处理 BufferCount = 0; KeyboardTemp = 0; } EX1=1; }
5.3 PS/2键盘初始化
#define NUM_LED 0x02 #define CAPS_LED 0x04 #define SCROLL_LED 0x01 #define COMM_CONFIG_LED 0xed #define COMM_RESET 0xff #define COMM_DISABLE_KEY 0xf5 #define COMM_ENABLE_KEY 0xf4 #define COMM_ACK 0xfa #define COMM_RESEND 0xfe #define COMM_CONFIG_RATE 0xf3 #define COMM_READ_ID 0xf2 #define COMM_SET_RESOLUTION 0xe8 #define COMM_SET_SCALING 0xe6 void config_led(unsigned char led) { led &= 0x07; do { while (PS2_SendComm(COMM_CONFIG_LED)); }while (PS2_ReceData() != COMM_ACK); while (PS2_SendComm(led)); } void PS2_readID(void) { unsigned char i[2]; while (PS2_SendComm(COMM_READ_ID)); i[0]=PS2_ReceData(); i[1]=PS2_ReceData(); Prints("PS2 ID:"); PrintHex(i[0]); PrintHex(i[1]); Prints('/n'); } void PS2_Init(void) { unsigned char i; unsigned char j; PS2_SendComm(COMM_RESET); Prints("PS2 reset command/n"); i=PS2_ReceData(); Prints("PS2 reset result: "); PrintHex(i); Prints('/n'); PS2_SendComm(COMM_CONFIG_LED); i=PS2_ReceData(); Prints("PS2 config led result: "); PrintHex(i); Prints('/n'); PS2_SendComm(0); i=PS2_ReceData(); Prints("PS2 turns of all led result: "); PrintHex(i); Prints('/n'); PS2_SendComm(COMM_READ_ID); i=PS2_ReceData(); j=PS2_ReceData(); Prints("PS2 read id result:"); PrintHex(i); Prints('/n'); Prints("PS2 id is: ;"); PrintHex(j); Prints('/n'); PS2_SendComm(COMM_CONFIG_LED); i=PS2_ReceData(); Prints("PS2 config led result:"); PrintHex(i); Prints('/n'); PS2_SendComm(2); i=PS2_ReceData(); Prints("PS2 turns of num lock led result: ;"); PrintHex(i); Prints('/n'); PS2_SendComm(COMM_CONFIG_RATE); i=PS2_ReceData(); Prints("PS2 config rate result: ;"); PrintHex(i); Prints('/n'); PS2_SendComm(0x20); i=PS2_ReceData(); Prints("PS2 config rate 20 result: ;"); PrintHex(i); Prints('/n'); PS2_SendComm(COMM_ENABLE_KEY); i=PS2_ReceData(); Prints("PS2 enble key result: ;"); PrintHex(i); Prints('/n'); PS2_SendComm(COMM_CONFIG_RATE); i=PS2_ReceData(); Prints("PS2 config rate result: ;"); PrintHex(i); Prints('/n'); PS2_SendComm(0); i=PS2_ReceData(); Prints("PS2 config rate 0 result: ;"); PrintHex(i); Prints('/n'); }