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;//取消抑制键盘
}
}