PS2键盘解码的基本原理是通过外部中断读取键盘输出的串行信号,在根据扫描码进行查表解码。键盘发送往主机的信号总是在时钟的下降沿因此此中断是在下降沿触发,且时钟信号是由键盘给出,因此使用P1口中断(已经在初始化端口时设置)。发送的数据位11位,第一位是起始位,总为0,紧接是8个数据位,然后是奇校验位,最后是停止位总为1.

     本程序只能对基本按键(即键被按下时产生三个字节的扫描码的按键)做出解码,包括所有的可显示字符键和Table,Back Space和Ente三个特殊功能键。基本按键的扫描码由三个字节组成,第1个字节为接通码,第2、3字节为断开码;其中第1字节和第3字节相同,中间字节为断开标志0xf0。例如:通码和断码是以什么样的序列发送到你的计算机使得字符G 出现在你的字处理软件里呢?因为这是一个大写字母需要发生这样的事件次序按下Shift 键按下G 键释放G 键释放Shift 键,与这些时间相关的扫描码如下:Shift 键的通码12h G键的通码34h G 键的断码F0h 34h Shift 键的断码F0h 12h 因此发送到你的计算机的数据应该是12h 34h F0h 34h F0h 12h如果按键按着不放会连续发送通码命令,可以连续显示字符(没有验证,实验验证是可以的)。

      具体的说明都已经在程序中做了注释,主程序,中断服务函数中读取键盘发送的值:

/***************************************************** 程序功能:接收并解码来自标准键盘的基本按键的扫描码 然后在1602液晶上显示。按Back Space键可以前向删除显 示字符,按Space键可以后向删除显示字符。 ----------------------------------------------------- 将拨码开关的SN74LVC2454和LCD位拨至ON 读取键盘的信号需要电平转换,注意设置SN74LVC2454的转换方向 跳线设置:将跳线座J13的B8脚和P1.7脚短接 ----------------------------------------------------- 测试说明:敲定标准键盘上的按键,观察液晶显示 *****************************************************/ #include <msp430.h> #include "cry1602.h" #include "cry1602.C" #include "PS2Keyboard.h" #include "PS2Keyboard.C" #define SIDval P5IN & BIT6 #define BufferSize 32 //显示缓存大小 unsigned char bitcount=11; //位计数变量 unsigned char kb_buffer[BufferSize]; //显示缓存 unsigned char input=0; //数据压入缓存位置指针 unsigned char output=0; //数据弹出缓存位置指针 unsigned char pebit=0xff; //奇偶校验标志位 unsigned char recdata=0; //接收到的数据 unsigned char tishi[]={"this is a demo!"}; /****************主函数****************/ void main(void) { uchar disptmp,i; uchar x = 0,y = 0; uchar first = 1; WDTCTL = WDTPW + WDTHOLD; //关闭看门狗 P6DIR |= BIT2;P6OUT &= ~BIT2; //打开电平转换 P2DIR |= BIT3;P2OUT |= BIT3; //方向5V-->3.3V /*------选择系统主时钟为8MHz-------*/ BCSCTL1 &= ~XT2OFF; // 打开XT2高频晶体振荡器 do { IFG1 &= ~OFIFG; //清除晶振失败标志 for (i = 0xFF; i > 0; i--); //等待8MHz晶体起振 } while ((IFG1 & OFIFG)); // 晶振失效标志仍然存在? BCSCTL2 |= SELM_2; //主时钟选择高频晶振 LcdReset(); //复位液晶 DispNchar(0,0,15,tishi); //液晶显示提示信息 Init_KB(); //初始化键盘端口 _EINT(); //打开全局中断 while(1) { LPM3; //进入低功耗模式 if(first) { first = 0; LcdWriteCommand(0x01, 1); //显示清屏 LcdWriteCommand(0x0f, 1); //打开游标 } disptmp = GetChar(); //读取键值对应的ASCII码 if(disptmp != 0xff) //取出了一个有效字符 { if(disptmp == 8) //如果是退格键 { if((x == 0) && (y == 0))//如果游标在第1行第1位 { x = 15; y = 1; Disp1Char(x,y,0x20); //0x20是空格的ASCII码 LocateXY(x,y); } else if((x == 0) && (y == 1))//如果游标在第2行第1位 { x = 15; y = 0; Disp1Char(x,y,0x20); LocateXY(x,y); } else { Disp1Char(--x,y,0x20); LocateXY(x,y); } } else if((disptmp == 9) || (disptmp == 13)) //如果是Table键或Enter键 { _NOP(); } else //其余字符显示 { Disp1Char(x++,y,disptmp); if(x == 16) //如果一行显示完毕 { x = 0; y ^= 1; LocateXY(x,y); //重新定位游标位置 } } } } } /******************************************* 函数名称:PORT1_ISR 功 能:P1端口的中断服务函数,在这里接收来 自键盘的字符 说明:键盘发送往主机的信号总是在时钟的下降沿 因此此中断是在下降沿触发,且时钟信号是由键盘 给出,因此使用P1口中断(已经在初始化端口时 设置)。发送的数据位11位,第一位是起始位,总 为0,紧接是8个数据位,然后是奇校验位,最后是 停止位总为1. 参 数:无 返回值 :无 ********************************************/ #pragma vector=PORT1_VECTOR __interrupt void PORT1_ISR(void) { if(P1IFG & BIT7) //如果是clock的中断 { P1IFG &=~ BIT7; //清除中断标志 if(bitcount == 11) //接收第1位 { if(SIDval) //起始位总为0如果是1就不是起始位 return; //返回 else bitcount--; //是起始位就接着接收下一位,进行计数 } else if(bitcount == 2) //接收奇偶校验位 { if(SIDval) //如果校验位等于1 pebit = 1; //这个程序中只是对校验位进行读取,正确与否并为做判断 else pebit = 0; bitcount--; } else if(bitcount == 1) //接收停止位 { if(SIDval) //若停止位正确 { bitcount = 11; //复位位计数变量 if( Decode(recdata) ) //解码获得此键值的ASCII值并保存 LPM3_EXIT; //退出低功耗模式 recdata = 0; //清除接收数据 } else //如果出错 { bitcount = 11; recdata = 0; } } else //接收8个数据位 { recdata >>= 1; if(SIDval) recdata |= 0x80; bitcount--; } } }

