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
#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--;
}
}
}
|