求助菜鸟的89S52仿真PS2键盘,无法通过开机自检。
我自己想用AT89S52仿真一个PS2键盘,我有详细的看过PS2协议,但是没办法通过开机自检。实在是找不到错的地方了,望高手指点,谢谢了。这是全部的程序,扫描部分没有做,完整的处理,每次只能按下一个键。
#include<reg52.h>
#include<stdio.h>
#include<intrins.h>
#define row P1 //键盘使用12M的晶振。
#define lineone P0 //P2和P0口的加上上拉电阻。
#define linetwo P2
sbit KBCLK=P3^0; //键盘时钟线
sbit KBDATA=P3^1; //键盘数据线
sbit LEDNUM=P3^3; //NUMLOCK指示灯
sbit LEDCAP=P3^4; //CAPLOCK指示灯
sbit LEDSCR=P3^5; //SCROLLLOCK指示灯
bit ledne=0; //当他为1之后,在进行主机到键盘的通讯之后,立即将得到数据送给void set_led(unsigned char);
bit flag=0; //当他为1时,缓冲区头为通码,断开后需要进行断码处理。
bit keyne=1; //键盘扫描使能。为1时键盘允许扫描,为0时不让扫描。
bit sl=0; //扫描码对于特殊扫描码的通断码标志位。
bit spl=0; //对于PRINTSCAN键的通断码标志位。为0时已经打了断码,为1时没有打断码
bit spe=0; //特殊码处理标志。
void bat(void); //键盘诊断自检。
void ssend_byte(void);
void set_led(unsigned char); //点亮LED
void Timer1_init(void); //定时器1初始化
void keycode1(void); //将扫描到的在第一列上的扫描码,转换为ASCII码
void keycode2(void); //将扫描到的在第二列上的扫描码,转换为ASCII码
void set_led(unsigned char); //设置LED,在主机给键盘发送的命令里面调用
void receive_byte(void); //接收键盘命令并处理
void send_byte(void); //发送一字节数据/命令。
void key_manage(unsigned char); //有效按键处理,还有对于按键的缓存。
void manage(unsigned char); //有效命令处理
void send_command(unsigned char); //对于命令的缓存
//void ssend_byte(void);
void delayms(unsigned int); //ms级别的延时程序
//void delay(unsigned char); //us级别的延时程序
void delayus(unsigned char); //us级别的延时程序,在中断外使用。
void delays();
void clr_buf(void); //清键盘缓冲区
void re_send(void);
void Skey_manage(unsigned char); //特殊按键的处理。
void SAkey_manage(unsigned char); //按键PRINTSCAN键的处理。
void SSkey_manage(void); //按键PAUSE的处理。
unsigned char change(unsigned char); //将16进制转换成数字
unsigned char bdata redata; //定义一个可位寻址区,bdata的无符号字符型变量。
sbit redata_bit0=redata^0;
sbit redata_bit1=redata^1;
sbit redata_bit2=redata^2;
sbit redata_bit3=redata^3;
sbit redata_bit4=redata^4;
sbit redata_bit5=redata^5;
sbit redata_bit6=redata^6;
sbit redata_bit7=redata^7;
//bit send_code(unsigned char _KeyNo,bit flag);
//发送按键扫描码,flag=0发送断开码,flag=1发送接通码。
//void set_default(); //设置缺省值
//void set_scan_v(unsigned char); //拍发速率、延迟时间.
//void set_flag(unsigned char); //设置缓冲区对应标记。
//unsigned char paifa_count=0; //拍发计数
unsigned char huancun=0x00; //缓存最后一位发送的数据或者命令。
unsigned char key_buf; //按键缓冲区
unsigned char command_buf; //命令缓冲区
//unsigned char keyrow,keylineone,keylinetwo; //定义3个全局可查询变量。
unsigned char key_count=0; //按键计数器
unsigned char command_count=0; //命令计数器,计算命令缓冲区内的命令个数
unsigned char Rnum; //定义扫描到的行和列的大小
unsigned char Cnum1;
unsigned char Cnum2;
unsigned char idata tab1={{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C}};
unsigned char idata tab2={{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C},{0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C}};
unsigned char idata tab3={0xE1,0x14,0x77,0xE1,0xF0,0x14,0xF0,0x17};
unsigned char idata tab4={0xE0,0xF0,0x7C,0xE0,0xF0,0x12};
unsigned char idata tab5={0xE0,0x12,0xE0,0x7C};
// 函数名称: 主程序
// 函数功能: 循环查询主机状态
//--------------------------------------------------------------------------------------------------
void main()
{
bat(); //键盘诊断自检(基本保证测试)
while(1)
{
send_byte();
}
}
//--------------------------------------------------------------------------------------------------
// 函数名称: Timer0_init()和Timer1_init()
// 函数功能: 初始化设置
// 设定INT0,INT1的工作方式
//--------------------------------------------------------------------------------------------------
void Timer1_init(void)
{
ET1=1; //定时器1中断允许,方式为1,中断时间为10MS,65536-15536=50000 每50MS扫描一次键盘
TH0=0x3C;
TL0=0xB0;
TR1=1; //定时器1开始计数
}
//--------------------------------------------------------------------------------------------------
// 函数名称: TIMER1_intrupt
// 函数功能: 定时器1中断处理程序 按键定时查询
//--------------------------------------------------------------------------------------------------
void TIMER1_int(void) interrupt 3 using 3 //用定时器中断查询,而不用延时查询时为了节约CPU资源,P57页详细讲解。
{
unsigned char R,L1,L2,keyrow,keylineone,keylinetwo;
if(keyne==0)
{
return; //中断扫描
}
row=0xFF;
lineone=0x00;
linetwo=0x00;
R=row;
if(~R)
{
delayms(16); //延时16MS,延时消抖;
row=0xFF;
lineone=0x00;
linetwo=0x00;
R=row;
if(~R)
{
keyrow=row;
Rnum=change(keyrow);
lineone=0xFF;
linetwo=0xFF;
row=0x00;
L1=lineone;
L2=linetwo;
if(~L1)
{
keylineone=lineone;
Cnum1=change(keylineone);
keycode1(); //调用扫描码转换成ASCII的函数
} //在函数里面要完成把码压入缓冲区
if(~L2)
{
keylinetwo=linetwo;
Cnum2=change(keylinetwo);
keycode2(); //调用扫描码转换成ASCII的函数
} //在函数里面要完成把码压入缓冲区
}
}
if(flag)
{
key_manage(0x00);
}
}
//-----------------------函数声明,变量定义--------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// 函数名称: delay
// 入口参数: N
// 函数功能:延时子程序,实现(16*N+24)us的延时
// 系统采用11.0592MHz的时钟时,延时满足要求,其它情况需要改动
//--------------------------------------------------------------------------------------------------
/*void delay(unsigned char a)
{
while(--a); //若12 M,下面有 i delay time/us
// 1 7 调用函数3个机器周期 RET2个机器周期
// 2 9
// 3 11 延时20us,为delay 8 time19
}*/
//--------------------------------------------------------------------------------------------------
//函数名称:void keycode1(unsigned char a,unsigned char b)和void keycode2(unsigned char c,unsigned char d)
//函数功能:将P0口和P1口,P2口的扫描码转换成相应的ASCII码,并且用函数将它压入缓冲区中。
//--------------------------------------------------------------------------------------------------
void keycode1() using 3
{
key_manage(tab1);
}
void keycode2() using 3
{
key_manage(tab2);
}
//------------------------------------------------------------------------------------------------
// 函数名称: key_manage
// 函数功能: 有效按键处理
// 按键计数器加1,缓存区数据后移1位
//--------------------------------------------------------------------------------------------------
void key_manage(unsigned char key_code) using 3
{
unsigned char i,j,l;
if(key_buf!=key_code)
{
if(sl==1)
{
Skey_manage(0x00);
}
if(spl==1)
{
SAkey_manage(0x00);
}
else
{
for(l=16;l>0;l--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=0xF0; //将断码符号送入缓冲区
for(j=16;j>0;j--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=key_buf; //将断码以前的送入缓冲区
flag=0; //缓冲区头为断码
key_count=key_count+2;
}
}
else
{
switch(key_code)
{
case 0xFF: Skey_manage(0x1F);break; //也可以goto语句跳转到句末
case 0xFE: Skey_manage(0x14);break;
case 0xFD: Skey_manage(0x27);break;
case 0xFC: Skey_manage(0x11);break;
case 0xFB: Skey_manage(0x2F);break;
case 0xFA: SAkey_manage(0x7C);break;
case 0xF9: SSkey_manage();break;
case 0xF8: Skey_manage(0x70);break;
case 0xF7: Skey_manage(0x6C);break;
case 0xF6: Skey_manage(0x7D);break;
case 0xF5: Skey_manage(0x71);break;
case 0xF4: Skey_manage(0x69);break;
case 0xF3: Skey_manage(0x7A);break;
case 0xF2: Skey_manage(0x75);break;
case 0xF1: Skey_manage(0x6B);break;
case 0xF0: Skey_manage(0x72);break;
case 0xEF: Skey_manage(0x74);break;
case 0xEE: Skey_manage(0x4A);break;
case 0xED: Skey_manage(0x5A);break;
default: break;
}
if(spe==0)
{
for(i=16;i>0;i--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf= key_code; //将键值送入缓冲区
flag=1;
key_count++;
}
spe=0;
} //缓冲区头为通码。
if(key_count>16)
{
key_count=16;
}
}
//--------------------------------------------------------------------------------------------------
// 函数名称: send_byte
// 函数功能: 发送一字节数据
//--------------------------------------------------------------------------------------------------
void send_byte(void)
{
unsigned char i,temp,keycode;
bit check;
if(KBCLK==0)
{
delayus(4); //延时20us
_nop_();
if(KBCLK==0)
{
return;
}
}
delayus(17);
if(KBCLK==0)
{
return;
}
if(KBDATA==0)
{
receive_byte();
return;
}
if(command_count>0)
{
command_count=command_count-1;
keycode=command_buf;
}
else if(key_count>0)
{
key_count=key_count-1;
keycode=key_buf;
}
else
{
return;
}
huancun=keycode;
temp=keycode; //16进制的位,赋值一个机器周期,寻找数据位置一个周期
ACC=keycode;
check=!P; //给bit赋值时,赋值一个机器周期,寻找数据位置一个周期,寻找位地址一个机器周期
KBDATA=0;
delayus(4); //延时19us
KBCLK=0; //发送起始位 2个机器周期,这两个机器周期一个算在前,一个算在后面。
_nop_();
_nop_();
_nop_();
_nop_(); //nop用了补充for语句第一次运行时少用的4个机器周期
for(i=8;i>0;i--) //for语句第一次调用,用两个机器周期,第二次调用用6个机器周期,执行完最后用6个机器周期
{
delayus(11); //延时33us
KBCLK=1;
delayus(2); //延时15us
_nop_();
KBDATA=(temp&0x01);//发送数据&运算双目运算符,先运算“&”之后再对KNDATA赋值,所有的赋值应该算入前面的计算
temp=temp>>1; // ">>"双目运算符 赋值占用两个机器周期
delayus(2); //延时15us
KBCLK=0; //延时1US
}
delayus(11); //延时33us,发送校验位
KBCLK=1;
delayus(3); //延时17us
KBDATA=check; //发送校验位 三个机器周期,因为check是可变量所以调用需要3个机器周期
delayus(4); //延时19us
KBCLK=0;
delayus(14); //延时37us,发送停止位。
KBCLK=1;
delayus(4); //延时19us
KBDATA=1; //发送停止位
delayus(4); //延时19us
KBCLK=0;
delayus(19); //延时49us
KBCLK=1; //释放时钟线
delayus(300);
return;
}
//--------------------------------------------------------------------------------------------------
// 函数名称: receive_byte
// 函数功能: 接收一子节数据
//--------------------------------------------------------------------------------------------------
void receive_byte(void)
{
unsigned char i;
bit check,jy;
KBCLK=0;
delayus(13); //延时37us
for(i=8;i>0;i--)
{
KBCLK=1; //接收起始位,丢弃
delayus(3); //延时17us
redata_bit0=KBDATA; //对位赋变量值 占3个字节
redata=redata<<1; //移动加赋值占3个字节
delayus(2); //延时15us
_nop_();
KBCLK=0;
delayus(11); //延时33us
}
KBCLK=1;
delayus(3); //延时17us
jy=KBDATA; //读取校验位,3个机器周期
delayus(4); //延时19us
KBCLK=0;
delayus(14); //延时39us
KBCLK=1;
delayus(14); //停止位的接收,直接忽略了检测,本来应该检验是0;
KBCLK=0;
delayus(14);
KBCLK=1;
delayus(2); //应答位的发送15us
KBDATA=0;
_nop_(); //延时5us
_nop_();
_nop_();
_nop_();
_nop_();
KBCLK=0;
delayus(14); //延时39us
KBCLK=1;
_nop_(); //延时5us
_nop_();
_nop_();
_nop_();
_nop_();
KBDATA=1;
ACC=redata;
check=P;
if(check==jy)
{
redata=0xAA; //校验错误,让电脑重发。
}
if((ledne==1)&&(redata!=0xFE))
{
set_led(redata);
}
manage(redata);
}
//--------------------------------------------------------------------------------------------------
// 函数名称: manage
// 函数功能: 主机命令处理函数
//--------------------------------------------------------------------------------------------------
void manage(unsigned char rec_data)
{
switch(rec_data)
{
case 0xFF: send_command(0xFA);bat();break;
case 0xFE: re_send();break;
case 0xF6: send_command(0xFA);break; //载入缺省的机打速率/延时,按键类型(所有按键都使能机打/通码/断码),
//以及第二套扫描码集
case 0xF5: send_command(0xFA);keyne=0;break;//禁止键盘扫描
case 0xF4: send_command(0xFA);keyne=1;break;//开放键盘扫描
case 0xF2: send_command(0xFA);send_command(0xAB);send_command(0x83);break;
case 0xEE: send_command(0xEE);break;
case 0xED: ledne=1;send_command(0xFA);break;
case 0xAA: send_command(0xFA);send_command(0xFE);break;
default: send_command(0xFA);break;
}
}
//--------------------------------------------------------------------------------------------------
//将16位数据转换成10进制
//--------------------------------------------------------------------------------------------------
unsigned char change(unsigned char shiliu) using 3
{
unsigned char num;
switch(shiliu)
{
case 0xFE: num=0; break;
case 0xFD: num=1; break;
case 0xFB: num=2; break;
case 0xF7: num=3; break;
case 0xEF: num=0; break;
case 0xDF: num=1; break;
case 0xBF: num=2; break;
case 0x7F: num=3; break;
default: break;
}
return(num);
}
//--------------------------------------------------------------------------------------------------
//键盘诊断自检或者复位。完成键盘所有功能和时间上的初始化。
//--------------------------------------------------------------------------------------------------
void bat(void)
{
TMOD=0x11; //定时器0和1都工作在方式1
EA=1;
IP=0x02; //中断优先级为定时器0为为高优先级。IP ---PSPT1PX1PT0PX0
//Timer0_init(); //将定时器0和1进行初始化。
Timer1_init();
//set_scan_v(); //设置初始缺省拍发速率,屏蔽设置拍发速率的函数值,而在接收PC命令的时候处理此状态
clr_buf(); //*定义扫描码集,置所有按键为机打/通码/断码*
set_led(0x07); //将3个灯全部点亮。
ssend_byte();
//send_command(0xAA); //向PC发送命令0xAA表示自检通过。
delays(); //做一个秒级别的延时。
set_led(0x00); //将3个灯全部熄灭。
}
//--------------------------------------------------------------------------------------------------
//键盘灯灯显示函数
//--------------------------------------------------------------------------------------------------
void set_led(unsigned char a)
{
if(a&0x01)
{
LEDNUM=0; //NUMLOCK指示灯
}
else
{
LEDNUM=1;
}
if(a&0x02)
{
LEDCAP=0; //NUMCAP指示灯
}
else
{
LEDCAP=1;
}
if(a&0x04)
{
LEDSCR=0; //NUMSCR指示灯
}
else
{
LEDSCR=1;
}
ledne=0;
}
//--------------------------------------------------------------------------------------------------
//对于命令的缓存。
//--------------------------------------------------------------------------------------------------
void send_command(unsigned char command)
{
unsigned char i;
for(i=7;i>0;i--)
{
command_buf=command_buf; //缓冲区内数据后移1位
}
command_buf= command; //将键值送入缓冲区
/* if(command_count>8)
{
command_count=8; //缓存的按键计数多余8的时候将最高位放弃。
}
else
{
command_count++; //按键计数器加一
}*/
command_count++;
}
//--------------------------------------------------------------------------------------------------
//重发最后一个函数
//--------------------------------------------------------------------------------------------------
void re_send(void)
{
command_buf=huancun;
command_count++;
}
//--------------------------------------------------------------------------------------------------
//ms级别的延时函数;
//--------------------------------------------------------------------------------------------------
void delayms(unsigned int ms)
{
unsigned char i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}
void delays(void)
{
unsigned int i,j;
for(i=0;i<200;i++)
{
for(j=0;j<1240;j++){;}
}
}
//--------------------------------------------------------------------------------------------------
//清所有缓冲区的函数;
//--------------------------------------------------------------------------------------------------
void clr_buf(void)
{
unsigned char k,l;
for(k=8;k>0;k--)
{
command_buf=0x00; //缓冲区内数据后移1位
}
for(l=16;l>0;l--)
{
key_buf=0x00;
}
}
//--------------------------------------------------------------------------------------------------
//特殊按键的处理;
//--------------------------------------------------------------------------------------------------
void Skey_manage(unsigned char ss) using 3
{
unsigned char m,n;
if(ss==0x00) //进行断码处理
{
for(m=16;m>0;m--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=0xE0; //将断码符号送入缓冲区
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=0xF0;
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=key_buf;
key_count=key_count+3;
sl=0;
}
else
{
for(m=16;m>0;m--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=0xE0; //将断码符号送入缓冲区
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=ss;
key_count=key_count+2;
sl=1;
spe=1;
}
}
//--------------------------------------------------------------------------------------------------
//特殊按键PRINTSCAN的处理;
//--------------------------------------------------------------------------------------------------
void SAkey_manage(unsigned char kk) using 3
{
unsigned char m,n;
if(kk==0x00) //进行断码处理
{
for(m=0;m<6;m++)
{
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=tab4;
}
key_count=key_count+6;
spl=0;
}
else
{
for(m=0;m<4;m++)
{
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=tab5;
}
key_count=key_count+4;
spl=1;
spe=1;
}
}
//--------------------------------------------------------------------------------------------------
//特殊按键PAUSE的处理;
//--------------------------------------------------------------------------------------------------
void SSkey_manage() using 3
{
unsigned char m,n;
for(m=0;m<8;m++)
{
for(n=16;n>0;n--)
{
key_buf=key_buf; //缓冲区内数据后移1位
}
key_buf=tab3;
key_count=key_count+8;
}
spe=1;
}
//US级别的延时
void delayus(unsigned char a)
{
while(--a); //若12 M,下面有 i delay time/us
// 1 7 调用函数3个机器周期 RET2个机器周期
// 2 9
// 3 11 延时20us,为delay 8 time19
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
}
/*//开机自检发送0xAA
void ssend_byte(void)
{
unsigned char i,check,temp;
temp=0xAA; //16进制的位,赋值一个机器周期,寻找数据位置一个周期
ACC=0XAA;
check=!P; //给bit赋值时,赋值一个机器周期,寻找数据位置一个周期,寻找位地址一个机器周期
KBDATA=0;
delayus(4); //延时19us
KBCLK=0; //发送起始位 2个机器周期,这两个机器周期一个算在前,一个算在后面。
_nop_();
_nop_();
_nop_();
_nop_(); //nop用了补充for语句第一次运行时少用的4个机器周期
for(i=8;i>0;i--) //for语句第一次调用,用两个机器周期,第二次调用用6个机器周期,执行完最后用6个机器周期
{
delayus(11); //延时33us
KBCLK=1;
delayus(2); //延时15us
_nop_();
KBDATA=(temp&0x01);//发送数据&运算双目运算符,先运算“&”之后再对KNDATA赋值,所有的赋值应该算入前面的计算
temp=temp>>1; // ">>"双目运算符 赋值占用两个机器周期
delayus(2); //延时15us
KBCLK=0; //延时1US
}
delayus(11); //延时33us,发送校验位
KBCLK=1;
delayus(3); //延时17us
KBDATA=check; //发送校验位 三个机器周期,因为check是可变量所以调用需要3个机器周期
delayus(4); //延时19us
KBCLK=0;
delayus(14); //延时37us,发送停止位。
KBCLK=1;
delayus(4); //延时19us
KBDATA=1; //发送停止位
delayus(4); //延时19us
KBCLK=0;
delayus(19); //延时49us
KBCLK=1; //释放时钟线
delayus(300); //
}*/ 自己顶一下,我觉得是 发送和接受数据部分出问题了,但是找不到·····,原来是定义的10MS扫描检测数据线,后来看别人的帖子改成扫描的方式了,不知道对不对? 看着头大:) 你没有观察一下,开机自检的过程中,主机给你发下来什么数据了? 我没有示波器·····应该是发送或者接受部分有问题···,但是我自己钻牛角尖了 ,找不出来··· 想问下 哥们的问题解决了吗能否公布下程序 期待楼主的问题能早日解决
我也是新入行做pc键盘软件驱动,有个键盘在其他pc主板上可以使用,但在主板A上使用时,连续软启动,每隔一次才能正常使用(在进bios中都能用,但选系统后进问题就出现了);用示波器捕捉波形,如果进xp时有通讯就可以正常使用,没有就不能使用,他们都说是时序问题,我做了些调整还是没解决(这键盘在其他pc机可以正常使用,热重启12次没有一次出差错),标准ps2键盘可以正常使用.
也请高手指点下
页:
[1]