单片机驱动PS/2键盘

发布者:EnchantingEyes最新更新时间:2016-07-07 来源: eefocus关键字:单片机  驱动  PS2键盘 手机看文章 扫描二维码
随时随地手机看文章
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/2接口

设计程序思路:

采用状态机思想来解码一帧数据,定义四个状态: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 ;
  }
 } 
}

关键字:单片机  驱动  PS2键盘 引用地址:单片机驱动PS/2键盘

上一篇:单片机通过定时器来实现多任务
下一篇:单片机小白学步系列二十 IO口原理

推荐阅读最新更新时间:2024-03-16 14:59

51单片机如何访问外部接口芯片?
单片机怎要访问外部接口芯片? 答:因为MCS-51单片机的外部数据存储器RAM和I/O口是统一编址的 。因此,用户可以把外部64KB的数据存储器RAM空间的一部分作为扩展外围I/O的地址空间。这样,单片机就可以像访问外部RAM存储器那样访问外部接口芯片,对其进行读/写操作了。
[单片机]
8051单片机教程第五课:延时程序分析
上一次课中,我们已经知道,程序中的符号R7、R6是代表了一个个的RAM单元,是用来放一些数据的,下面我们再来看一下其它符号的含义。 DELAY:MOVR7,#250;(6) D1:MOVR6,#250;(7) D2:DJNZR6,D2;(8) DJNZR7,D1;(9) RET;(10) MOV:这是一条指令,意思是传递数据。说到传递,我们都很清楚,传东西要从一个人的手上传到另一个人的手上,也就是说要有一个接受者,一个传递者和一样东西。从指令MOVR7,#250中来分析,R7是一个接受者,250是被传递的数,传递者在这条指令中被省略了(注意:并不是每一条传递指令都会省的,事实上大部份数据传递指令都会有传递者
[单片机]
8051<font color='red'>单片机</font>教程第五课:延时程序分析
avr单片机USART串口通讯初始化配置及说明
avr atmega16 单片机通用同步和异步串行接收器和转发器 (USART) 是一个高度灵活的串行通讯设备,其工作模式及其初始化,寄存器说明如下。 //*****************************USART 控制和状态寄存器A(UCSRA)******************************** /*USART 控制和状态寄存器A(UCSRA) bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 RXC TXC UDRE FE DOR PE U2X MPCM RXC: USART 接收结束
[单片机]
用PIC单片机实现50Hz锁相信号发生器
    摘要: 在UPS不间断电源系统的设计中,与外部交流电压锁相的50Hz正弦信号发生器是十分关键的一部分,本文介绍了一种利用数字信号处理技术通过PIC单片机实现此电路的方法。     关键词: 单片机 信号发生器 数字信号处理 引言 不间断电源(UPS)通过逆变向用电设备提供纯净、稳定的电能,保证设备的正常运行,50Hz锁相正弦信号发生器则是其中的关键部分,作为系统输出电压的基准,不但要求它的输出信号频率和幅度稳定,还要与外部交流电的电压相位同步。 传统的交流信号发生方式采用反馈振荡电路,利用电路的自激振荡和选频作用输出正弦波,但是低频模拟振荡器有一个缺点:受电压和温度影响大,输出信号的频率和幅度
[工业控制]
飞思卡尔S12(X)系列单片机之map文件详解
本文介绍的map文件内容解析适用于Freescale S12(X)系列MCU(CodeWarrior 5.9.0) 的CodeWarrior 应用工程编译结果的map文件,结合的具体例子是基于S12XS256的工程编译链接的MAP文件。 map文件是嵌入式MCU应用工程编译链接结果的内存映射结果文件,其中输出了工程编译器、链接器配置信息、用户代码和数据编译结果,函数调用关系,存储器资源分配和使用结果统计等非常详尽的信息,可以说是对编译结果所做分门别类的最详细描述。(上面这段是百度里面搜的)。 详细介绍如下: TARGET SECTION(编译目标属性设置) 列举工程所使用的处理器类型(processor):Free
[单片机]
飞思卡尔S12(X)系列<font color='red'>单片机</font>之map文件详解
基于单片机的步进电机控制系统设计
单片机实现的步进电机控制系统具有成本低、使用灵活的特点,广泛应用于数控机床、机器人,定量进给、工业自动控制以及各种可控的有定位要求的机械工具等应用领域。步进电机是数字控制电机,将脉冲信号转换成角位移,电机的转速、停止的位置取决于脉冲信号的频率和脉冲数,而不受负载变化的影响,非超载状态下,根据上述线性关系,再加上步进电机只有周期性误差而无累积误差,因此步进电机适用于单片机控制。步进电机通过输入脉冲信号进行控制,即电机的总转动角度由输入脉冲总数决定,而电机的转速由脉冲信号频率决定。步进电机的驱动电路是根据单片机产生的控制信号进行工作。因此,单片机通过向步进电机驱动电路发送控制信号就能实现对步进电机的控制。 1 系统设计原理
[工业控制]
JSN-SR04T超声波模块驱动(模式二、STM32)
一、前期准备 单片机:STM32F103C8T6 开发环境:MDK5.14 库函数:标准库V3.5 JSN-SR04T模块:淘宝有售 二、实验效果 三、驱动原理 此模块分3中模式: (1)模式一:R27 = open,普通驱动模式; (2)模式二:R27 = 47K,串口模式,每隔100ms更新一次数据。 (3)模式三:R27 = 120K,串口模式,发0x55。 注意:次模块测试盲区20cm。 串口二接收模块数据,每隔100ms刷新次数据,串口接收数据之后,做完校验之后算出测试距离并打印出来。 需要完整工程或者有问题的请加QQ:1002521871,验证:呵呵。 四、驱动代码 JSN-SR04T.h #if
[单片机]
JSN-SR04T超声波模块<font color='red'>驱动</font>(模式二、STM32)
离线式LED驱动电路设计方案
作为一种新型的节能、环保的绿色光源产品, LED 拥有广阔的市场前景。市场现阶段已经出现上千款 LED驱动 IC 。其中我们遇到比较多的是单 芯片 线路结构(图1a)。   根据IC的数据表可知,这类IC为工作于 PWM 方式的高效LED驱动控制 电路 ,借助于外部电路,能适应从8V到450V 的宽输入 电压 范围。通过外部的 电阻 (或 电容 )可设定固定频率控制外部 功率 MOS 管,以恒流的方式可靠地 驱动 LED串。LED的 电流 可以通过选择恰当的限流电阻来设定。同时提供线性调光功能,支持低频可变占空比的数字脉冲(PWM)调光功能。 根据应用场合和按照不同的标准
[电源管理]
离线式LED<font color='red'>驱动</font>电路设计方案
小广播
添点儿料...
无论热点新闻、行业分析、技术干货……
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved