您的位置 首页 新品

单片机驱动PS/2键盘

PS/2简介PS/2设备有主从之分,主设备采用Female插座,从设备采用Male插头.现在广泛使用的PS/2键盘鼠标均在从设备方式下工作.PS/2接口的时钟与…

PS/2简介

PS/2设备有主从之分,主设备选用Female插座,从设备选用Male插头.现在广泛运用的PS/2键盘鼠标均在从设备方法下作业.PS/2接口的时钟
与数据线都是集电极开路结构,有必要外接上拉电阻(一般上拉电阻设置在主设备中).主从设备之间数据通讯选用双向同步串行方法传输,时钟信号由从设备产生.

1.1 从设备到主设备的通讯
当从设备向主设备发送数据时,首要查看时钟线,以承认时钟线是否为高电平.如果是高电平,从设备就可以开端传输数据;反之,从设备要等候取得总线的控制权,才干开端传输数据.传输的每一帧由11位组成,发送时序及每一位的意义如图2所示.

每一帧数据中开端位总是为0,数据校验选用奇校验方法,中止位一直为1.从设备到主设备通讯时,从设备总是在时钟线为高时改动数据线状况,主设备在时钟下降沿读人数据线状况.

1.2 主设备到从设备的通讯
主设备与从设备进行通讯时,主设备首要将时钟线和数据线设置为“恳求发送”状况,详细方法为:首要下拉时钟线至少100us按捺通讯,然后下拉数据线“恳求发送”,最终开释时钟线.在此过程中,从设备在不超越10us的距离内有必要查看这个状况,当设备检测到这个状况时,它将开端产生时钟信号.此刻数据传输的每一帧由12位构成,其时序和每一位意义如图3所示.

与从设备到主设备通讯比较,其每帧数据多了一个ACK位.这是从设备应对接收到字节的应对位,由从设备经过拉低数据线产生,应对位ACK总
是为0.主设备到从设备通讯过程中,主设备总是在时钟线为低电平时改动数据线的状况,从设备在时钟上升沿读人数据线状况.

2.1 PS/2键盘的编码
现在,PC机运用的PS/2键盘都默许选用第2套扫描码集.扫描码有两种不同的类型:“通码(make code)”和“断码(break code)”.当一个键被按下或继续按住时,键盘会将该键的通码发送给主机;而当一个键被开释时,键盘会将该键的断码发送给主机.依据键盘按键扫描码的不同,可将按键分为3类:
第1类按键 通码为一个字节,断码为0xF0+通码方式.如A键,其通码为0x1C;断码为0xF0 0x1C.
第2类按键 通码为两字节0xE0+0xXX方式,断码为0xE0+0xF0+0xXX方式.如Right Ctrl键,其通码为0xE0 0x14;断码为0xE0 0xF0 0x14.
第3类特别按键 有两个,Print Screen键,其通码为0xE0 0x12 0xE0 0x7C;断码为0xE0 0xF0 0x7C 0xE0 0xF0 0x12.Pause键,其通码为0xE1 0x14 0x77 0xE1 0xF0 0xl4 0xF0 0x77;断码为空.
组合按键扫描码的发送是依照按键产生的次第,如按下面次序按左Shift十A键:① 按下左Shift键;② 按下A键;③ 开释A键;④ 开释左Shift键,那么计算机上接收到的一串数据为0x12 0x1C 0xF0 0x1C 0xF0 0x12.

2.2 PS/2键盘的指令集
主机可经过向PS/2键盘发送指令对键盘进行设置或许取得键盘的状况等操作.每发送一个字节,主机都会从键盘取得一个应对0xFA(“重发
resend”和“回应echo”指令破例).驱动程序在键盘初始化过程中所用的指令:0xED,主机在该指令后跟从发送一个参数字节,用于指示键盘上Num Lock,Caps Lock,Scroll Lock Led的状况;0xF3,主机在这条指令后跟从发送一个字节参数界说键盘机打的速率和延时;0xF4,用于当主机发送0xF5制止键盘后,从头使能键盘.

衔接电路图:

规划程序思路:

选用状况机思想来解码一帧数据,界说四个状况:PS_IDLE 、PS_START、PS_PARITY、PS_STOP

PS_IDLE状况承受开始码,并判别开始码是否有用;PS_START状况承受8位数据;PS_PARITY状况承受奇偶校验位;PS_STOP承受中止为,并奇偶校验数据,一起处理shift按键以及断码问题;

界说结构体