解码程序PS2Keyboard.C:

#include <msp430x14x.h> #include "PS2Keyboardcode.h" #define BufferSize 32 extern uchar kb_buffer[BufferSize]; extern uchar input; extern uchar output; extern uchar flag; /******************************************* 函数名称:PushBuff 功 能:将一个字符压入显示缓存,如果缓存以 满则覆盖前面的数据 参 数:c--要显示的字符 返回值 :无 ********************************************/ void PutChar(uchar c) { kb_buffer[input] = c; if (input < (BufferSize-1)) input++; else input = 0; } /******************************************* 函数名称:PopChar 功 能:从显示缓存中取出一个字符 参 数:无 返回值 :取出的字符 ********************************************/ uchar GetChar(void) { uchar temp; if(output == input) return 0xff; else { temp = kb_buffer[output]; if(output < (BufferSize-1)) { output++; } else { output = 0; } return temp; } } /******************************************* 函数名称:Init_KB 功 能:初始化与键盘相关的IO 参 数:无 返回值 :无 ********************************************/ void Init_KB(void) { P1DIR &=~ BIT7; //Clock接P1.7,设置为输入 P5DIR &=~ BIT6; //SID接P5.6,设置为输入 P1IES |= BIT7; //下降沿中断 P1IFG = 0x00; //中断标志清零 P1IE |= BIT7; //使能时钟端口中断 P1SEL = 0x00; //P1口作为IO使用 } /******************************************* 函数名称:Decode 功 能:对来自键盘的信息进行解码,转换成对 应的ASCII编码并压入缓存 参 数:sc--键盘发送过来的信息 返回值 :是否收到有效数据:0--否,1--是 说明 :本程序只能对基本按键(即键被按下时产 生三个字节的扫描码的按键)做出解码, 包括所有的可显示字符键和Table, Back Space和Enter三个特殊功能键。 基本按键的扫描码由三个字节组成,第1个字节为接通 码,第2、3字节为断开码;其中第1字节和第3字节相 同,中间字节为断开标志0xf0。 例如:通码和断码是以什么样的序列发送到你的计算机使得字符G 出现在你的字处理软件 里呢?因为这是一个大写字母需要发生这样的事件次序按下Shift 键按下G 键 释放G 键释放Shift 键,与这些时间相关的扫描码如下:Shift 键的通码12h G 键的通码34h G 键的断码F0h 34h Shift 键的断码F0h 12h 因此发送到 你的计算机的数据应该是12h 34h F0h 34h F0h 12h 如果按键按着不放会连续发送通码命令,可以连续显示字符(没有验证,实验验证是可以的) ********************************************/ uchar Decode(uchar sc) { static uchar shift = 0; //Shift键是否按下标志:1--按下,0--未按 static uchar up = 0; //键已放开标志: 1--放开,0--按下 uchar i,flag = 0; if(sc == 0xf0) //如果收到的是扫描码的第2个字节---0xf0:按键断开标志 { up = 1; return 0; } else if(up == 1) //如果收到的是扫描码的第3个字节 { up = 0; //收到第3字节表示按键已经放开 if((sc == 0x12) || ( sc==0x59)) shift = 0;//shift按下之后放开,不进行操作 return 0; } //如果收到的是扫描码的第1个字节,第一个字节为通码 if((sc == 0x12) || (sc == 0x59)) //如果是左右shift键 { shift = 1; //设置Shift按下标志 flag = 0; } else { if(shift) //对按下Shift的键进行解码 { for(i = 0;(shifted[i][0] != sc) && shifted[i][0];i++);//查表找到对应的码值 if (shifted[i][0] == sc) { PutChar(shifted[i][1]); //存入显示缓存 flag = 1; //解码成功标志 } } else //直接对按键进行解码 { for(i = 0;(unshifted[i][0] != sc) && unshifted[i][0];i++); if(unshifted[i][0] == sc) { PutChar(unshifted[i][1]); flag = 1; } } } if(flag) return 1; //根据解码是否成功返回相应的值 else return 0; }

需要查的表PS2Keyboardcode.h

//不按Shift的字符对应的编码 const unsigned char unshifted[][2] = { 0x0d,9, //Table 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,0x27, 0x54,'[', 0x55,'=', 0x5a,13, //Enter 0x5b,']', 0x5d,0x5c, 0x61,'<', 0x66,8, //Back Space 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 }; //按住Shift后字符对应的编码 const unsigned char shifted[][2] = { 0x0d,9, //Table 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,'+', 0x5a,13, //Enter 0x5b,'}', 0x5d,'|', 0x61,'>', 0x66,8, //Back Space 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 };