kkklooop 发表于 2014-5-16 20:59:32

求助:51模拟PS2键盘,自检时电脑常死机,重拔插电源才通过

程序如下:

#include<reg52.h>    //包含单片机寄存器的头文件
#include<intrins.h>//包含_nop_()函数定义的头文件
typedef    unsigned charuint8 ;
typedef    unsigned int   uint16;
#define TURNON 1
#define TURNOFF 0
bit flag = TURNON;

//uint8 ctrl = 0x0;
static uint8 PS2RecChar1 = 0xCC;
/*******************************************************************************
以下是引脚定义
*******************************************************************************/
sbit PS2CLK1 = P1^4;         
sbit PS2DAT1 = P3^2;   
sbit ctrl0 = P1^5;
//sbit ctrl1 = P1^6;
//sbit ctrl2 = P1^7;
/*****************************************************
函数功能:延时
***************************************************/

void Delay_5us ()      //5.43us晶振为11.0592M时调用一个函数要4个指令周期即4.34us,也就是说每多一个加一个指令周期(1/f)*12=1.085us
{        _nop_();
}
void Delay_10us ()      //
{        _nop_();_nop_();_nop_();_nop_();_nop_();
}

void Delay_20us ()       //19.53us
{       
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();
}
void Delay_15us ()       //15.19us
{       
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
void Delay_13us ()      //13.02
{       
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
}
void Delay_40us ()      //39.06
{       
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();
}
void Delay_50us ()      //50.1
{       
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
        _nop_();_nop_();_nop_();
}

void delay_ms ( uint16 t )
{
        uint16 i,j;
        for ( i = t; i > 0; i-- )
                for ( j = 110; j > 0; j-- )
                                ;               
}

void init_serial()
{
T2CON = 0x34;                           //Set the Counter/Timer2 for the baud rate generator
RCAP2H = 0xFF;                           //T2 baud is 38400          
RCAP2L = 0xF7;          
SCON = 0x50;                           //Serial port in Mode 2
EA=1;
ES = 1;                                  //Allow serial interrupt
}          

//==================================================
//    c51模拟PS2键盘和PC机通讯程序
//   发送程序代码
//==================================================

void OnPS2SendChar1 ( uint8 dat )
{
        uint8 h = 0, j;
        uint8 i = 8;
bit bparity = 0 ;

ACC = dat;                                                                                 //获取字节的奇偶信息
if ( !P )                                                                                   //ACC中偶数时,P为0,但是PS2中时奇校验( 字节中的1的个数+校验位 = 奇数)
                bparity = 1;

if( PS2CLK1 && PS2DAT1 )                                                                 //发送前检测PS2总线
               {
                                Delay_50us ();                                                             //50us   
                                if ( PS2CLK1 )                                    //时钟线空闲
                                                {
                                                       if ( PS2DAT1 )                              //数据线空闲
                                                                       {
                                                                                        for (j = 11;j > 0;j--)               //1共11个数据
                                                                                                        {
                                                                                                               if ( h == 0 )               // 送起始位
                                                                                                                               {
                                                                                                                                                PS2DAT1 = 0;
                                                                                                                                                h++;
                                                                                                                               }
                                                                                                               else if ( h == 1 )            //送8位数据位
                                                                                                                               {
                                                                                                                                                PS2DAT1 = dat & 0x01; //先LSB开始
                                                                                                                                                dat >>= 1;
                                                                                                                                                i--;
                                                                                                                                                if        (i == 0)         //发送完成
                                                                                                                                                               h++;
                                                                                                                               }
                                                                                                               else if ( h == 2 )            //送校验位
                                                                                                                               {
                                                                                                                                                PS2DAT1 = bparity;
                                                                                                                                                h++;
                                                                                                                               }
                                                                                                               else
                                                                                                                               { PS2DAT1 = 1; }               //送停止位
                                                                                                               //Delay_10us ();               //10us
                                                                                                               Delay_13us ();                   //数据改变后,到此处耗时6.51us
                                                                                                               PS2CLK1 = 0;                   //拉低时钟线,设备发送
                                                                                                               Delay_40us ();               //40us
                                                                                                               PS2CLK1 = 1;                  
                                                                                                               Delay_5us ();               //for循环到下一次DATA置1时程序本身要耗时约20us
                                                                                                               if ( !PS2CLK1 )                //检测到时钟线变低
                                                                                                                               {                                                                                             //主机不要这次通讯 (很罕见)
                                                                                                                                                break;                  //return;
                                                                                                                               }
                                                                                                        }
                                                                                        //Delay_15us ();                        //发送完一包后延迟30us   
                                                                                        //Delay_15us ();               
                                                                       }//if(PS2DAT)
                                                       else
                                                                                return;                              //主机发送命令,KB转入接收程序
                                               }//if(PS2CLK)
               }//if(PS2CLK&&PS2DAT)
}//end

//======================================================================
//    c51模拟PS2键盘和PC机通讯程序
//   接收程序代码
//======================================================================

void OnPS2ReceChar1 ()
{       
        bit bparity = 1 ; //奇偶校验错误,明天来确认。原来为0 改为1后进不了系统!
        bit ParityBit = 0 ;
        bit DATAFlag = 1;
        uint8 dat = 0x0, i;
        if( !PS2DAT1 )                                       
                        {
                                //标准键盘DATA线拉低250us后产生时钟,所以我在延迟约200us后再检测DATA
                                Delay_50us () ;   
                                Delay_50us () ;
                                Delay_50us () ;
                                if ( !PS2DAT1 )
                                                {                                             //data为低,则读取一个字节
                                                                for ( i = 0; i < 8; i ++ )//read 8bit
                                                                               {
                                                                                                _nop_();          //1T
                                                                                               Delay_20us ();
                                                                                                PS2CLK1 = 0;                      //1T
                                                                                                Delay_40us ();                  
                                                                                          PS2CLK1 = 1;                      //1T
                                                                                          dat = dat >> 1;                   //4T
                                                                                                if ( PS2DAT1 )                  //2T
                                                                                                        {
                                                                                                                dat|=0x80;                  //2T
                                                                                                                bparity = ~bparity;         //1T
                                                                                                        }
                                                                                                else
                                                                                                        {
                                                                                                                _nop_();_nop_();_nop_();
                                                                                                        }
                                                                                                if ( PS2CLK1 == 0 )               //2T
                                                                                                        { return; }             //如果时钟被拉低,则有错误发生,发送0XFE报错
                                                                                                //OnPS2SendChar1 ( 0xfe );
                                                                               }                                    //6T
                                                                                                       
                                                               Delay_20us ();                                     //读取校验位   
                                                               PS2CLK1 = 0;         
                                                               Delay_40us ();
                                                               PS2CLK1 = 1;
                                                               Delay_20us ();
                                                               if ( PS2DAT1 )
                                                                                ParityBit = 1;
                                                               else
                                                                                ParityBit = 0;
                                                               
                                                               Delay_20us ();                                     //越过停止位
                                                               PS2CLK1 = 0;         
                                                               Delay_40us ();
                                                               PS2CLK1 = 1;
                                                               Delay_20us ();       //STOP BIT H_DATA 0 ERR
                                                               /*
                                                               if ( !PS2DAT1 )                                    //如果DATA线为低,输出CLK直到DATA线为高
                                                                                {
                                                                                       DATAFlag = 0;
                                                                                       Delay_20us ();                                    
                                                                                       PS2CLK1 = 0;         
                                                                                       Delay_40us ();
                                                                                       PS2CLK1 = 1;
                                                                                       Delay_20us ();      
                                                                                }               
                                                               if ( DATAFlag == 0 )
                                                                       OnPS2SendChar1 ( 0xfe );
                                                               */
                                                               
                                                               Delay_10us ();                                    //协议中要求15us后
                                                               PS2DAT1 = 0;                                          //ACK bit
                                                               Delay_10us ();                                    //协议中要求5us后
                                                               PS2CLK1 = 0;
                                                               Delay_40us ();
                                                               PS2CLK1 = 1;
                                                               Delay_10us ();                                    //协议中要求5us后,我发现5us时间太短,ASK上升沿会与CLK上升沿重合
                                                               PS2DAT1 = 1;
                                                               /*
                                                               Delay_40us ();
                                                               PS2CLK1 = 0;
                                                               Delay_40us ();
                                                               Delay_20us ();
                                                               PS2CLK1 = 1;
                                                               */
                                                               if ( ParityBit == bparity )
                                                                       PS2RecChar1 = dat;
                                                               else
                                                                       OnPS2SendChar1 ( 0xfe );                                                                                   //校验错误,请求重发,而不是报错0XFC
                                                               Delay_40us ();                                       //延迟45us
                                                               Delay_5us ();
                                                       }
                                        }
}

//======================================================================
//    c51模拟PS2键盘和PC机通讯程序
//   开机自检程序代码
//======================================================================

void ProcessPS21 ( void )
{
        if ( PS2RecChar1 != 0xCC )                           
        {
                switch ( PS2RecChar1 )
                {       
                        case 0xEE:                        
                        {
                                OnPS2SendChar1 ( 0xEE );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;
                        case 0xFE:                         //7 //resend
                        {
                                PS2RecChar1 = 0xCC;
                        } break;
                        case 0xAA:                           //12
                        {
                                OnPS2SendChar1 ( 0xAA );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;
                        case 0xEF:                         //5
                        {
                                OnPS2SendChar1 ( 0xFA );
                                //Delay_50us () ;
                                Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;
                                OnPS2SendChar1 ( 0xBF );
                                //Delay_50us () ;
                          Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;
                                OnPS2SendChar1 ( 0xB0 );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;
                        case 0xF2:
                        {
                                OnPS2SendChar1 ( 0xFA );
                                //Delay_50us () ;
                                Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;
                                OnPS2SendChar1 ( 0xAB );
                                //Delay_50us () ;
                                Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;
                                OnPS2SendChar1 ( 0x83 );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;
                        case 0xFF:
                        {
                                OnPS2SendChar1 ( 0xFA );
                                //Delay_50us () ;
                                Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;Delay_50us () ;
                                OnPS2SendChar1 ( 0xAA );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;
                        default:
                        {
                                OnPS2SendChar1 ( 0xFA );
                                //Delay_50us () ;
                                delay_ms ( 1 );
                                PS2RecChar1 = 0xCC;
                        } break;       
                }
        }       
}


/*
void ProcessPS21 ( void )
{
       
                if( PS2RecChar1 == 0xF3 )//1
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0x00 )//11
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0x02 )//111
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0x20 )//1111
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1= 0xCC;
                }
                else if( PS2RecChar1 == 0xED )//2
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0xF0 )//3
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0xF2 )//4
                {
                        OnPS2SendChar1 ( 0xFA );
//                        Delay30us();
                        OnPS2SendChar1 ( 0xAB );
//                        Delay30us();
                        OnPS2SendChar1 ( 0x83 );
                        PS2RecChar1 = 0xCC;
                }
                else if( PS2RecChar1 == 0xEF )//5
                {
                        OnPS2SendChar1 ( 0xFA );
//                        Delay30us();
                        OnPS2SendChar1 ( 0xBF );
//                        Delay30us();
                        OnPS2SendChar1 ( 0xB0 );
                        PS2RecChar1 = 0xCC;
                }
                else if ( PS2RecChar1 == 0xF3 )//6
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if(PS2RecChar1==0xFE)//7 //resend
                {
                        PS2RecChar1=0xCC;
                }
                else if(PS2RecChar1 == 0xEE)//8
                {
                        OnPS2SendChar1 ( 0xEE );
                        PS2RecChar1 = 0xCC;
                }
                else if(PS2RecChar1 == 0xEE)//9
                {
                        OnPS2SendChar1 ( 0xEE );
                        PS2RecChar1 = 0xCC;
                }
                else if (PS2RecChar1 == 0xF1)//10
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if (PS2RecChar1 == 0xF4)//11
                {
                        OnPS2SendChar1 ( 0xFA );
                        PS2RecChar1 = 0xCC;
                }
                else if (PS2RecChar1 == 0xAA )//12
                {
                        OnPS2SendChar1 ( 0xAA );
                        PS2RecChar1 = 0xCC;
                }
                else if ( PS2RecChar1 == 0xFF )//13
                {
                        OnPS2SendChar1 ( 0xFA );
//                        Delay30us();
                        OnPS2SendChar1 ( 0xAA );
//                        Delay30us();
                        PS2RecChar1 = 0xCC;
                }
                else ;
       
}
*/

/********************************************************************************************************************/
//               主程序                                          
/********************************************************************************************************************/
void main(void)
{
        uint8 temp;
        uint8 code table[]={0x1c,0x32,0x21,0x23,0x24,0x2b,0x34,0x33,0x43,0x3b};         //   A,B,C,D,E,F,G,H,I,J                  
        //PS2Init ( );                                                //开中断
        ctrl0 = 0;
        PS2DAT1 = 1;
        PS2CLK1 = 1;
        init_serial();
        while(1)
        {
                if ( PS2DAT1 == 0 )
                {
                        Delay_50us () ;
                        if ( PS2DAT1 == 0 )
                        {
                                OnPS2ReceChar1 ();
                        }                       
                }
                if ( PS2RecChar1 != 0xCC )                           
                {
                        TI=0;
                  SBUF = PS2RecChar1;
                  while( !TI );
                  TI = 0;
                        Delay_50us () ;
                        Delay_50us () ;
                        Delay_50us () ;
                        ProcessPS21 ();
                }
                /*
                if ( ctrl0 == 1 )//不停的发q
                        {
                                OnPS2SendChar1 ( 0X15 );                //按下通码
                                delay_ms ( 2 );
                                OnPS2SendChar1 ( 0xF0 );        //抬起,断码
                                delay_ms ( 1 );         //每发一包数据后,时钟会拉低约200us,如果立刻发下一包数据,将被忽略
                                OnPS2SendChar1 ( 0X15 );
                                delay_ms ( 1000 );
                        }
   */
       
                if ( ( ctrl0 == 1 ) && ( flag == TURNON ) )//发送abcdefghij
                {
                        flag = TURNOFF;
                        for ( temp = 0; temp < 10; temp ++ )
                        {
                                OnPS2SendChar1 ( table );                //按下通码
                                delay_ms ( 2 );
                                OnPS2SendChar1 ( 0xF0 );        //抬起,断码
                          delay_ms ( 1 );         //每发一包数据后,时钟会拉低约200us,如果立刻发下一包数据,将被忽略
                                OnPS2SendChar1 ( table );
                                delay_ms ( 500 );
                        }
                }
                               
        }
}

lr120363092 发表于 2014-5-16 21:16:40

楼主模拟PS2键盘有什么用呢?

kkklooop 发表于 2014-5-16 21:21:02

当正常自检通过时,将收到的数据通过串口转发到其它电脑,可以确认的是收数正常;当进入系统后,把p1.5接高电平能正常发数。
个人觉得应该是程序其它部分有BUG,但就是找不出来,串口抓到的数据是00 F6 F2,到此就死机不动了,必须重拔插电源。

正常通过自检时主机发给键盘的数据奉献给需要的朋友:
依次是FF F6 F2 EE F2 ED 00 F4 F2 ED 02 F6
FF F3 00 ED 00 ED 00 F3 20 F3 20 ED 02

kkklooop 发表于 2014-5-16 21:28:34

做一种产品测试工装,一次测8台,其中一项是测试PS2,做高低温应力筛选实验,同时接8个键盘就失去了自动测试的意义

slotg 发表于 2014-5-16 21:47:09

关注中, 几年前我也用汇编写过模拟PS2键盘的程序, 实际连接到PC时有些PC可以通过开机检测, 有些PC通不过, 到现在还不清楚是什么原因!

sailing8 发表于 2014-5-23 00:32:43

与楼上相同的遭遇{:sweat:}貌似是自检协议完整性不好。
页: [1]
查看完整版本: 求助:51模拟PS2键盘,自检时电脑常死机,重拔插电源才通过