typedef struct ps2_frame
{
uchar state ;//状况
uchar data ;//数据
uchar temp ;//用于移位
uchar parity ;//奇偶校验位
uchar count ;//1的位数因为奇偶校验
uchar ready ;//一帧数据承受结束
uchar shift ;//shift键是否按下
uchar down ;//按键是否弹起
} ps2_frame ;

下面是程序:

头文件

#include “main.h”

#define PS_DATA RA1
#define PS_CLK RB0

#define PS_IDLE 0x01
#define PS_START 0x02
//#define PS_DATA 0x03
#define PS_PARITY 0x04
#define PS_STOP 0x05
//#define PS_ACK
typedef struct ps2_frame
{
uchar state ;//状况
uchar data ;//数据
uchar temp ;//用于移位
uchar parity ;//奇偶校验位
uchar count ;//1的位数因为奇偶校验
uchar ready ;//一帧数据承受结束
uchar shift ;//shift键是否按下
uchar down ;//按键是否弹起
} ps2_frame ;

void init_ps2() ;
uchar ps_decoding(uchar data,uchar shift) ;
#endif

初始化宽和码子程序以及解码表

#include “ps2.h”

const uchar unshifted[][2]=//shift键没按下译码表
{
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,/,
0x54,[,
0x55,=,
0x5b,],
0x5d,//,
0x61,<,
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
};
const uchar shifted[][2]= //shift键按下译码表
{
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,+,
0x5b,},
0x5d,|,
0x61,>,
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
};

void init_ps2()
{
ADCON1=0X07;//A口为一般IO
TRISA1=1 ;
INTCON=0 ;
INTEDG=1 ;
INTE=1 ;
PEIE=1 ;
GIE=1 ;
}

uchar ps_decoding(uchar data,uchar shift)
{
uchar temp ,i=0;
if(shift)
{
while(i!=255)
{
if(shifted[i][0]==data)
{
temp=shifted[i][1] ;
break ;
}
i++ ;
}
}
else
{
while(i!=255)
{
if(unshifted[i][0]==data)
{
temp=unshifted[i][1] ;
break ;
}
i++ ;
}
}
return temp ;
}

主程序:

#include “main.h”
#include “t232.h”
#include “ps2.h”

ps2_frame ps_frame ;
void interrupt main_int()
{
if(INTF)//下降沿触发
{
GIE=0 ;
INTF=0 ;
switch(ps_frame.state)
{
case PS_IDLE :
if(!PS_DATA)
{
ps_frame.ready=0 ;
ps_frame.state=PS_START ;
ps_frame.temp = 1 ;
ps_frame.count = 0 ;
ps_frame.data=0 ;
}
else
ps_frame.state=PS_IDLE ;
break ;
case PS_START :
if(PS_DATA)
{
ps_frame.data=ps_frame.data|ps_frame.temp ;
ps_frame.count++ ;
}
ps_frame.temp=ps_frame.temp<<1 ;
if(!ps_frame.temp)
ps_frame.state=PS_PARITY ;
break ;
case PS_PARITY :
ps_frame.parity=PS_DATA ;
ps_frame.state=PS_STOP ;
break ;
case PS_STOP :
if(PS_DATA)
{
if(ps_frame.parity)
{
if(ps_frame.count%2==0)
ps_frame.ready=1 ;

}
else
{
if(ps_frame.count%2==1)
ps_frame.ready=1 ;
}
switch(ps_frame.data)//处理通码和断码
{
case 0xF0 :
ps_frame.down=0 ;
ps_frame.ready=0 ;
break ;
case 0x12 :
if(!ps_frame.down)
ps_frame.shift=0 ;
else
ps_frame.shift=1 ;
ps_frame.ready=0 ;
break ;
case 0x59 :
if(!ps_frame.down)
ps_frame.shift=0 ;
else
ps_frame.shift=1 ;
ps_frame.ready=0 ;
break ;
default :
if(!ps_frame.down)
{
ps_frame.ready=0 ;
ps_frame.down=1 ;
}
break ;
}
}

ps_frame.state = PS_IDLE ;
break ;
default :
break ;
}
}
GIE=1 ;
}

void init_all()
{
init_232() ;
init_ps2() ;
ps_frame.state = PS_IDLE ;
ps_frame.shift=0 ;
ps_frame.down = 0 ;
}
void main()
{
const char str[]= “hello world !” ;
uchar temp ;
init_all() ;

send_str(str) ;//测验串口
while(1)
{
if(ps_frame.ready)
{
temp=ps_decoding(ps_frame.data,ps_frame.shift) ;

put_char(temp) ;
ps_frame.ready=0 ;
}
}
}

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/xinpin/276628.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部