天天看點

ps2鍵盤測試軟體,51單片機驅動PS2鍵盤完整程式

// PS2鍵盤測試程式5(完整程式)

// 功能:1602顯示PS2鍵盤第1類按鍵的鍵值,可以顯示大小寫,顯示在第2行

// 顯示pageup、pagedown、方向鍵(上、下、左、右)的按下次數,顯示在第1行

// 顯示capslock、numlock的狀态,顯示在第1行

// 訓示燈: 接收按鍵值 P30(run) 取反。 鍵盤上電P31亮,接收按鍵值,P31滅。

// Caps 對大寫字母起作用 , 收到非字母,caps不起作用

// 按下capslock 和 numlock 鍵,鍵盤訓示燈做出相應變化。即實作了單片機向鍵盤發送指令

// 中斷接收采用電平觸發,如果用下降沿觸發,單片機向鍵盤發送指令正常,但發完指令再接收資料出錯,

// 接收到所有資料都是正常值的兩倍。

#include

sbit PS2CLK=P3^3; // PS2時鐘

sbit PS2DATA=P3^4; // PS2資料

sbit RUN=P3^0; // 運作标志

sbit P31=P3^1; // 運作标志

#define lcd_bus P0 // 資料總線

sbit rs =P2^0; //資料&指令選擇,H:寫資料,L:寫指令

sbit rw =P2^1; //讀&寫選擇,H:read,L:write

sbit e =P2^2; //讀寫使能

sbit bf =P0^7; //忙閑狀态标志位,H:内部正執行操作,L:空閑

void chk_busy(void);//檢測LCD忙閑

void init_lcd(void);//LCD初始化

void wr_comm(unsigned char comm); //寫指令

void wr_comm_no(unsigned char comm);//寫指令,不檢測忙閑

void wr_data(unsigned char dat); // 寫資料

void wr_str(unsigned char *p); //顯示字元串

unsigned char rd_lcd(void);//讀LCD資料

void delayus(unsigned char us);//延時子程式 us

void delayms(unsigned int ms); //延時子程式 ms

unsigned char bitnum=0; // 中斷次數,即接收鍵盤資料位的個數

unsigned char keyval=0; // 存放按鍵值

unsigned char lcdbuf[17]={'C','a','p',' ','0',' ','N','u','m',' ','0',' ','U','P',' ','0',0}; // 1602第1行

