/*------------------------------------------------------------------
//作者:古道热肠 //Email:xg_2004_sy@126.com //Blog: gdrc.bolg.21ic.com //版本:1.0 //完成日期:2008-10-14 //硬件平台,51单片机IO口模拟PS/2协议与PS/2设备通讯 --------------------------------------------------------------------*/
//#include<conio.h> #include "....incincludes.h"
sbit PS2Kbd_Data = P1^1; sbit PS2Kbd_Clk = P1^2;
#define PS2ClkToLow() PS2Kbd_Clk=0 #define PS2ClkToHigh() PS2Kbd_Clk=1 #define PS2DataToLow() PS2Kbd_Data=0 #define PS2DataToHigh() PS2Kbd_Data=1
#define PS2DataTransEnable() PS2Kbd_Clk = 1 #define PS2DataTransDisable() PS2Kbd_Clk = 0
uchar ucPS2CommErrFlag = 0;
static bool GetPS2DataStatus(void) { // PS2Kbd_Data=1; return(PS2Kbd_Data); }
static bool GetPS2ClkStatus(void) { // PS2Kbd_Clk=1; return(PS2Kbd_Clk); }
static void Delay10Us(uchar ucDelayCount) { register uchar uci;
if(ucDelayCount == 0) { return; } for( ;ucDelayCount>0;ucDelayCount--) { for(uci=0; uci<30; uci++) { _nop_(); } } }
static void CaptureClkLow(void) { while(1) { if(GetPS2ClkStatus() == 1) { break; } }
while(1) { if(GetPS2ClkStatus() == 0) { if(GetPS2ClkStatus() == 0) { break; } } } }
static void CaptureClkHigh(void) { while(1) { if(GetPS2ClkStatus() == 0) { break; } }
while(1) { if(GetPS2ClkStatus() == 1) { if(GetPS2ClkStatus() == 1) { break; } } } }
/************************************************** 功能介绍:实现向PS2口发送1个字符的功能。 入口参数: 待发送的字符 返回: 发送是否成功。true 表示成功 备注: ***************************************************/ bool PS2_SendChar(uchar ucSendChar) { uchar ucCount; bool ucOddValue; uchar ucTempVal;
ucOddValue = true; ucTempVal = ucSendChar;
// ComShowString(COM_1,"start Test pS2");
// printf("start Send char to Ps2 Kbd
"); //先计算发送数据的奇校验值备用 for(ucCount=0; ucCount<8; ucCount++) { if(ucTempVal & 0x01) { ucOddValue = !ucOddValue; } ucTempVal >>= 1; } //抢占PS/2总线的控制权 PS2ClkToLow(); PS2DataToLow(); Delay10Us(10); //Delay100uS DISABLE;
PS2ClkToHigh(); //通知PS/2设备,时机已成熟
//等待PS/2设备切换角色并发起数据接收的时序 ucTempVal = ucSendChar; for(ucCount=0; ucCount<8; ucCount++) { //以下函数未设超时退出机制,如果设备不响应会死循环. CaptureClkLow(); //捕获到下降沿后改变数据线的值. if(ucTempVal & 0x01) { PS2DataToHigh(); } else { PS2DataToLow(); } ucTempVal >>= 1; //发送到P2/2的数据位会在时钟的上升沿锁入PS/2设备中的. } //发送校验位 CaptureClkLow(); if(ucOddValue) { PS2DataToHigh(); } else { PS2DataToLow(); } //发送停止位 CaptureClkLow(); PS2DataToHigh();
//最后一个脉冲表示设备响应 //备注:根据PS/2协议,主机发往PS/2的数据在设备没有确认前,想取消还来得及 // 方法就是我先将数据线拉低,不让设备拉低应答,设备就会丢弃接收到的数据 // 至今想不明白IBM当年设计PS/2接口时为何要加这个规定. CaptureClkLow(); if(GetPS2DataStatus() == 0) { CaptureClkHigh(); ENABLE; return true; } else { CaptureClkHigh(); ENABLE; return false; } }
//从PS/2端口获取PS/2发送的字符 static bool PS2_ReadChar(uchar *ucGetChar) { uchar data ucCount; bool ucOddValue; uchar ucTempVal; uchar ucResult; uint data uiDelayCount;
ucOddValue = true;
DISABLE; //Host释放总线控制权,让PS/2设备转换到数据发送状态,想发就快发,待会MCU要去干别的了. PS2DataTransEnable(); PS2Kbd_Data=1; PS2Kbd_Clk=1; // uiDelayCount = 0x1ff; //关键时间 //捕获时钟下降沿,有超时退出机制,这段时间就是让PS/2设备响应时间 while(--uiDelayCount) { if(GetPS2ClkStatus() == 0) { // DISABLE; break; } }
//已经查询过了,没有数据要发上来,俺MCU去返回干别的去了. if(GetPS2ClkStatus() != 0) //Repeat check { *ucGetChar = 0;
ENABLE; return false; }
//每位在时钟的下降沿被主机读入 //读取起始位必定为0 if(GetPS2DataStatus()) { if(GetPS2DataStatus()) { *ucGetChar = 0;
ucPS2CommErrFlag = 1; ComShowString(COM_1,"START BIT ERR"); ENABLE; return false; } } //读完起始位开始读取数据位 ucTempVal = 0x01; ucResult = 0; for(ucCount=0; ucCount<8; ucCount++) { CaptureClkLow(); if(GetPS2DataStatus()) { ucResult |= ucTempVal; ucOddValue = !ucOddValue; //计算校验值 } ucTempVal <<= 1; }
//读取奇校验位 CaptureClkLow(); if(GetPS2DataStatus() != ucOddValue) { //防抖动,不理想. if(GetPS2DataStatus() != ucOddValue) { ucPS2CommErrFlag = 1; ComShowString(COM_1,"Odd Check ERR"); ENABLE; return false; } } //获取停止位,必定为1 CaptureClkLow(); if(GetPS2DataStatus() == 0) { if(GetPS2DataStatus() == 0) {
ucPS2CommErrFlag = 1; ComShowString(COM_1,"STOP BIT ERR");
ENABLE; return false; } } //数据读取完成,置标志和结果转运 *ucGetChar = ucResult; ucPS2CommErrFlag = 0; //等待发送事件完成 CaptureClkHigh(); //禁止PS/2设备发送新数据,俺MCU有绝对控制权,要先去看看其它事件是否发生.有按键数据先自己缓存 PS2DataTransDisable(); ENABLE; return true; }
/************************************************** 功能介绍:实现从PS2口接收1个字符的功能。 入口参数: 接收字符存放的缓冲区指针 返回: 接收是否成功。true 表示成功 备注: 有延时等待发生。 ***************************************************/ bool PS2_GetChar(uchar *ucGetChar) { /* if(ucPS2CommErrFlag) { PS2_SendChar(0xFE); BeepShort(); } */ return (PS2_ReadChar(ucGetChar));
} |