赞
踩
1 PS/2协议
目前,PC机广泛采用的PS/2接口为mini-DIN 6pin的连接器,如图1所示.
PS/2设备有主从之分,主设备采用Female插座,从设备采用Male插头.现在广泛使用的PS/2键盘鼠标均在从设备方式下工作.PS/2接口的时钟
与数据线都是集电极开路结构,必须外接上拉电阻(一般上拉电阻设置在主设备中).主从设备之间数据通信采用双向同步串行方式传输,时钟信号由从设备产生.
1.1 从设备到主设备的通信
当从设备向主设备发送数据时,首先检查时钟线,以确认时钟线是否为高电平.如果是高电平,从设备就可以开始传输数据;反之,从设备要等待获得总线的控制权,才能开始传输数据.传输的每一帧由11位组成,发送时序及每一位的含义如图2所示.
每一帧数据中开始位总是为0,数据校验采用奇校验方式,停止位始终为1.从设备到主设备通信时,从设备总是在时钟线为高时改变数据线状态,主设备在时钟下降沿读入数据线状态.
1.2 主设备到从设备的通信
主设备与从设备进行通信时,主设备首先将时钟线和数据线设置为“请求发送”状态,具体方式为:首先下拉时钟线至少100us抑制通信,然后下拉数据线“请求发送”,最后释放时钟线.在此过程中,从设备在不超过10ms的间隔内必须检查这个状态,当设备检测到这个状态时,它将开始产生时钟信号.此时数据传输的每一帧由12位构成,其时序和每一位含义如图3所示.
与从设备到主设备通信相比,其每帧数据多了一个ACK位.这是从设备应答接收到字节的应答位,由从设备通过拉低数据线产生,应答位ACK总是为0.主设备到从设备通信过程中,主设备总是在时钟线为低电平时改变数据线的状态,从设备在时钟上升沿读入数据线状态.
3 PS/2键盘与单片机的连接电路
PS/2键盘与AT89C51单片机的连接方式如图4所示.P1.0接PS/2数据线;P3.2(INT0)接PS/2时钟线.因为单片机的P1,P3口内部是带上拉电阻的,所以PS/2的时钟线和数据线可以直接与单片机的P1,P3相连接.
- void ps2_sentchar(unsigned char sentchar){//ps2主设备向从设备发送数据
- unsigned char sentbit_cnt= 0x00;
- unsigned char sentchar_chk = 0x00;
- EX0=0; //关外部中断0
- //发起一个传送,发起始位
- PS2_SGN_CLOCK = 0; //将时钟线拉低并保持100 us
- delay100us();
- PS2_SGN_DATA= 0; //起始位
- PS2_SGN_CLOCK = 1;
- //发送DATA0-7
- for(sentbit_cnt=0;sentbit_cnt< 8;sentbit_cnt++){
- while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变为低
- PS2_SGN_DATA = sentchar& 0x01;//发送数据
- if(PS2_SGN_DATA) sentchar_chk++; //计算校验
- while(!PS2_SGN_CL0CK) _nop_(); //等待时钟线变高
- sentchar>>=1; //待发送数据右移一位
- }
- //发送校验位
- while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变低
- switch(sentchar_chk){
- case 0:
- case 2:
- case 4:
- case 6:PS2_SGN_DATA =1;break;//奇校验
- case 1:
- case 3:
- case 5:
- case 7:PS2_SGN_DATA = 0;break;//奇校验
- default;break;
- )
- while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高
- while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变低
- PS2_SGN_DATA =1;//发送停止位,停止位总为1
- while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高
- while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变低
- //接收ACK
- //if(PS2_SGN_DATA) error();
- //ACK信号由键盘发出,总为低电平
- while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高
- EX0= 1; //开外部中断0
- }
- void int0() interrupt 0 using 0 {//外部中断0设置为下降沿触发
- EX0=0; //关外部中断0
- switch(ps2_revchar_cnt){
- case 1:
- ……
- case 8:mcu_revchar<<=1;
- if(PS2_SGN_DATA) mcu_revchar |= 0x01;
- ps2_revchar_cnt++;
- break;
- case 0:ps2_revchar_cnt++;break; //开始位,
- case 9:ps2_revchar_cnt++;break; //校验位,可添加校验程序
- case 10: _nop_();//停止位
- ps2_revchar_cnt= 0;
- revchar_flag=1;//置接收到数据标识位
- break;
- default:break;
- }
- EX0=1;//开外部中断0
- }
- if(F0_FLAG){//接收扫描码为断码
- switch(mcu_revchar){//处理控制键
- case 0x11:ages_status&=0xF7;break;//左alt释放
- case 0x12:ages_status&=0xFE;break;//左shift释放
- case 0x14:agcs_status&=0xFD;break;//左ctrl释放
- case 0x58;if(led_status&0x04) led_status &= 0x03; //caps lock
- else led_status |=0x04;
- ps2_ledchange();
- break;
- case 0x59: agcs_status &= 0xEF;break;//右shift释放
- case 0x77: if(led_status&0x02)led_status&=0x05;//num lock
- else led_status |=0x02;
- ps2_ledchange();
- break;
- case 0x7E:if(led_status&0x01) led_status&=0x06;//scroll lock
- else led_status |=0x01;
- ps2_ledchange();
- break;
- default;break;
- }
- F0_FLAG=0;
- }
- else{//接收扫描码为通码
- if(led_status&0x04) caps_flag=1;else caps_flag = 0;
- if(led_status&0x02) num_flag =1;else num_flag =0;
- if(agcs_status&0x11) shift_flag = 1;else
- shift_flag=0;
- //扫描码键值转换
- if((caps_flag == shift_flag) || (!num_flag)) KeyVal=kb_plain_map[mcu_revchar];
- else KeyVal = kb_shift_map[mcu_revchar];
- switch(mcu_revchar)(//处理控制键或状态键
- case 0x11:agcs_status|= 0x08;//左alt按下
- Case 0x12:agcs_status|= 0x01;//左shift按下
- case 0x14:agcs_status|= 0x02;//左ctrl按下
- case 0x59:agcs_status|= 0x10;//右shift按下
- default:break;
- }
- }
5 结语
该驱动程序经Keil uVision2 编译,在AT89C51单片机上运行通过,实现了对PS/2 104键盘的支持,实现了对字符按键大小写切换,Num Lock切换、控制键及组合按键的支持.同时该程序对其他嵌入式或单片机系统中PS/2键盘的应用也有借鉴意义。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。