PS\2鍵盤通信(只寫了接收部分因為按鍵碼太多譯碼可以根據需要選擇性翻譯)
#include //STC12C5AxxS2系列單片機頭文件
#include//包函_nop_延時函數的頭文件
#define uchar unsigned char//宏定義
#define uint unsigned int//宏定義
//-------------------------------------------------------------------------------------
//全局聲明部分
sbit CLK=P3^2; //時鐘線
sbit DATA=P1^0; //數據線
sbit LED_zs=P0^0; //運行指示LED
char DATA_Z[6]; //按鍵碼緩沖區
int D_Z=0; //按鍵碼緩沖區隊尾指針
char DATA_K[6]; //鍵盤控制指令緩沖區
int D_K=0; //控制指令緩沖區隊尾指針
//-------------------------------------------------------------------------------------
void DELAY_MS (unsigned int a){
unsigned int i;
while( --a != 0){
for(i = 0; i < 500; i++);
}
}
/*********************************************************************************************/
函數名:UART串口初始化函數
void UART_init (void){
EA = 1; //允許總中斷(如不使用中斷,可用//屏蔽)
//ES = 1; //允許UART串口的中斷
TMOD = 0x20; //定時器T/C1工作方式2
SCON = 0x50; //串口工作方式1,允許串口接收(SCON = 0x40 時禁止串口接收)
TH1 = 0xF3; //定時器初值高8位設置
TL1 = 0xF3; //定時器初值低8位設置
PCON = 0x80; //波特率倍頻(屏蔽本句波特率為2400)
TR1 = 1; //定時器啟動
}
/*********************************************************************************************/
函數名:UART串口接收中斷處理函數
void UART_R (void) interrupt 4 using 1{ //切換寄存器組到1
unsigned char UART_data; //定義串口接收數據變量
RI = 0; //令接收中斷標志位為0(軟件清零)
UART_data = SBUF; //將接收到的數據送入變量 UART_data
//用戶函數內容(用戶可使用UART_data做數據處理)
//SBUF = UART_data; //將接收的數據發送回去(刪除//即生效)
//while(TI == 0); //檢查發送中斷標志位
//TI = 0; //令發送中斷標志位為0(軟件清零)
}
/*********************************************************************************************/
函數名:UART串口發送函數
void UART_T (unsigned char UART_data){ //定義串口發送數據變量
SBUF = UART_data; //將接收的數據發送回去
while(TI == 0); //檢查發送中斷標志位
TI = 0; //令發送中斷標志位為0(軟件清零)
}
/*********************************************************************************************/
函數名:外部中斷INT初始化函數
void INT_init (void){
EA = 1; //中斷總開關
EX1 = 1; //允許外部中斷1中斷
EX0 = 1; //允許外部中斷0中斷
IT1 = 1; //1:下沿觸發 0:低電平觸發
IT0 = 1; //1:下沿觸發 0:低電平觸發
}
/*********************************************************************************************/
函數名:外部中斷INT1中斷處理程序
void INT_1 (void) interrupt 2 using 2{ //切換寄存器組到2
//用戶函數內容
}
/*********************************************************************************************/
函數名:外部中斷INT0中斷處理程序
void INT_0 (void) interrupt 0 using 2{ //切換寄存器組到2
//必須在鍵盤上電前完成中斷的準備工作,開始等待中斷后鍵盤在上電,上電后鍵盤如果斷電
//的話主程序也必須重啟否則時鐘可能會和數據部同步,這可能就是PS/2鍵盤不支持熱插拔的原因
unsigned char dat ; //用于存放接收到的一個完整按鍵碼
unsigned int i; //用于循環標記
//開始接收一幀11位的標準幀
EX0=0; //處理一個字節時先關中斷避免重復中斷
while(CLK==0);
while(CLK==1); //因為第一位為起始碼沒保留意義所以丟棄
for(i=0;i<8;i++) //獲取八位按鍵碼
{
while(CLK==0); //等待下一位數據的到來
while(CLK==1); //等待時鐘線下降沿
_nop_(); //延時兩個周期待數據穩定后開始獲取一位數據
_nop_();
dat=dat>>1; //因為按鍵碼數據是小頭數據所以右移后存儲數據位
if(DATA==1)
{
dat=dat|0x80;
}else
{
dat=dat&0x7f;
}
}
while(CLK==0); //丟棄奇偶校驗位(因為不是很重要的數據就不做奇偶校驗了)
while(CLK==1);
while(CLK==0); //丟棄停止位,等待鍵盤釋放時鐘線
if(D_Z<6)//如果緩沖區隊列沒滿就把鍵碼數據入隊否則丟丟棄這次按鍵
{
DATA_Z[D_Z]=dat;
D_Z++; //隊尾后移
}
LED_zs=!LED_zs; //有按鍵碼的話指示燈閃爍
DELAY_MS(3); //。。。。。!這個延時長度非常重要,因為按鍵釋放碼和特殊按鍵碼都是多幀延這個延時長了會丟幀,太短了又會陷入死循環。。。!
CLK=0;//抑制鍵盤發送等待主機處理數據
}
/**********************************************************************************************/
//--------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------
//主入口點函數
void main()
{
uint i,j,z; //用于循環標記
INT_init(); //外部中斷初始化
UART_init(); //串口初始化
for(i=0;i<6;i++)
{
DATA_Z[i]=0;
}
while(1)
{
for(z=0;z<6;z++) //發送緩沖區所有緩存按鍵碼
{
if(DATA_Z[0]!=0) //如果按鍵碼緩沖區有數據就將隊列最前的數據發出去,然后發送過的數據出隊
{
UART_T(DATA_Z[0]);
for(i=0,j=1;j<6;i++,j++) //隊列前移
{
DATA_Z[i]=DATA_Z[j];
}
DATA_Z[5]=0;
D_Z--; //隊尾指針前移
}
}
EX0=1;//等待鍵盤發完一幀釋放時鐘線后才能開中斷過早開中斷會導致數據出錯和死循環
CLK=1;//取消抑制鍵盤
}
}