unsigned char lcdbuf2[17]={'0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; // 1602第2行

bit E0_flag=0,F0_flag=0,Shift_flag=0,Caps_flag=0,Num_flag=0,BF=0,Flag=0; // 标志位

unsigned char up='0',down='0',left='0',right='0',pgup='0',pgdown='0'; // 存放方向鍵按下的次數

unsigned char LED_status=0; // 鍵盤訓示燈狀态

unsigned char code unshifted[][2]= //shift鍵未按下譯碼表

{

0x0e,'`',

0x15,'q',

0x16,'1',

0x1a,'z',

0x1b,'s',

0x1c,'a',

0x1d,'w',

0x1e,'2',

0x21,'c',

0x22,'x',

0x23,'d',

0x24,'e',

0x25,'4',

0x26,'3',

0x29,' ',

0x2a,'v',

0x2b,'f',

0x2c,'t',

0x2d,'r',

0x2e,'5',

0x31,'n',

0x32,'b',

0x33,'h',

0x34,'g',

0x35,'y',

0x36,'6',

0x39,',',

0x3a,'m',

0x3b,'j',

0x3c,'u',

0x3d,'7',

0x3e,'8',

0x41,',',

0x42,'k',

0x43,'i',

0x44,'o',

0x45,'0',

0x46,'9',

0x49,'.',

0x4a,'/',

0x4b,'l',

0x4c,';',

0x4d,'p',

0x4e,'-',

0x52,''',

0x54,'[',

0x55,'=',

0x5b,']',

0x5d,'\',

0x61,' 0x69,'1',

0x6b,'4',

0x6c,'7',

0x70,'0',

0x71,'.',

0x72,'2',

0x73,'5',

0x74,'6',

0x75,'8',

0x79,'+',

0x7a,'3',

0x7b,'-',

0x7c,'*',

0x7d,'9',

0,0

};

unsigned char code shifted[][2]= //shift鍵按下譯碼表

{

0x0e,'~',

0x15,'Q',

0x16,'!',

0x1a,'Z',

0x1b,'S',

0x1c,'A',

0x1d,'W',

0x1e,'@',

0x21,'C',

0x22,'X',

0x23,'D',

0x24,'E',

0x25,'$',

0x26,'#',

0x29,' ',

0x2a,'V',

0x2b,'F',

0x2c,'T',

0x2d,'R',

0x2e,'%',

0x31,'N',

0x32,'B',

0x33,'H',

0x34,'G',

0x35,'Y',

0x36,'^',

0x39,'L',

0x3a,'M',

0x3b,'J',

0x3c,'U',

0x3d,'&',

0x3e,'*',

0x41,' 0x42,'K',

0x43,'I',

0x44,'O',

0x45,')',

0x46,'(',

0x49,'>',

0x4a,'?',

0x4b,'L',

0x4c,':',

0x4d,'P',

0x4e,'_',

0x52,'"',

0x54,'{',

0x55,'+',

0x5b,'}',

0x5d,'|',

0x61,'>',

0x69,'1',

0x6b,'4',

0x6c,'7',

0x70,'0',

0x71,'.',

0x72,'2',

0x73,'5',

0x74,'6',

0x75,'8',

0x79,'+',

0x7a,'3',

0x7b,'-',

0x7c,'*',

0x7d,'9',

0,0

};

void init_lcd(void)

{

wr_comm_no(0x38); //不檢測忙閑

delayms(5);

wr_comm_no(0x38);

delayms(5);

wr_comm_no(0x38);

delayms(5);

wr_comm_no(0x38);

delayms(5);

wr_comm(0x38); // 設定LCD為16*2顯示,5*7點陣,8位資料接口,檢測忙信号

delayus(3); // 延時11us

wr_comm(0x08); // 關閉顯示,檢測忙信号

delayus(3);

wr_comm(0x01); // 顯示清屏,檢測忙信号

delayus(3);

wr_comm(0x06); // 顯示光标自動右移,整屏不移動,檢測忙信号

delayus(3);

wr_comm(0x0c); //開顯示,不顯示光标,檢測忙信号

delayus(3);

}

void chk_busy(void)

{

lcd_bus=0xff;

rs=0;

rw=1;

;

e=1;

while(bf==1);

e=0;

}

void wr_comm(unsigned char comm)

{

chk_busy();

rs=0;//H:寫資料,L:寫指令

rw=0;

e=0;

;

lcd_bus=comm;//内容

delayus(3);

e=1;

;

e=0;

}

void wr_comm_no(unsigned char comm)

{

rs=0;//H:寫資料,L:寫指令

rw=0;

e=0;

;

lcd_bus=comm;//内容

delayus(3);

e=1;

;

e=0;

}

void wr_data(unsigned char dat)

{

chk_busy();

rs=1;//H:寫資料,L:寫指令

rw=0;

e=0;

;

lcd_bus=dat;//内容

delayus(3);

e=1;

;

e=0;

}

unsigned char rd_lcd(void)

{

unsigned char rd_data;

chk_busy();//檢測忙閑

rs=1;

rw=1;

e=1;

;

rd_data=lcd_bus;

e=0;

return rd_data;

}

void wr_str(unsigned char *s)

{

while(*s>0) //字元串以0結束

{

wr_data(*s);

s++;

}

}

void delayus(unsigned char us)

{

while(--us); // 一個循環2us

}

void delayms(unsigned int ms) //延時 n ms

{

while(ms)

{

int i;

i=110;

while(i--);

ms=ms-1;

}

}

void ps2_sentchar(unsigned char sentchar) // 單片機向ps2鍵盤發送資料

{

unsigned char sentbit_cnt= 0x00; // 資料

unsigned char sentchar_chk = 0x00; // 校驗

unsigned char i;

EX1=0; //關外部中斷1

//發起一個傳送,發起始位

PS2CLK = 0; //将時鐘線拉低并保持100 us

i=60;while(--i);

PS2DATA= 0; //起始位

i=5;while(--i);

PS2CLK = 1;

//發送DATA0-7

for(sentbit_cnt=0;sentbit_cnt< 8;sentbit_cnt++)

{

while(PS2CLK); // 等待時鐘線變為低

PS2DATA = sentchar& 0x01; // 發送資料

if(PS2DATA) sentchar_chk++; // 計算校驗

while(!PS2CLK) ; // 等待時鐘線變高

sentchar>>=1; // 待發送資料右移一位

}

//發送校驗位

while(PS2CLK); //等待時鐘線變低

switch(sentchar_chk)

{

case 0:

case 2:

case 4:

case 6: PS2DATA =1;break;//奇校驗

case 1:

case 3:

case 5:

case 7: PS2DATA = 0;break;//奇校驗

default:break;

}

while(!PS2CLK); // 等待時鐘線變高

while(PS2CLK); // 等待時鐘線變低

PS2DATA =1; // 發送停止位,停止位總為1

//while(!PS2CLK) ; // 等待時鐘線變高

//while(PS2CLK) ; // 等待時鐘線變低

//接收ACK

//if(PS2_SGN_DATA) error();

//ACK信号由鍵盤發出,總為低電平

//while(!PS2CLK) ; // 等待時鐘線變高

EX1=1; //開外部中斷1

}

void kbinter(void) interrupt 2 // 中斷接收鍵盤資料,低電平觸發

{

EX1=0; // 關外部中斷1

if((bitnum>0)&&(bitnum<9)) // 保留接收資料的第1到第8位,即D0-D7,去掉起始位、校驗位、停止位

{

keyval=keyval>>1; // 先接收到的是資料的D0位

if(PS2DATA==1)

keyval=keyval|0x80;

}

bitnum++; // 中斷1次,位數加1

while(!PS2CLK); //等待PS2CLK拉高

if(bitnum>10) // 接收完1幀資料(11位)

{

bitnum=0;

BF=1; // 接收完畢标志位置1

}

while(PS2CLK==0); // 等待時鐘線恢複高電平

EX1=1; // 開外部中斷1

}

void scancode(unsigned char codeval) // 判斷按鍵值

{

unsigned char i,j;

if(!E0_flag) // 第1類按鍵

{

if(!F0_flag) // 通碼

{

switch(codeval) // 控制鍵

{

case 0xe0: E0_flag=1; break;

case 0xf0: F0_flag=1; break;

case 0x12: Shift_flag=1; break;

case 0x59: Shift_flag=1; break;

case 0x58: Caps_flag=~Caps_flag; // 按下capslock鍵

EA=0;

delayms(2);

ps2_sentchar(0xed); // 向鍵盤發送修改訓示燈狀态的指令

delayms(2);

if(Caps_flag) // 修改訓示燈參數

{

lcdbuf[4]='1';

LED_status=LED_status|0x04;

}

else

{

lcdbuf[4]='0';

LED_status=LED_status&0x03;

}

ps2_sentchar(LED_status); // 發送參數

delayms(2);

EA=1;

break;

case 0x77: Num_flag=~Num_flag; // 按下numlock鍵

EA=0;

delayms(2);

ps2_sentchar(0xed); // 向鍵盤發送修改訓示燈狀态的指令

delayms(2);

if(Num_flag) // 修改訓示燈參數

{

lcdbuf[10]='1';

LED_status=LED_status|0x02;

}

else

{

lcdbuf[10]='0';

LED_status=LED_status&0x05;

}

ps2_sentchar(LED_status); // 發送參數

delayms(2);

EA=1;

break;

case 0xaa: P31=0; // 鍵盤上電正常,lcd顯示0xAA,P31亮。

lcdbuf2[0]='0';lcdbuf2[1]='x';lcdbuf2[2]='A';lcdbuf2[3]='A';

break;

case 0xfc: P31=0; // 鍵盤上電錯誤,lcd顯示ERR,P31亮。

lcdbuf2[0]='E';lcdbuf2[1]='R';lcdbuf2[2]='R';

break;

default:

if((codeval==0x15)||((codeval>=0x1a)&&(codeval<=0x1d))||((codeval>=0x21)&&(codeval<=0x24))||((codeval>=0x2a)&&(codeval<=0x2d))||((codeval>=0x31)&&(codeval<=0x35))||((codeval>=0x3a)&&(codeval<=0x3c))||((codeval>=0x42)&&(codeval<=0x44))||(codeval==0x4b)||(codeval==0x4d))

{ // 收到字母,capslock起作用

if(Caps_flag==Shift_flag) Flag=0;

else Flag=1;

}

else // 收到非字母,capslock不起作用

Flag=Shift_flag;

if(Flag==0) // shift 未按下

{

i=0;

while((codeval!=unshifted[i][0])&&(unshifted[i][0]!=0))

{ // 查表,将按鍵值轉換成字元,便于1602顯示

i++;

}

lcdbuf2[0]=unshifted[i][1];

lcdbuf2[1]=' ';

lcdbuf2[2]=' ';

lcdbuf2[3]=' ';

}

else // shift 按下或 capslock按下

{

j=0;

while((codeval!=shifted[j][0])&&(shifted[j][0]!=0))

{ // 查表,将按鍵值轉換成字元,便于1602顯示

j++;

}

lcdbuf2[0]=shifted[j][1];

lcdbuf2[1]=' ';

lcdbuf2[2]=' ';

lcdbuf2[3]=' ';

}

break;

}

}

else // 斷碼

{

F0_flag=0;

switch(codeval)

{

case 0x12: Shift_flag=0; break; // 左shift松開

case 0x59: Shift_flag=0; break; // 右shift松開

default: break;

}

}

}

else // 第2類按鍵

{

if(!F0_flag) // 通碼

{

switch(codeval)

{

case 0xf0: F0_flag=1; break;

case 0x75: up++; // 方向鍵 向上

if(up>'9') up='0';

lcdbuf[12]='U'; lcdbuf[13]='P'; lcdbuf[15]=up; break;

case 0x74: right++; // 方向鍵 向右

if(right>'9') right='0';

lcdbuf[12]='R'; lcdbuf[13]='T'; lcdbuf[15]=right; break;

case 0x6b: left++; // 方向鍵 向左

if(left>'9') left='0';

lcdbuf[12]='L'; lcdbuf[13]='F'; lcdbuf[15]=left; break;

case 0x72: down++; // 方向鍵 向下

if(down>'9') down='0';

lcdbuf[12]='D'; lcdbuf[13]='N'; lcdbuf[15]=down; break;

case 0x7d: pgup++; // pageup

if(pgup>'9') pgup='0';

lcdbuf[12]='P'; lcdbuf[13]='U'; lcdbuf[15]=pgup; break;

case 0x7a: pgdown++; // pagedown

if(pgdown>'9') pgdown='0';

lcdbuf[12]='P'; lcdbuf[13]='D'; lcdbuf[15]=pgdown; break;

default:break;

}

}

else // 收到斷碼,标志位清0

{

F0_flag=0;

E0_flag=0;

}

}

BF=0; // 完成1幀資料的處理,标志位清0

}

void main()

{

delayms(20);

init_lcd(); //LCD初始化

EA=1; // 開總中斷

EX1=1; // 開外部中斷1

IT1=0; // 外部中斷1低電平觸發

while(1)

{

if(BF==1) // 接收完1幀資料

{

scancode(keyval); // 查找按鍵值,并作出響應

RUN=~RUN;

}

wr_comm(0x80); // LCD第一行

wr_str(lcdbuf);

wr_comm(0xc0); // LCD第二行

wr_str(lcdbuf2);

}

}