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位
y = 0;
Disp1Char(x,y,0x20);
else
Disp1Char(--x,y,0x20);
}
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;
}
解碼程式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;
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按下之後放開,不進行操作
}
//如果收到的是掃描碼的第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] =
0x0e,'~',
0x15,'Q',
0x16,'!',
0x1a,'Z',
0x1b,'S',
0x1c,'A',
0x1d,'W',
0x1e,'@',
0x21,'C',
0x22,'X',
0x23,'D',
0x24,'E',
0x25,'$',
0x26,'#',
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
本文轉自emouse部落格園部落格,原文連結:http://www.cnblogs.com/emouse/archive/2010/08/07/2198211.html,如需轉載請自行聯系原作者