单片机系统中PS2键盘驱动程序设计
单片机系统中ps2键盘驱动程序的设计
在单片机系统中,经常使用的键盘都是专用键盘.此类键盘为单独设计制作的,成本高、使用硬件连接线多,且可靠性不高,这一状况在那些要求键盘按键较多的应用系统中更为突出.与此相比,在PC系统中广泛使用PS/2键盘具有价格低、通用可靠,且使用连接线少(仅使用2根信号线)的特点,并可满足多种系统的要求.因此在单片机系统中应用PS/2键盘是一种很好的选择.文中在介绍PS/2协议和PS/2键盘工作原理与特点的基础上,给出了一个在单片机上实现对PS/2键盘支持的硬件连接与驱动程序设计实现.该设计实现了在单片机系统中对PS/2标准104键盘按键输入的支持.使用Keil C51开发的驱动程序接口和库函数可以方便地移植到其他单片机或嵌入式系统中.所有程序在Keil uVision2上编译通过,在单片机AT89C51上测试通过.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抑制通信,然后下拉数据线“请求发送”,最后释放时钟线.在此过程中,从设备在不超过10us 的间隔内必须检查这个状态,当设备检测到这个状态时,它将开始产生时钟信号.此时数据传输的每一帧由12位构成,其时序和每一位含义如图3所示.与从设备到主设备通信相比,其每帧数据多了一个ACK位.这是从设备应答接收到字节的应答位,由从设备通过拉低数据线产生,应答位ACK总是为0.主设备到从设备通信过程中,主设备总是在时钟线为低电平时改变数据线的状态,从设备在时钟上升沿读人数据线状态.2 PS/2键盘的编码与命令集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禁止键盘后,重新使能键盘.3 PS/2键盘与单片机的连接电路PS/2键盘与AT89C51单片机的连接方式如图4所示.P1.0接PS/2数据线;P3.2(INT0)接PS/2时钟线.因为单片机的P1,P3口内部是带上拉电阻的,所以PS/2的时钟线和数据线可以直接与单片机的P1,P3相连接.4 驱动程序设计驱动程序的开发使用Keil C51语言以及KeiluVision2编程环境.PS/2 104键盘驱动程序主要任务是实现单片机与键盘间PS/2通信,同时将接收到的按键扫描码转换为该按键的键值KeyVal,提供给系统上层软件使用.4.1 单片机与键盘间PS/2通信的程序设计在PS/2通信过程中,主设备(文中是单片机)是在时钟信号为低时发送和接收数据信号.因为单片机向键盘发送的是指令,需要键盘回应,所以这部分程序采用查询方式;而单片机接收键盘数据时,数据线上的信号在时钟为低时已经稳定,所以这部分程序采用中断方式,且不需要在程序中加入延时程序.单片机向PS/2键盘发送数据程序代码为: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 usdelay100us();PS2_SGN_DATA= 0; //起始位PS2_SGN_CLOCK = 1;//发送DATA0-7for(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;//发送停止位,停止位总为1while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变低//接收ACK//if(PS2_SGN_DATA) error();//ACK信号由键盘发出,总为低电平while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高EX0= 1; //开外部中断0}单片机由PS/2键盘接收数据程序:外部中断0设置为下降沿触发void int0() interrupt 0 using 0 {//EX0=0; //关外部中断0switch(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}4.2 键盘扫描码转换程序设计由于键盘扫描码无规律可循,因此由键盘扫描码获得相应按键的键值(字符键为其ASCII值,控制键如F1,Ctrl等为自定义值),只能通过查表的方式获得.由于按键的3种类型及部分按键对应着两个键值(如A键的键值根据Caps和Shift键状态有0x41(A)和0x61(a)两种),因此综合考虑查表转换速度和资源消耗,设计中使用4个键盘表:键盘扫描码转换基本集和切换集(kb_plain_map[NR_KEYS]与kb_shift_map[NR_KEYS]);包含E0前缀的键盘扫描码转换基本集和切换集(kbeO_plain_map[NR_KEYS]与kbe0_shiftmap[NR_KEYS]).PS/2 104键盘按键扫描码最大值为0x83,所以设置NR_KEYS为132.所有4个键盘表的定义均为如下形式:KB_MAP[MAKE CODE]=KEYVAL,如果扫描码对应的按键为空(如KB_MAP[0x00]),则定义相应键值为NULL_KEY(0x00).以下是键盘扫描码基本集的部分代码实例:kb_plain_map[NR_K EYS]={……NULL_KEY;0x2C;0x6B;0x69;0x6F;0x30;0x39;NULL_KEY; //扫描码0x40~0x47//对应按键空,逗号,K,I,O,0,9,空//对应键值0x00,',','k','i','o','O','9',0x00…… };如此设计键盘转换表的另一个好处在于,以后如需扩展支持有ACPI、Windows多媒体按键键盘时,只需要将键表中相应处修改即可,如ACPIPower按键通码为0xE0 0x37,修改kbe0_plain_map[0x37]=KB_ACPI_PWR即可.特殊按键Pause使用单独程序处理,如果接收到0xE1就转入这段程序.而Print Screen键则将其看作是两个通码分别为0xE0 0x12和0xE0 0x7C的“虚键”的组合键处理.在驱动程序中设定如下全局变量:led_status记录Scroll Lock Led,Num Lock Led和Caps Lock Led的状态(关为0,开为1);agcs_status记录左右Shift Ctrl Gui Alt状态,相应键按下则对应位为1,释放为0.E0_FLAG接到0xE0置1;E1_FLAG接收到0xE1置1;F0_FLAG接收到0xF0置1.按键键值通过KeyVal提供上层程序使用.PS/2键盘扫描码键值转换程序ps2_codetrans()流程框架如图5所示.第1类按键的扫描码键值转换程序代码。
C51程序PS2
}
}
void getkey() interrupt 1 //内部中断0 用来处理缓冲区里的数据
{
unsigned char i=0;
tr0=0;
th0=0;
tl0=0;
count=0; //中断记数则0
if((temp[0]==18 || temp[0]==89) && temp[1]!=0xf0 ) //shift被按下
{
key=noshift[i][1];
ie=0x83;
return;
}
}
}
for(i=0;i<5;i++)
{
temp[i]=0;
}
}
unsigned char count=0,num=9,temp[5],shu=0; //中数次数 中断控制变量 缓冲区数组 缓冲区指针
unsigned char key=0; //按键最终值
void zhongduan() interrupt 0 //外部中断0 用来接受键盘发来的数据
{
dat>>=1; //接受数据 低->高
tr0=1;
}
else
{
temp[shu]=dat1;temp[shu+1]=dat2; shu+=2; //如果shift键被按下则记录与它同时按下的那个键
count=0;
}
if((temp[0]==18 || temp[0]==89) && (temp[2]==18 || temp[2]==89) ) tr0=1; //如果缓冲区中有两个间隔的shift键则证明需要的铵键结束
用单片机来模拟ps2电脑键盘的程序
用单片机来模拟ps2电脑键盘的程序//本程序可实现用单片机来代替ps2键盘来给电脑输入数据.//您只需从单片机上引出2根线到电脑的ps/2接口就可实现//本程序在51hei-5开发板上测试通过//跳线设置:默认//晶振:6M#include<reg51.h> //包含单片机寄存器的头文件#include<intrins.h> //包含_nop_()函数定义的头文件//#include"51hei.h"/************************************************************** *****************以下是引脚定义*************************************************************** ****************/sbit PS2CLK=P3^5;sbit PS2DAT=P2^7;/*****************************************************函数功能:延时1ms(3j+2)*i=(3×33+2)×10=1010(微秒),可以认为是1毫秒***************************************************/void delay1ms(){unsigned char i,j;for(i=0;i<10;i++)for(j=0;j<33;j++);}//===================================== =================================//2051模拟PS2键盘和PC机通讯程序 6M下发送程序代码//程序的完整版本及hex文件下载地址://===================================== =================================PS2keytohost(unsigned char vale){unsigned char h = 0;unsigned char i = 8;unsigned char J;unsigned char t;bit bparity =0 ;ACC = vale;//获取字节的奇偶信息if(!P) //ACC中偶数时,P为0,但是PS2中时奇校验( 字节中的1的个数+校验位 = 奇数){ bparity = 1; }if(PS2CLK&&PS2DAT) //发送前检测PS2总线{for(J = 12 ;J > 0;J--) {;}//6M,大约延时40uS后再检测if(PS2CLK)//时钟线空闲{if(PS2DAT)//数据线空闲{//for(t = 10;t > 0;t--){;} //6M,大约延时20uSfor(J = 11;J > 0;J--) //1共11个数据{if(h == 0) // 送起始位{PS2DAT = 0;h++;}else if(h == 1)//送8位数据位{PS2DAT = vale & 0x01;//先LSB开始vale >>= 1;i--;if(i == 0) //发送完成{ h++; }}else if(h == 2)//送校验位{PS2DAT = bparity;h++;}else{ PS2DAT = 1; } //送停止位for(t = 12;t > 0;t--){;} //6M,大约延时40uS PS2CLK = 0;//拉低时钟线,主机接收for(t = 12;t > 0;t--){;} //6M,大约延时40uS PS2CLK = 1;//拉高时钟线,设备发送if(!PS2CLK) //检测到时钟线变低{ //主机不要这次通讯 (很罕见)return(0);//返回 0}for(t = 6;t > 0;t--){;} //6M,大约延时20uS}//for(J = 11;J > 0;J--)for(t = 12;t >0;t--){;}return(1);//返回 1}//if(PS2DAT)}//if(PS2CLK)}//if(PS2CLK&&PS2DAT)}//end/***************************************************** 函数功能:主函数***************************************************/ void main(void){PS2keytohost(0X1C);while(1){delay1ms(); }}。
51单片机控制PS2键盘头文件
51单片机控制PS2键盘头文件51单片机控制PS2键盘是DM51的一个有用接口,光盘中程序给出了调试过的,如果有用户丢失了头文件,请将下面文件存为ps2.h转载请注明出处。
//===========================ps2.h头文件=============================// #ifndef PS2_H#define PS2_Hsbit keydata=P1^7;sbit clk=P3^2;unsigned char times=0;unsigned char i=0;unsigned char keycode=0,ps2_key; //ps2_key用于存放接收到的键码static unsigned char BF=0; //标识是否有字符被收到unsigned char code noshift[80][2]={1 , 8,// { f9 }3 , 4,// { f5 }4 , 2,// { f3 }5 , 0,// { f1 }6 , 1,// { f2 }7 ,11,// { f12 }9 , 9,// { f10 }13 ,25,// { tab }20 ,27,// { ctrl }41 ,29,// { space } 31 ,30,// { win } 12 , 3,// { f4 }11 , 5,// { f6 }10 , 7,// { f8 }14 ,96,// { ` }22 ,49,// { 1 }28 ,97,// { a }30 ,50,// { 2 }33 ,99,// { c }38 ,51,// { 3 }37 ,52,// { 4 }46 ,53,// { 5 }47 ,31,// { winright} 54 ,54,// { 6 }61 ,55,// { 7 }62 ,56,// { 8 }50 ,98,// { b }35 ,100,// { d }36 ,101,// { e }43 ,102,// { f }52 ,103,// { g }51 ,104,// { h }59 ,106,// { j }58 ,109,// { m }49 ,110,// { n }21 ,113,// { q }45 ,114,// { r }27 ,115,// { s }60 ,117,// { u } 42 ,118,// { v }29 ,119,// { w } 34 ,120,// { x }53 ,121,// { y }26 ,122,// { z }65 ,44,// { , }66 ,107,// { k }67 ,105,// { i }68 ,111,// { o }69 ,48,// { 0 }70 ,57,// { 9 }73 ,46,// { . }74 ,47,// { / }75 ,108,// { l }76 ,59,// { ; }77 ,112,// { p }78 ,45,// { - }82 ,39,// { ' }85 ,61,// { = }84 ,91,// { [ }91 ,93,// { ] }88 ,26,// { caps } 93 ,92,// { \ }90 ,32,// { enter } 120,10,// { f11 } 102,12,// { back } 224,13,// { home } 105,14,// { end }125,15,// { pageup }122,16,// { pagedown }117,17,// { up }114,18,// { down }107,19,// { left }116,20,// { right }113,21,// { del }112,22,// { insert }225,23,// { pause }118,24,// { esc }131, 6,// { f7 }};unsigned char code addshift[47][2]= {14,126, // { ~ }22, 33, // { ! }30, 64, // { @ }38, 35, // { # }37, 36, // { $ }46, 37, // { % }54, 94, // { ^ }61, 38, // { & }62, 42, // { * }70, 40, // { ( }69, 41, // { ) }78, 95, // { _ }85, 43, // { + }93,124, // { | }84,123, // { { }76, 58, // { : } 82, 34, // { " } 65, 60, // { < } 73, 62, // { > } 74, 63, // { ? } 28 ,65,// { a } 50 ,66,// { b } 33 ,67,// { c }35 ,68,// { d }36 ,69,// { e } 43 ,70,// { f } 52 ,71,// { g } 51 ,72,// { h } 67 ,73,// { i } 59 ,74,// { j } 66 ,75,// { k } 75 ,76,// { l } 58 ,77,// { m } 49 ,78,// { n } 68 ,79,// { o } 77 ,80,// { p } 21 ,81,// { q } 45 ,82,// { r } 27 ,83,// { s } 44 ,84,// { t } 60 ,85,// { u } 42 ,86,// { v } 29 ,87,// { w } 34 ,88,// { x }26 ,90,// { z }};unsigned char getchar(unsigned char k) //转换键码为ASCII码{unsigned char j;if(!i)for(j=0;j<80;j++){if(noshift[j][0]==k){ps2_key=noshift[j][1];return 1;}}elsefor(j=0;j<47;j++){if(addshift[j][0]==k){ps2_key=addshift[j][1];return 1;}}return 0;}void Keyboard_out(void) interrupt 0{if(times<9){keycode=keycode>>1; //因键盘数据是低>>高,结合上一句所以右移一位if(keydata) keycode=keycode | 0x80; //当键盘数据线为1时为1到最高位 }times++;if(times>10){times=0;if(keycode==0xe0 || keycode==0xf0){}//return;}else if((keycode==18 || keycode==89) && i==0){i=1;}else if((keycode==18 || keycode==89) && i==1){i=0;}else {EX0=0;BF=1;} //关中断等显示完后再开中断//当中断11次后表示一帧数据收完,清变量准备下一次接收//(注:如这里不用BF和关中断直接调Decode()//则所Decode中所调用的所有函数要声明为再入函数)}// while(!clk); //等待PS/2CLK拉高}#endif。
单片机与PS2键盘接口程序
/*---------------------------------------------
2个放键事件处理(集中管理)
----------------------------------------------*/
PS2Buffers.PS2Keyco u* nt = 0;//脉冲计数归零
if (PS2DATA){//高电平是停止位
if (key == 0xe0){//本次是扩展键
PS2Buffers.PS2KeyExtFlage = 0xe0;//置扩展键标志(小键盘只有回车键)
}
else if (key == 0xf0){//本次是键断码,键释放
void int1proc() interrupt IE1_VECTOR using 1
{
unsigned ch* ar i, key;
code unsigned ch* ar PS2TAB[] = {//20键PS2小键盘键码表
0x70,//0
0x69,//1
0x72,//2
0x7a,//3
计数器清零
PS2CLOCK = 1;//释放PS2时钟总线
}
}
}
else PS2Buffers.PS2KeyPopError = 0xed;//置停止位错误号0xed
}
else PS2Buffers.PS2Keyco u* nt = 0;//PS2键盘出错
}
/*------------------------------------------------------------------
PS2键盘的设计---C51程序
PS2键盘的设计---C51程序+详细注释(转)PS2键盘的设计---C51程序#include <reg51.H>#define Frequence 11 //晶振频率单位是MHZ#define DELAY 10*Frequence/6 //发送程序延时#define SLEEP 8*Frequence/6 //发送程序延时sbit KBCLK="P3"^0; //键盘时钟线sbit KBDATA="P3"^1; //键盘数据线bit bat(void); //基本保证测试无错误返回0,有错返回1unsigned char buf_length(); //返回缓冲区数据个数bit command_s(); //键盘命令检查,有命令要接受返回1void clr_buf(void); //清键盘缓冲区void del_head(); //删除缓冲区头unsigned char exist(unsigned char);//检查键盘缓冲区中是否有与参数相等数,有则返回位置,无则返回255//bit emputy(); //检查键盘缓冲区是否空,是返回1unsigned char get_head(); //取键盘缓冲区头,头指针不变unsigned char get_head_f();//取缓冲区头对应标记,标记为0表示对应键已经松下bit insert(unsigned char,unsigned char);//插入缓冲区,并设置对应标记,成功则返回1void ini_timer01(); //定时器初始化void receive_process(); //接收键盘命令并处理void reset(); //软件复位unsigned char scankb(unsigned char); //扫描第N行,返回列直void send_buf(); //发送缓冲区扫描码bit send_code(unsigned char _KeyNo,bit flag);//发送按键扫描码,flag=0发送断开码,flag=1发送接通码bit send(unsigned char); //发送数据void set_default(); //设置缺省值void set_timer1(); //复位定时器1void set_scan_v(unsigned char); //设置扫描速度(拍发速率、延迟时间)void set_flag(unsigned char); //设置缓冲区对应标记void set_led(unsigned char); //设置LEDvoid secret(unsigned char);void scan(void);unsigned char get_end();bit emputy(void);//-----------------------函数声明,变量定义--------------------------------------------------------#include <reg51.h>#define KEY P1unsigned char key_code; //键值unsigned char key_buf[8]; //按键缓冲区unsigned char key_COUNT; //按键计数器unsigned char COUNT_TI; //定时中断计数//-----------------------变量声明---------------------------------------------------------------------void system_init(void ); //初始化,设置定时器0的工作方式,供主程序调用void TIMER0_SCANkey(); //定时器0中断处理函数bit judge_hitkey(); //判断是否有键按下,有返回1,没有返回0unsigned char scan_key(); //扫描键盘,返回键值(高四位代表行,低四位代表列) void key_manage(unsigned char keycode); //按键处理//...........每个按键对应一个处理程序//--------------------------------------------------------------------------------------------------// 函数名称:scan_key// 函数功能:扫描键盘,返回键值(高四位代表行,低四位代表列)// 无键按下返回0//--------------------------------------------------------------------------------------------------unsigned char scan_key() //扫描键盘,返回键值(高四位代表行,低四位代表列){unsigned char scancode,keycode,keycode_line,keycode_row;scancode="0xF0"; //列置低,行置高KEY="scancode"; //输入扫描码,扫描行keycode_line=KEY;scancode="0xF0"; //列置高,行置低KEY="scancode"; //输入扫描码,扫描列keycode_row=KEY;keycode=(((keycode_line<<4)&0xF0)|(keycode_row&0x0F));return(keycode);}//--------------------------------------------------------------------------------------------------// 函数名称:Timer0_init()// 函数功能:初始化设置// 设定INT0的工作方式//--------------------------------------------------------------------------------------------------void Timer0_init(void ){TMOD="0x20"; //定时器0工作在方式2的定时模式ET0=1; //定时器0中断允许TH0=0;TL0=0;TR0=1; //定时器0开始计数EA="1"; //系统中断允许}//--------------------------------------------------------------------------------------------------// 函数名称:TIMER0_intrupt// 函数功能:定时器0中断处理程序按键定时查询//--------------------------------------------------------------------------------------------------void TIMER0_SCANkey() interrupt 1 using 1{EA="0"; //系统中断禁止if((++COUNT_TI)%30==0){switch(COUNT_TI/30){case 1:if(scan_key()==0)COUNT_TI=0; //无键按下,计数值归零break;case 2:break;case 3:if(scan_key()==0)COUNT_TI=0; //无键按下,计数值归零,上次按键未扰动elsekey_code=scan_key(); //又有效建,获取键值break;default:if(scan_key()==0) //等待按键释放key_manage(key_code); //有一个有效按键,调用按键处理程序}}EA=1;}//--------------------------------------------------------------------------------------------------// 函数名称:key_manage// 函数功能:有效按键处理// 按键计数器加1,缓存区数据后移1位//--------------------------------------------------------------------------------------------------void key_manage(unsigned char keycode){unsigned char i;for(i=7;i>=0;i--){key_buf[i]=key_buf[i-1]; //缓冲区内数据后移1位}key_buf[0]= keycode; //将键值送入缓冲区key_COUNT++; //按键计数器加一}//-----------------------函数声明,变量定义-------------------------------------------------------- #include <reg51.h>#define KEY P1sbit DATA="P3"^1; //数据线sbit CLK="P3"^2; //时钟线unsigned char key_buf[8]; //按键缓冲区unsigned char key_COUNT; //按键计数器//--------------------------------------------------------------------------------------------------// 函数名称:delay// 入口参数:N// 函数功能:延时子程序,实现(16*N+24)us的延时// 系统采用11.0592MHz的时钟时,延时满足要求,其它情况需要改动//--------------------------------------------------------------------------------------------------void delay(unsigned int N){int i;for(i=0;i<N;i++);}//--------------------------------------------------------------------------------------------------// 函数名称:CAL_jiaoyan// 函数功能:计算校验位//--------------------------------------------------------------------------------------------------bit CAL_jiaoyan(unsigned char byte_data){//}//--------------------------------------------------------------------------------------------------// 函数名称:SEND_byte// 函数功能:发送一子节数据//--------------------------------------------------------------------------------------------------void SEND_byte(unsigned char byte_data){unsigned char i,temp;if(CLK==0) //时钟线为低temp="byte"_data;CLK="1";DA TA="0";delay(0);CLK="0"; //发送起始位for(i=0;i<8;i++){delay(0);CLK="1";DA TA=(temp&0x01); //发送数据byte_data=byte_data>>1;delay(0);CLK=0;}delay(0);CLK="1";DA TA=CAL_jiaoyan(byte_data); //发送校验位delay(0);CLK=0;delay(0);CLK="1";DA TA=1; //发送结束位delay(0);CLK=0;}//-------------------------------------------------------------------------------------------------- // 函数名称:RECEIVE_byte// 函数功能:接收一子节数据//-------------------------------------------------------------------------------------------------- unsigned char RECEIVE_byte(){unsigned char byte_data,i;CLK="0";delay(0);CLK="1"; //接收起始位,丢弃for(i=0;i<8;i++){delay(0);CLK="0";delay(0);CLK=1;byte_data=byte_data>>1;if(DATA=1)byte_data=byte_data|0x80;elsebyte_data=byte_data&0x7F; //接收8位数据}for(i=0;i<2;i++){delay(0); //接收校验位和结束位CLK="0";delay(0);CLK=1;}return(byte_data);}//--------------------------------------------------------------------------------------------------// 函数名称:manage// 函数功能:主机命令处理函数//-------------------------------------------------------------------------------------------------- void manage(unsigned char rec_data){}//--------------------------------------------------------------------------------------------------// 函数名称:SEND_keydata// 函数功能:发送按键值到主机//-------------------------------------------------------------------------------------------------- void SEND_keydata(){unsigned char ASCII_code; //// ASCII_code=judge_key(key_buf[key_COUNT]); //判断键值,按键编码成ASCII码SEND_byte(ASCII_code);key_COUNT--;}//--------------------------------------------------------------------------------------------------// 函数名称:主程序// 函数功能:循环查询主机状态//-------------------------------------------------------------------------------------------------- void main(){unsigned char rec_data;while(1){if(CLK==0&&DATA==0){rec_data=RECEIVE_byte(); //接收主机键盘manage(rec_data); //指令处理函数}if(key_COUNT!=0&&CLK==1) //有按键等待处理//线路空闲SEND_keydata();}。
51单片机PS2键盘程序
51单片机PS2键盘程序/*中断程序,帮助了解中断事件*/#include#includesbit key_data=P3^0;sbit key_clk=P3^2; //定义键盘接口的时钟脚sbit RELAY=P1^0; //继电器bit BF=0;//code unsigned char tmpdate[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};//定义常量做为输出unsigned char keyv=0; //变量为常量的索引unsigned int intNum=0; //计数接收个数unsigned char key_up=1; //检测按键按下否void ldedata(unsigned char scandata);//数据处理void main(void) //入口函数{EA=1; //首先开启总中断EX0=1; //开启外部中断0IT0=1; // 设置成下降沿触发方式P2=0;do{ //循环if(BF)ldedata(keyv);elseEA=1;}while(1);}void key_scan() interrupt 0{ //外部中断0if((intNum>0)&&(intNum<9)){keyv=keyv>>1;if(key_data)keyv=keyv|0x80;}intNum++;while(!key_clk)if(intNum>10){intNum=0;BF=1;EA=0; //等待处理完键值再开启}}void ldedata(unsigned char scandata) {// unsigned char Tempdata;if(!key_up) //键盘松开时{switch(scandata){case 0xf0:key_up=1;break;case 0x12: //左SHIFT,可以下面写相应处理RELAY=0;//shift=1;break;case 0x59: //右SHIFT,可以下面写相应处理RELAY=1;//shitf=1;break;default:P0=scandata;if(scandata==0x76) //当按下键盘上的J键时,继电器响RELAY=0;if(scandata==0x52){ //当按下键盘上的空格键时,断开继电器RELAY=1;}break;}}else{key_up=0;switch(scandata){case 0x12://shift=0;break;case 0x59://shift=0;break;}}BF=0; //标识字符处理完了}。
基于单片机的PS2键盘设计
PS2键盘在单片机上的应用摘要:在嵌入式PC应用系统中,作为人机交互设备的键盘,往往采用结构简单按键少的矩阵键盘。
标准键盘虽然能直接与嵌入式PC机的PS/2接口相连,但是体积大,按键多,不能满足需求,本文提出用一种AT89C52单片机实现具有标准PS/2接口的矩阵键盘,具有便捷,实用的特点。
关键词:PS/2接口,PS/2键盘,拨号键,AT89C52,LCD1602Abstract:PS/2 interface is one of the most useful mouse interface.It was IBM’s patent named osulum before. It is the dedicate interface of mouse and keyboard. This text implied a plan using PS/2 to make a system. PS/2 tansmit the data which was pressed, and AT89C52 receive it ,disposed it and transmit it to Lcd1602. Lcd1602 discover it to make us know which key has been pressed. PS/2 simulate a phone’s dial keyboard. This system’s feature is the circuit is sample and useful.Keywords:PS/2 keyboard, AT89C52, LCD1602,PS/2 interface,dial keyboard目录1、前言 (1)2、整体方案设计 (2)2.1方案论证 (2)2.2方案比较 (3)3、单元模块设计 (4)3.1PS2键盘模块 (4)3.2单片机模块 (6)3.3LCD显示模块 (7)4、软件设计 (9)5、系统技术指标及精度和误差分析 (10)6、结论 (11)7、设计小结 (12)8、致谢 (14)9、参考文献 (14)附录1:电路总图 (15)附录2:仿真图 (16)附录3:软件代码 (17)1、前言单片机因其性价比高, 处理能力强, 且抗干扰能力好, 在医疗器械、机电液控制、数据传输等各类工控系统和设备仪器中得到广泛应用。
基于单片机的PS2键盘显示系统设计ppt课件
完整最新版课件
7
PS2接口的电气特性
❖ PS2模块由PS2键盘和PS2接口组成,利用通 信协议对PS2键盘进行操作,PS2通讯协议 是一种双向同步串行通讯协议。通讯的两端 通过 Clock(时钟脚)同步,并通过Data(数据 脚)交换数据。任何一方如果想抑制另外一方 通讯时,只需要把Clock(时钟脚)拉到低电平。
完整最新版课件
22
结束
完整最新版课件
23
此课件下载可自行编辑修改,供参考! 感谢您的支持,我们努力做得更好!
完整最新版课件
24
此课件下载可自行编辑修改,此课件供参考! 部分内容来源于网络,如有侵权请与我联系删除!感谢你的观看!
此课件下载可自行编辑修改,此课件供参考! 部分内容来源于网络,如有侵权请与我联系删除!感谢你的观看!
❖ 方案二:数码管显示设计方案
❖ 方案三:PS2键盘设计方案
完整最新版课件
4
1.2 方案比较
❖ 由于方案一采用的是4X4键盘,设计需要 CPU不断的扫描键盘,占用了大量的CPU资 源;而方案二利用PS2键盘,有键按下时才 发送数据,减轻了CPU的负担,但是用数码 管显示不够直观,交互界面不够好,因此综 合方案一和方案二的优点,本设计采用了方 案三。
完整最新版课件
12
2.3 LCD显示模块设计
❖ 本论文介绍了液晶显示器LCD12864 与单片机的接 口及编程的方法,主要的控制脚有读\写选择引脚 R/W、读写使能引脚E、数据输入输出引脚D0-D7。
完整最新版课件
13
❖ LCD12864接口由8位数据线,电源地电源正,液晶 显示偏压信号(VL),数据命令选择端(RS), 读写选择端(R/W)组成。
完整最新版课件
PS2键盘在单片机系统中的应用
PS/2键盘在单片机系统中的应用PS/2键盘作为PC机上的标准输入设备,目前应用已及其普遍。
如果将其应用于单片机系统中,将使系统增色不少。
这里,就对PS/2接口进行详细的介绍,实现单片机对通用PC 键盘的驱动。
1、PS/2接口简介PS/2设备接口用于许多现代的鼠标和键盘。
它是由IBM开发,并且最初出现在IBM 技术参考手册里。
但是这方面的资料现在已经比较少了。
这里只是结合笔者实际的实验情况来进行讲解。
这个文件描述了用于PS/2鼠标、PS/2键盘及AT键盘的接口。
包括其物理、电气接口以及协议。
2、PS/2的物理接口物理上的PS/2端口是有两种:5脚的DIN或6脚的miniDIN(DIN标准是由德国标准化组织(Deutsches Institut fuer Norm)建立的)。
这两种连接器在电气特性上是十分类似的,实际上两者只有一点不同那就是管脚的排列。
这就意味着这两类连接器可以很容易用一种简单的硬件连线的适配器来转换。
这种适配器大约每个值6美元或者你可以根据任意两种连接器的对应管脚关系做你自己的适配器。
PC键盘可以有6脚的miniDIN或5脚的DIN连接器。
如果你的键盘是6脚的miniDIN 而你的计算机是5脚的DIN(或者相反),这两类连接器可以用上面提到的适配器来兼容。
具有6脚miniDIN的键盘通常被叫做PS/2键盘,而那些有5脚DIN叫做AT设备(XT 键盘也使用5脚DIN但它们非常古老并且多年前就不生产了)所有现代的PC键盘不是PS/2、AT,就是USB的。
这里只对PS/2键盘进行介绍,而不涉及USB键盘的相关内容。
PS/2接口引脚定义在上表中接口上的四个管脚电源地5V、数据和时钟。
单片机提供5V,并且键盘/鼠标的地连接到单片机的电源地上。
数据和时钟都是集电极开路的,这就意味着它们通常保持高电平而且很容易下拉到地(逻辑0)。
时钟和数据线上要有一个大的上拉电阻。
MCU驱动PS2键盘
4.(4242)《单片机系统中PS/2键盘驱动程序设计》P38键盘发送接口程序如下:.单片机向PS/2键盘发送数据程序代码如下:void ps2_sentchar(unsigned char sentchar){//ps2主设备向从设备发送数据unsigned char sentbit_cnt = 0x00;unsigned char sentchar_chk = 0x00;EX0 =0; //关外部中断0//发起一个传送,发起始位PS2_SGN_CLOCK = 0;//将时钟线拉低并保持100usdelay100us();PS2_SGN_DATA = 0;//起始位PS2_SGN_CLOCK = 1;//发送DATA0-7for(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_CLOCK)_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; //发送停止位停止位总为1while(!PS2_SGN_CLOCK)_nop_();//等待时钟线变高while(PS2_SGN_CLOCK)_nop_();//等待时钟线变为低//接收ACK// if(PS2_SGN_DATA) error();// ACK信号由键盘发出,总为低电平while(!PS2_SGN_CLOCK)_nop_();//等待时钟线变高EX0 = 1; //开外部中断0}单片机由PS/2键盘接收数据程序:外部中断0设置为下降沿触发void int0() interrupt 0 using 0{//EX0=0;//关外部中断0switch(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}。
用51单片机制作ps2接口的工业小键盘
用51单片机制作ps2接口的工业小键盘经过我的验证,是完全可行的。
#include <reg52.h>#include <intrins.h>#define uchar unsigned char#define SEM_FULL 1#define SEM_EMPTY 0uchar bdata ps2char;uchar bdata keystatus;uchar bdata parity_buf;uchar bdata s_buf,r_buf;uchar data last_s;sbit date=P3^0;sbit clock=P3^2;sbit key0=P2^0;sbit key1=P2^1;sbit key2=P2^2;sbit key3=P2^3;sbit key4=P2^4;sbit key5=P2^5;sbit key6=P2^6;sbit key7=P2^7;sbit s_buf0=s_buf^0;sbit r_buf7=r_buf^7;sbit parity_buf0=parity_buf^0;bit ps2_tx_sem=SEM_EMPTY;bit ps2_sendbyte(uchar c);void delayus(uchar us){while(--us);}void delayms(uchar ms){uchar i;while(ms--){for(i=0;i<120;i++);}}bit parity(){bit PARITY=1;uchar i;for(i=0;i<8;i++){PARITY^=parity_buf0;parity_buf=parity_buf>>1;}return PARITY;}void ps2_tx_sem_take(){ps2_tx_sem0:EA=0;if(ps2_tx_sem==SEM_FULL){EA=1;delayus(30);delayus(30);goto ps2_tx_sem0;}ps2_tx_sem=SEM_FULL;EA=1;return;}void ps2_tx_sem_give(){EA=0;ps2_tx_sem=SEM_EMPTY;EA=1;return;}void ps2_clk_check(){ps_tx_sem:EA=0;if(clock==0) // if PS2_CLK low, wait for 50us {EA=1;delayus(30);goto ps_tx_sem;}EA=1;return;}void ps2_send(uchar dat){ps2_tx_sem_take();ps2_clk_check();if(date==1)ps2_sendbyte(dat);ps2_tx_sem_give();}uchar ps2_recbyte() // Return receice data,Error return 0{uchar i;bit PARITY;r_buf=0;while(!clock); // Wait KBCLK Highdelayus(10);if(date) // KBDA TA shoud be low,mean received start bit{ps2_send(0xfe); // if KBDATA is High, return and send error status date(0xfe)return 0;}delayus(30);clock=0; // Device control the KBCLK signalfor(i=0;i<8;i++) // Rising edge of KBCLK, write data into r_buf7{delayus(30);clock=1;delayus(10);if(!clock) return 0; // if KBCLK pull down, mean Host cancelled this sendingr_buf7=date;if(i!=7)r_buf=r_buf>>1;delayus(10);clock=0;}delayus(30);clock=1;delayus(10);if(!clock) return 0; // if KBCLK pull down, mean Host cancelled this sendingPARITY=date; // Receive odd parity bitdelayus(10);clock=0;delayus(30);clock=1;delayus(10); // Receive Stop bit, should be highif(!date) // Otherwise,send error status date(0xfe){ps2_send(0xfe);return 0;}date=0; // Send ACK bit, mean have receive data done delayus(10);clock=0;delayus(30);clock=1; // Set KBCLK and KBDATA highdelayus(10);date=1;delayus(10);parity_buf=r_buf;if(PARITY==parity()) // Check Odd parity{return r_buf;}else{ps2_send(0xfe);return 0;}delayus(30);}bit ps2_sendbyte(uchar c) // Success return 1,Fail return 0{uchar i;bit PARITY;clock=1;if(!clock) // Host prevent Keyboard sending datareturn 0;parity_buf=c;PARITY=parity(); // Calculate value about sending data odd paritylast_s=c; // Save last sending datas_buf=c;if(!clock) // Host prevent Keyboard sending datareturn 0;if(!date) // Host prepared sending command data to keyboard return 0;date=0; // Falling edge of KBCLK send data, start bit 0 delayus(10);clock=0;for(i=0;i<8;i++) // First send LSB{delayus(10);clock=1;delayus(10);if(!clock) // if KBCLK is low,mean Hos prevent Keyboard sending datareturn 0;date=s_buf0;s_buf=s_buf>>1;delayus(10);clock=0;}delayus(10);clock=1;delayus(10);if(!clock) // if KBCLK is low,mean Hos prevent Keyboard sending datareturn 0;date=PARITY; // Sending odd parity bitdelayus(10);clock=0;delayus(10);clock=1;delayus(10);date=1; // Sending stop bitdelayus(10);clock=0;delayus(30);clock=1;delayus(30);delayus(10);return 1;}bit bat(){if(P2!=0xff)return 1;elsereturn 0;}void re_send ( ){ps2_send(last_s);}void reset(){date=1;while(!clock||!date); // Wait KBCLK and KBDATA highif(bat())ps2_send(0xfc); // Self-Check failedelseps2_send(0xaa); // Self-Check successed}bit Check_command ( ) // Keyboard receive host command,return 1 { clock=1;date=1;if(!clock&!date)return 1;elsereturn 0;}void receive_process(){uchar command;if(!Check_command()) return;command=ps2_recbyte();if(!command) return;switch(command){case 0xff: // reset commandps2_send(0xfa);reset();break;case 0xfe: // re-send commandps2_send(0xfa);re_send();break;case 0xf3: // Set Typematic Rate/Delayps2_send (0xfa);break;case 0xf2: // Read IDps2_send(0xfa);ps2_send(0xAB);ps2_send(0x83);break;case 0xee: // respond commandps2_send(0xee);case 0x00:ps2_send(0xfe);default:ps2_send(0xfa); // other command, just send a response of 0xFA break;}}void main(){P2=0xff;if(bat())ps2_send(0xfc);elseps2_send(0xaa);while(1){ if(Check_command())receive_process();key4=0;while(!key4){if(!key0){delayms(5);if(!key0){ps2_sendbyte(0x1C);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X1C);while(!key0);}}else if(!key1){delayms(5);if(!key1){ps2_sendbyte(0x32);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X32);while(!key1);}}else if(!key2){delayms(5);if(!key2){ps2_sendbyte(0x21);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X21);while(!key2);}}else if(!key3){delayms(5);if(!key3){ps2_sendbyte(0x23);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X23);while(!key3);}}else key4=1;}key5=0;while(!key5){if(!key0){delayms(5);if(!key0){ps2_sendbyte(0x24);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X24);while(!key0);}}else if(!key1){delayms(5);if(!key1){ps2_sendbyte(0x2B);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X2B);while(!key1);}}else if(!key2){delayms(5);if(!key2){ps2_sendbyte(0x34);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X34);while(!key2);}}else if(!key3){delayms(5);if(!key3){ps2_sendbyte(0x33);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X33);while(!key3);}}else key5=1;}key6=0;while(!key6){if(!key0){delayms(5);if(!key0){ps2_sendbyte(0x43);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X43);while(!key0);}}else if(!key1){delayms(5);if(!key1){ps2_sendbyte(0x3B);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X3B);while(!key1);}}else if(!key2){delayms(5);if(!key2){ps2_sendbyte(0x42);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X42);while(!key2);}}else if(!key3){delayms(5);if(!key3){ps2_sendbyte(0x4B);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X4B);while(!key3);}}else key6=1;}key7=0;while(!key7){if(!key0){delayms(5);if(!key0){ps2_sendbyte(0x3A);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X3A);while(!key0);}}else if(!key1){delayms(5);if(!key1){ps2_sendbyte(0x31);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X31);while(!key1);}}else if(!key2){delayms(5);if(!key2){ps2_sendbyte(0x44);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X44);while(!key2);}}else if(!key3){delayms(5);if(!key3){ps2_sendbyte(0x4D);delayms(5);ps2_sendbyte(0xF0);delayms(1);ps2_sendbyte(0X4D);while(!key3);}}else key7=1;}}}。
基于单片机的PS2键盘设计
PS2键盘在单片机上的应用摘要:在嵌入式PC应用系统中,作为人机交互设备的键盘,往往采用结构简单按键少的矩阵键盘。
标准键盘虽然能直接与嵌入式PC机的PS/2接口相连,但是体积大,按键多,不能满足需求,本文提出用一种AT89C52单片机实现具有标准PS/2接口的矩阵键盘,具有便捷,实用的特点。
关键词:PS/2接口,PS/2键盘,拨号键,AT89C52,LCD1602Abstract:PS/2 interface is one of the most useful mouse interface.It was IBM’s patent named osulum before. It is the dedicate interface of mouse and keyboard. This text implied a plan using PS/2 to make a system. PS/2 tansmit the data which was pressed, and AT89C52 receive it ,disposed it and transmit it to Lcd1602. Lcd1602 discover it to make us know which key has been pressed. PS/2 simulate a phone’s dial keyboard. This system’s feature is the circuit is sample and useful.Keywords:PS/2 keyboard, AT89C52, LCD1602,PS/2 interface,dial keyboard目录1、前言 (1)2、整体方案设计 (2)2.1方案论证 (2)2.2方案比较 (3)3、单元模块设计 (4)3.1PS2键盘模块 (4)3.2单片机模块 (6)3.3LCD显示模块 (7)4、软件设计 (9)5、系统技术指标及精度和误差分析 (10)6、结论 (11)7、设计小结 (12)8、致谢 (14)9、参考文献 (14)附录1:电路总图 (15)附录2:仿真图 (16)附录3:软件代码 (17)1、前言单片机因其性价比高, 处理能力强, 且抗干扰能力好, 在医疗器械、机电液控制、数据传输等各类工控系统和设备仪器中得到广泛应用。
在51单片机上使用PC机ps2键盘
在51单片机上使用PC机ps/2键盘(附源码)本人弄了几天,终于在今天晚上,也就是刚才实验成功,心情特佳,特写出来以享大家。
单片机上应用非编码键盘,各书上均有介绍。
作为实验用,我想到了用PC机的ps/2键盘。
PC机键盘内部有单片机电路来完成编码和去抖动,它按照ps/2协议来发送扫描码。
因此在应用中,我们需要做的只是将扫描码与字符对应起来,大部分事情都由键盘自己完成了。
首先介绍一下键盘的接口,典型的几种接口如下图:其中第一种用于老式键盘(我原来的键盘就是),第二种便是现在的PS/2键盘,第三个不用去管它。
我的实验是按照最常用的PS/2(即第二种)来做的。
再略微介绍一下PS/2协议的相关内容。
PS/2的一个数据帧为11位,时序如下:PS/2帧的第一位是起始位,为0,然后是8位数据位,发送键盘扫描码的一个字节(扫描码为1-4个字节),然后是奇偶校验位,最后是停止位,为1。
这些是在数据线(即1号引脚线)上发送的。
无键按下时,数据线和始终线都保持为1。
当有键按下时,时钟线CLOCK 送出脉冲,同时数据线送出数据。
主机(此处是89c51 MCU)在始终脉冲的下降沿对数据线采样获得数据。
键盘扫描码包括通码和断码,当键按下时发送通码,抬起时发送断码。
更详细的内容可参考所附的《PS/2技术参考》。
根据上述原理,我这样设计了实验:将键盘的脉冲线接至89c51的外部中断输入口(INT0或INT1),当键按下和抬起时有脉冲产生,此脉冲引发MCU中断。
将键盘的DATA线连至89c51的输入口(如P1.0)。
在中断处理程序中,从输入口读入数据,然后通过循环移位对读进的数据位进行处理,1(起始位)、10(奇偶校验)、11(停止位)可抛弃,如不嫌麻烦也可将奇偶校验位加以应用。
当一个数据帧收完后,将处理后剩下的2-9位(即扫描码)通过串口发至PC机,通过PC机的串口监视软件(如“串口调试助手”)来查看。
硬件连线和源码如下:源码:ORG 0000HAJMP MAIN;转入主程序ORG 0003H ;外部中断P3.2脚INT0入口地址AJMP INT ;转入外部中断服务子程序;以下为主程序进行CPU中断方式设置MAIN:MOV SCON,#50H;设置成串口1方式MOV TMOD,#20H;波特率发生器T1工作在模式2上MOV PCON,#80H;波特率翻倍为2400x2=4800BPSMOV TH1,#0F3H;预置初值(按照波特率2400BPS预置初值)MOV TL1,#0F3H;预置初值(按照波特率2400BPS预置初值)SETB EA ;打开CPU总中断请求SETB IT0 ;设定INT0的触发方式为脉冲负边沿触发SETB EX0 ;打开INT0中断请求SJMP $INT: CLR EA ;暂时关闭CPU的所有中断请求CJNE R0,#0,L1L3: INC R0SJMP L5L1: CJNE R0,#9,L2SJMP L3L2: CJNE R0,#10,L4SETB TR1;启动定时器T1MOV SBUF,AMOV R0,#0L5: SETB EA ;允许中断RETI ;退出子程序L4: MOV C,P1.0RRC ASJMP L3END搞定后,当按下和释放键时,会在PC机上显示其扫描码。
51单片机控制ps2键盘设计说明
51接PS2键盘2008-09-20 16:02在这个周末,终于有时间做做试验了。
昨晚用AVR和MC9S12DG128驱动1602成功,今天突然想到PS2键盘的驱动,当时觉得PS2键盘与MCU接口很神秘,做了之后才觉得其实不然哈。
再传张键盘的扫描码的波形。
(协议规定:数据低位在前,采用奇校验数据格式(PS->MCU):1起始位为0,8数据位,1奇校验位,1停止位为1)'A'键(0x1C)的make code:代码:PS_2_KB.H//************write by zhouyong********* //************qq:510559254************** //************2008-9-20*****************#ifndef _PS_2_KB_H#define _PS_2_KB_H#include <AT89X51.H>#define KB_CLK P3_3#define KB_DATA P3_4uchar Get_Key(void);uchar Key_Scan(void);uchar Key_Scan(void){uchar i,key_temp;KB_CLK=1; //输入KB_DATA=1;key_temp=0;while(KB_CLK); //第一次为0for(i=0;i<8;i++){key_temp>>=1;while(!KB_CLK); //下沿,第一位while(KB_CLK);_nop_();if(KB_DATA){key_temp|=0x80; //低位在前}}while(!KB_CLK); //校验位while(KB_CLK);while(!KB_CLK); //停止位while(KB_CLK);while(!KB_CLK);return key_temp;}//由于make_code和第二个break_code一样,就取break_code第二个作为键码uchar Get_Key(void){uchar Key_Code;Key_Code=Key_Scan(); //make_codeKey_Code=Key_Scan(); //break_code 1Key_Code=Key_Scan(); //break_code 2switch(Key_Code){case 0x1c: return 'A';break;case 0x32: return 'B';break;case 0x21: return 'C';break;case 0x23: return 'D';break;case 0x24: return 'E';break;case 0x2b: return 'F';break;case 0x34: return 'G';break;case 0x33: return 'H';break;case 0x43: return 'I';break;case 0x3b: return 'J';break;case 0x42: return 'K';break;case 0x4b: return 'L';break;case 0x3a: return 'M';break;case 0x31: return 'N';break;case 0x44: return 'O';break;case 0x4d: return 'P';break;case 0x15: return 'Q';break;case 0x2d: return 'R';break;case 0x1b: return 'S';break;case 0x2c: return 'T';break;case 0x3c: return 'U';break;case 0x2a: return 'V';break;case 0x1d: return 'W';break;case 0x22: return 'X';break;case 0x35: return 'Y';break;case 0x1a: return 'Z';break;case 0x45: return '0';break;case 0x16: return '1';break;case 0x1e: return '2';break;case 0x26: return '3';break;case 0x25: return '4';break;case 0x2e: return '5';break;case 0x36: return '6';break;case 0x3d: return '7';break;case 0x3e: return '8';break;case 0x46: return '9';break;default: return 0xff; break;}}#endifLCD1602.H://************write by zhouyong*********//************qq:510559254**************//************2008-9-14*****************#ifndef _LCD1602_#define _LCD1602_#include <AT89X51.H>#include <string.h>#include <intrins.h>//--------------------------------------------------------------------#define E_1602 P3_7 //on falling edge enable data or command #define RW_1602 P3_6 //read or write control#define RS_1602 P3_5 //cmd or data register select#define DATA_1602 P2 //data port#define DATA 1 //select DATA register#define CMD 0 //select CMD register#define READ 1#define WRITE 0//--------------------------------------------------------------- void Delay_us(uchar t);void Delay_ms(uchar t);void Init_1602(void);void Write_Char_1602(uchar Data,bit CMD_DATA,bit Check);void Check_Busy_1602(void);void Write_String_1602(uchar *P);void Set_R_C(uchar R,uchar C);//void Clear_LCD_1602(void);//---------------------------------------------------------------- /*void Clear_LCD_1602(void){Write_Char_1602(0x01,CMD,1);//clear screen}*/void Delay_us_1602(uchar t){while(--t);}void Delay_ms_1602(uchar t){while(t--){Delay_us_1602(225);Delay_us_1602(227);}}void Init_1602(void){Delay_ms_1602(15);Write_Char_1602(0x38,CMD,0); //don't check busyDelay_ms_1602(5);Write_Char_1602(0x38,CMD,0);Delay_ms_1602(5);Write_Char_1602(0x38,CMD,0);Write_Char_1602(0x38,CMD,1);//8 wire,2 line display,5x10 charWrite_Char_1602(0x08,CMD,1);//close display,no cursor,don't blink Write_Char_1602(0x01,CMD,1);//clear screenWrite_Char_1602(0x06,CMD,1);//the cursor move from left to right,the text don't moveWrite_Char_1602(0x0c,CMD,1);//open display}void Write_Char_1602(uchar Data,bit CMD_DATA,bit Check){if(Check)Check_Busy_1602();RS_1602=CMD_DATA;RW_1602=WRITE;DATA_1602=Data;E_1602=1;_nop_();E_1602=0;}void Write_String_1602(uchar *P) //only can write from start to end {uchar i,len;len=strlen(P);Set_R_C(0,0);if(len>16){for(i=0;i<16;i++){Write_Char_1602(P[i],DATA,1);}Set_R_C(1,0);for(i=16;i<len;i++){Write_Char_1602(P[i],DATA,1);}}else{for(i=0;i<len;i++){Write_Char_1602(P[i],DATA,1);}}}void Set_R_C(uchar R,uchar C) //set row and column R=0/1;C=0~F {R&=0x01;C&=0x0f;if(R)Write_Char_1602(0x80+0x40+C,CMD,1);elseWrite_Char_1602(0x80+C,CMD,1);}void Check_Busy_1602(void){DATA_1602=0xff; //set as input portRS_1602=CMD;RW_1602=READ;E_1602=1;while(DATA_1602 & 0x80){E_1602=0; //这两句protues仿真必须加E_1602=1; //}E_1602=0;}#endifmain.c:#include <AT89X51.H>#include <intrins.h>#define uchar unsigned char#define uint unsigned int#include "PS_2_KB.h"#include "LCD1602.h"void main(void){ucharBuffer[]="";uchar Key_Code,Key_Count,i;Init_1602();Set_R_C(0,0);for(;;){Key_Code=Get_Key();if(Key_Code!=0xff) // 为0xff时视为无效键{Buffer[Key_Count]=Key_Code;Write_String_1602(Buffer);Key_Code=0;Key_Count++; //第n次按键,显示在第n位if(Key_Count==32){for(i=0;i<32;i++){Buffer[i]=' ';}Key_Count=0;}P1=~P1; //P1口接有LED,用于指示按键 }}}。
PS2在单片机上的应用
一、设计目的:在嵌入式PC应用系统中,作为人机交互设备的键盘,往往采用结构简单按键少的矩阵键盘。
标准键盘虽然能直接与嵌入式PC机的PS/2接口相连,但是体积大,按键多,不能满足需求,本文提出用一种AT89C52单片机实现具有标准PS/2接口的矩阵键盘,具有便捷,实用的特点。
二、设计要求:利用PS2键盘,通过AT89C52单片机接收PS2键盘的键值,并显示在LCD1602上,PS2键盘模拟电话的拨号键。
三、设计主要过程:1、主程序设计:首先初始化IO口和键盘、LCD1602,然后接收按键的键值,并通过8位并行传输方式传送给LCD1602显示。
2、获取键值程序:按照PS2键盘协议操作,键盘按下时发送通码,弹起时发送断码,在单片机中,将接收到的码字与第二套扫描码比较,得出键值。
本次设计共采用12个键,模拟电话的拨号键。
3、LCD显示程序:首先初始化LCD1602端口和寄存器,然后接收单片机传送的数据,通过1602写操作时序显示接收到的数据。
程序整体设计框图四、电路图:五、程序:main.c:#include<reg52.h>#include "KEY4x4_MODE.h" #include <string.h>#include "type.h"#include "lcd1602.h"uchar Dis_buffer[12]="0123456789*#";uchar key_flag=0;void main(){uchar loca,key_value=0;Init_1602(); //初始化1602while(1){key_value = Get_key(); //键盘扫描if(key_flag){lcd_dis(Dis_buffer,key_value,loca++); //显示key_flag=0; //标志位清零}}}Lcd1602.c:#include<reg52.h>#include <string.h>#include "type.h"#include "lcd1602.h"void Init_1602(void) //初始化{Lcd_write_CMD(0x38,0);Lcd_write_CMD(0x38,1); //设置显示模式Lcd_write_CMD(0x08,1); //显示关闭Lcd_write_CMD(0x01,1); //清屏Lcd_write_CMD(0x06,1); //光标从左向右移,文本不移Lcd_write_CMD(0x0f,1); //显示开}void delay_ms(UCHAR i) //延时ms级{unsigned int j;for(;i>0;i--)for(j=0;j<125;j++)}void Lcd_write_CMD(UCHAR DATA_output,UCHAR If_Check) //写指令{if(If_Check)Check_ifbusy();RS_1602=0;RW_1602=0;DATA_out=DATA_output;E_1602=1;delay_ms(1);E_1602=0; //锁存数据RS_1602=1;RW_1602=1;}void Check_ifbusy(void) //读状态{DATA_out=0xff;RS_1602=0;RW_1602=1;E_1602=1;while(DATA_out & 0x80){E_1602=0; //这两句protues仿真必须加E_1602=1; //}E_1602=0;}void Lcd_1602_Display(UCHAR *dis_p) //显示{UCHAR i,len; //定义要放在前面len=strlen(dis_p);Lcd_Set_XY(0,0);if( len>16)for( i=0;i<16;i++){Lcd_write_DATA(dis_p[i],1);}Lcd_Set_XY(0,1);for( i=16;i<len;i++){Lcd_write_DATA(dis_p[i],1);}}else{for( i=0;i<len;i++){Lcd_write_DATA(dis_p[i],1);}}}void lcd_dis(UCHAR *dis_p,UCHAR i,UCHAR location){Lcd_Set_XY(location,0);Lcd_write_DATA(dis_p[i],1);}void Lcd_Set_XY(UCHAR x,UCHAR y) //设置显示的初始坐标{x &= 0x0f;y &= 0x01;delay_ms(1);if(y){Lcd_write_CMD(0X80+0X40+x,1);}{Lcd_write_CMD(0X80+x,1);}}void Lcd_write_DATA(UCHAR DATA_output,UCHAR If_Check)//写数据{if(If_Check)Check_ifbusy();RS_1602=1;RW_1602=0;DATA_out=DATA_output;E_1602=1;delay_ms(1);E_1602=0;delay_ms(1000);}六、结论及分析:本系统采用第二套键盘扫描码,利用PS2键盘作为输入,准确度比普通键盘高,完成了系统的仿真图,电路图和程序设计,实现了系统的功能。
实例制作一个51单片机连接PS2键盘
实例制作的是用一个AT89C51单片机连接PS/2键盘接口和一个16x2的液晶显示屏,当敲击键盘时,字母可以显示在液晶显示屏上。
这个实例能启发你如何利用单片机来实现对PS/2接口的控制。
实例中提供的源代码修改后可以用到其他PS/2键盘制作项目中。
实例中提供的16x2字符型的液晶显示屏的驱动函数也可以其他项目。
电路原理主电路板中的AT89C51单片机(可以用AT89C52/S51/S52直接替换,如用AT89C2051/4051则需要改程序)组成了51最小化系统。
液晶显示屏于嗯了SMC1602A. 键盘通过PS/2六孔插座和主电路板。
PS/2设备的连接器使用mini-DIN连接器,正有6个引线,其中2个保留为用。
DATA和CLK是可双向通信的I/O线,也就是说通过这两根线,既可以把主机的数据发送到PS/2设备,有可以把设备的数据发向主机。
在无键按下是,DATA 和CLK一直处于高电平状态。
但有键按下时,键盘先检查CLK,看它是否处于高电平,如果是处在低电平,说明主机无空闲接收数据,这是键盘将会把数据放在自己的缓冲区(16Bytes).直到CLK重新被拉高。
键盘获得总线权,这是键盘产生始终信号在CLK上输出。
同时每一个时钟周期在DATA 线上输出一位数据。
第1位是起始位为0,第2-9位为一个八位二进制数据由地位到高位依次输出,第10位为奇偶校验位下面是电路原理图PS/2设备接口用于许多现代的鼠标和键盘,PS/2连接器上有四个管脚:电源地、+5V、数据和时钟。
Host(计算机)提供+5V并且键盘/鼠标的地连接到host的电源地上,数据和时钟都是集电极开路的这就意味着它们通常保持高电平而且很容易下拉到地(逻辑0)。
任何你连接到PS/2鼠标、键盘或host 的设备,在时钟和数据线上要有一个大的上拉电阻。
置“0”就把线拉低,置“1”就让线上浮成高。
从键盘/鼠标发送到主机的数据在时钟信号的下降沿(当时钟从高变到低的时候)被读取;从主机发送到键盘/鼠标的数据在上升沿(当时钟从低变到高的时候)被读取。
PS2键盘C语言资料
PS2键盘编程详细资料推荐在单片机系统中,经常使用的键盘都是专用键盘.此类键盘为单独设计制作的,成本高、使用硬件连接线多,且可靠性不高,这一状况在那些要求键盘按键较多的应用系统中更为突出.与此相比,在PC系统中广泛使用PS/2键盘具有价格低、通用可靠,且使用连接线少(仅使用2根信号线)的特点,并可满足多种系统的要求.因此在单片机系统中应用PS/2键盘是一种很好的选择.文中在介绍PS/2协议和PS/2键盘工作原理与特点的基础上,给出了一个在单片机上实现对PS/2键盘支持的硬件连接与驱动程序设计实现.该设计实现了在单片机系统中对PS/2标准104键盘按键输入的支持.使用Keil C51开发的驱动程序接口和库函数可以方便地移植到其他单片机或嵌入式系统中.所有程序在Keil uVision2上编译通过,在单片机AT89C51上测试通过.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抑制通信,然后下拉数据线“请求发送”,最后释放时钟线.在此过程中,从设备在不超过10us 的间隔内必须检查这个状态,当设备检测到这个状态时,它将开始产生时钟信号.此时数据传输的每一帧由12位构成,其时序和每一位含义如图3所示.与从设备到主设备通信相比,其每帧数据多了一个ACK位.这是从设备应答接收到字节的应答位,由从设备通过拉低数据线产生,应答位ACK总是为0.主设备到从设备通信过程中,主设备总是在时钟线为低电平时改变数据线的状态,从设备在时钟上升沿读人数据线状态.2 PS/2键盘的编码与命令集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禁止键盘后,重新使能键盘.3 PS/2键盘与单片机的连接电路PS/2键盘与AT89C51单片机的连接方式如图4所示.P1.0接PS/2数据线;P3.2(INT0)接PS/2时钟线.因为单片机的P1,P3口内部是带上拉电阻的,所以PS/2的时钟线和数据线可以直接与单片机的P1,P3相连接.4 驱动程序设计驱动程序的开发使用Keil C51语言以及KeiluVision2编程环境.PS/2 104键盘驱动程序主要任务是实现单片机与键盘间PS/2通信,同时将接收到的按键扫描码转换为该按键的键值KeyVal,提供给系统上层软件使用.4.1 单片机与键盘间PS/2通信的程序设计在PS/2通信过程中,主设备(文中是单片机)是在时钟信号为低时发送和接收数据信号.因为单片机向键盘发送的是指令,需要键盘回应,所以这部分程序采用查询方式;而单片机接收键盘数据时,数据线上的信号在时钟为低时已经稳定,所以这部分程序采用中断方式,且不需要在程序中加入延时程序.单片机向PS/2键盘发送数据程序代码为: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 usdelay100us();PS2_SGN_DATA= 0; //起始位PS2_SGN_CLOCK = 1;//发送DATA0-7for(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;//发送停止位,停止位总为1while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高while(PS2_SGN_CLOCK) _nop_(); //等待时钟线变低//接收ACK//if(PS2_SGN_DATA) error();//ACK信号由键盘发出,总为低电平while(!PS2_SGN_CLOCK) _nop_(); //等待时钟线变高EX0= 1; //开外部中断0}单片机由PS/2键盘接收数据程序:外部中断0设置为下降沿触发void int0() interrupt 0 using 0 {//EX0=0; //关外部中断0switch(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}4.2 键盘扫描码转换程序设计由于键盘扫描码无规律可循,因此由键盘扫描码获得相应按键的键值(字符键为其ASCII值,控制键如F1,Ctrl等为自定义值),只能通过查表的方式获得.由于按键的3种类型及部分按键对应着两个键值(如A键的键值根据Caps和Shift键状态有0x41(A)和0x61(a)两种),因此综合考虑查表转换速度和资源消耗,设计中使用4个键盘表:键盘扫描码转换基本集和切换集(kb_plain_map[NR_KEYS]与kb_shift_map[NR_KEYS]);包含E0前缀的键盘扫描码转换基本集和切换集(kbeO_plain_map[NR_KEYS]与kbe0_shiftmap[NR_KEYS]).PS/2 104键盘按键扫描码最大值为0x83,所以设置NR_KEYS为132.所有4个键盘表的定义均为如下形式:KB_MAP[MAKE CODE]=KEYVAL,如果扫描码对应的按键为空(如KB_MAP[0x00]),则定义相应键值为NULL_KEY(0x00).以下是键盘扫描码基本集的部分代码实例:kb_plain_map[NR_KEYS]={……NULL_KEY;0x2C;0x6B;0x69;0x6F;0x30;0x39;NULL_KEY; //扫描码0x40~0x47//对应按键空,逗号,K,I,O,0,9,空//对应键值0x00,',','k','i','o','O','9',0x00…… };如此设计键盘转换表的另一个好处在于,以后如需扩展支持有ACPI、Windows多媒体按键键盘时,只需要将键表中相应处修改即可,如ACPIPower按键通码为0xE0 0x37,修改kbe0_plain_map[0x37]=KB_ACPI_PWR即可.特殊按键Pause使用单独程序处理,如果接收到0xE1就转入这段程序.而Print Screen键则将其看作是两个通码分别为0xE0 0x12和0xE0 0x7C的“虚键”的组合键处理.在驱动程序中设定如下全局变量:led_status记录Scroll Lock Led,Num Lock Led和Caps Lock Led的状态(关为0,开为1);agcs_status记录左右Shift Ctrl Gui Alt状态,相应键按下则对应位为1,释放为0.E0_FLAG接到0xE0置1;E1_FLAG接收到0xE1置1;F0_FLAG接收到0xF0置1.按键键值通过KeyVal提供上层程序使用.PS/2键盘扫描码键值转换程序ps2_codetrans()流程框架如图5所示.第1类按键的扫描码键值转换程序代码。
ps2键盘
3、PS2键盘的编码
键盘的编码并不是和 ASCII 码相对 应 。 一次按键过程至少会发送两组码, 通码和断码,通码是按键被按下时发送, 断码是当按键释放时发送。 每个键的通码 和断码都是唯一的,因此通过查唯一的扫 描码,就可以知道哪个键被按下或释放。 扫描码集有三套标准,分别是第一套, 第二套和第三套,所有现代的键盘默认使 用第二套扫描码。
第二套扫描码:
101、102和104 键的键盘
4、单片机对PS2键盘的驱动程序
(1)电路连接: CLK:P3.2(外部中断) DATA:P3.7 (2)驱动程序:
1、PS2物理接口
下图是PS2接口的mini-DIN 6pin的连 接器形式
PS2设备有主从之分,主设备采用母头插 座,从设备采用公头插头,现在广泛使用的 PS2键盘鼠标都是在从设备方式之下工作, PS2接口的数据和时钟线都要接上拉电阻,主 从设备之间通信采用同步串行方式传输协议 (每在时钟线上发一个脉冲,就在数据线上发 送一位数据。 ),时钟信号由送数据时,首先检 测时钟线,以确认时钟线是否是高电平。如 果是高电平,从设备就可以传送数据;反之, 从设备要等待获得总线的控制权,才能开始 传送数据.传送的数据以帧为单位,每帧数据 由11位组成,发送时序及每一位的含义如下 图。
从设备到主设备通信时, 从设备在时钟线 高电平时改变数据线状态,主设备在时钟下 降沿后读入数据线状态
PS2键盘与单片机的接 口设计
在单片机系统中经常使用的键盘有独立式 键盘、矩阵式键盘,此类键盘是单独设计的, 使用硬件连线较多,这一状况在那些要求键盘 按键较多的系统中更为突出。与此相比, PS/2 键盘作为 PC 机上的标准输入设备,目 前应用已及其普遍,具有使用可靠、连接线少 的特点,可以满足多种系统的要求,因此在单 片机系统中应用PS/2键盘是个不错的选择, 本章进行PS/2 接口的介绍,实现单片机对通 用 PS/2键盘的驱动。
AVR单片机 mega16 C语言 PS2键盘实验程序
else //如果是上升沿触发中断
{
MCUCR=2; //设置INT0为下降沿触发中断
edge=0; //设置下降沿中断标志
if(--bitcount==0) //如果11位全部接收完毕
{
Decode(data); //将扫描码翻译成ASCII码
{
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',
0x33,'H',
0x34,'G',
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
单片机系统中PS/2键盘驱动程序设计摘要分析PS/2协议;介绍PS/2标准健盘的第二套扫描码和命令集,并给出在单片机系统中支持PS/2健盘的硬件连接方式和利用Keil C51语言实现的驱动程序设计及部分代码。
该驱动程序可以方便地移植到其他单片机或嵌入式系统中。
关健词 PS/2协议 PS/2健盘单片机驱动程序在单片机系统中,经常使用的键盘都是专用键盘。
这类键盘都是单独设计制作的,成本高,连线多,且可靠性不高。
这些问题在那些要求键盘按键较多的应用系统中显得更加突出。
与此相比,在 PC系统中广泛使用的PS/2键盘具有价格低、通用可靠,且使用的连线少(仅使用2根信号线)的特点,并可满足多数系统的要求。
因此,在单片机系统中应用PS/2键盘是一种很好的选择。
本文在分析PS/2协议和PS/2键盘工作原理与特点的基础上,给出在AT89C51单片机上实现对PS/2键盘支持的硬件连接方法以及驱动程序的设计实现。
1 PS/2协议现在PC机广泛采用的PS/2接口为mini - DIN 6引脚的连接器。
其引脚如图1所示。
740)this.width=740" border=undefined>PS/2设备有主从之分,主设备采用female插座,从设备采用male插座。
现在广泛使用的PS/2键盘鼠标均工作在从设备方式下。
PS/2接口的时钟与数据线都是集电极开路结构的,必须外接上拉电阻。
一般上拉电阻设置在主设备中。
主从设备之间数据通信采用双向同步串行方式传输,时钟信号由从设备产生。
(1)从设备到主设备的通信当从设备向主设备发送数据时,首先会检查时钟线,以确认时钟线是否是高电平。
如果是高电平,从设备就可以开始传输数据;否则,从设备要等待获得总线的控制权,才能开始传输数据。
传输的每一帧由11位组成,发送时序及每一位的含义如图2所示。
740)this.width=740" border=undefined>每一帧数据中开始位总是为0,数据校验采用奇校验方式,停止位始终为1。
从设备到主设备通信时,从设备总是在时钟线为高时改变数据线状态,主设备在时钟下降沿读人数据线状态。
(2)主设备到从设备的通信主设备与从设备进行通信时,主设备首先会把时钟线和数据线设置为“请求发送”状态。
具体方式为:首先下拉时钟线至少100 us来抑制通信,然后下拉数据线“请求发送”,最后释放时钟线。
在此过程中,从设备在不超过 10us的间隔内就要检查这个状态。
当设备检测到这个状态时,将开始产生时钟信号。
此时数据传输的每一帧由12位构成,其时序和每一位含义如图3所示。
740)this.width=740" border=undefined>与从设备到主设备通信相比,其每帧数据多了一个ACK位。
这是从设备应答接收到的字节的应答位,由从设备通过拉低数据线产生,应答位ACK总是为。
主设备到从设备通信过程中,主设备总是在时钟为低电平时改变数据线的状态,从设备在时钟的上升沿读入数据线状态。
2 PS/2键盘的编码与命令集(1) PS/2扭盘的编码现在PC机使用的PS/2键盘都默认采用第二套扫描码集。
该扫描码集可参考文献[1]。
扫描码有两种不同的类型:通码(make code)和断码(break code)。
当一个键被按下或持续按住时,键盘会将该键的通码发送给主机;而当一个键被释放时,键盘会将该键的断码发送给主机。
根据键盘按键扫描码的不同,在此可将按键分为如下几类:第一类按键,通码为1字节,断码为OxFO+通码形式。
如A键,其通码为Ox1C,断码为OxFO Ox1C, 第二类按键,通码为2字节0 xEO + 0 xXX形式,断码为OxEO+OxFO+OxXX形式。
如 right ctrl键,其通码为OxEO 0x14,断码为OxEO OxFO 0x14, 第三类特殊按键有两个,print screen键通码为OxEO 0x12 OxEO Ox7C,断码为 OxEO OxFO Ox7C OxEO OxFO 0x12; pause键通码为Ox El 0x14 0x77 OxEl OxFO 0x14 OxFO 0x77,断码为空。
组合按键的扫描码发送按照按键发生的次序,如以下面顺序按左SHIFT+A键:1按下左SHIFT键,2按下 A键,3释放A键,4释放左SHIFT键,那么计算机上接收到的一串数据为0x12 Ox1C OxFO Ox1C OxFO 0x12, 在驱动程序设计中,就是根据这样的分类来对不同的按键进行不同处理的。
(2) PS/2键盘的命令集主机可以通过向PS/2键盘发送命令来对键盘进行设置或者获得键盘的状态等操作。
每发送一个字节,主机都会从键盘获得一个应答0 xFA“重发resend"和“回应echo',命令例外)。
下面简要介绍驱动程序在键盘初始化过程中所用的指令(详细键盘命令集见参考文献[1]): OxED主机在本命令后跟随发送一个参数字节,用于指示键盘上num lock, caps lock, scroll lock led的状态; OxF3主机在这条命令后跟随发送一个字节参数来定义键盘机打的速率和延时; OxF4用于在当主机发送OxF5禁止键盘后,重新使能键盘。
3 PS/2键盘与单片机的连接电路PS/2键盘与AT89C51单片机的连接方式如图4所示。
Pi. 0接 PS/2数据线,P3.2 (INTO)接 PS/2时钟线。
因为单片机的P1,P3口内部是带上拉电阻的,所以PS/2的时钟线和数据线可以直接与单片机的P1,P3相连接。
740)this.width=740"border=undefined>4 驱动程序设计驱动程序使用Keil C51语言,Keil uVision2编程环境。
PS/2 104键盘驱动程序的主要任务,是实现单片机与键盘间PS/2通信,以及将接收到的按键扫描码转换为该按键的键值KeyVal,提供给系统上层软件使用。
(1)单片机与健盘间PS/2通信的程序设计在PS/2通信过程中,主设备(单片机)是在时钟信号为低时发送和接收数据信号的。
因为单片机到键盘发送的是指令,需要键盘回应,所以这部分程序采用查询方式;而单片机接收键盘数据时,数据线上的信号在时钟为低时已经稳定,所以这部分程序采用中断方式,且不需要在程序中加人延时程序。
(2)健盘扫描码转换程序设计由于键盘扫描码无规律可循,因此由键盘扫描码获得相应按键的键值(字符键为其ASCII值,控制键如Fl,CTRL等为自定义值),只能通过查表的方式。
由于按键的三种类型及部分按键对应着两个键值(如A键的键值根据CAPS和 SHIFT键状态有 0x41 (A)和 Ox61(a)两种),因此综合考虑查表转换速度和资源消耗,设计中使用4个键盘表:键盘扫描码转换基本集和切换集kb-plain_map[ NR_ KEYS]与kb- shift- map[ NR_ KEYS];包含EO前缀的键盘扫描码转换基本集和切换集kbe0_plain_map[N又KEYS〕与kbe0_ shift-map [ NR_ KEYS]。
PS/2 104键盘按键扫描码最大值为0x83,所以设置NR_ KEYS为132。
所有四个键盘表的定义均为如下形式:KB_ MAP[ MAKE CODE] = KEYVAL,如果扫描码对应的按键为空,如KB_MAP[0x00],则定义相应键值为NULL-KEY(0x00)。
以下是键盘扫描码基本集的部分代码实例:kb_plain_map[NIZKEYS] ={……NULL- KEY; Ox2C; Ox6B; 0x69;Ox6F;Ox3O;0x39;NULL_KEY;//扫描码Ox4O-Ox47刀对应按键空,逗号,K,I,0,0,9,空//对应键值0x00,’,’,'k','i','o','0','9',0x00……};如此设计键盘转换表的另一个好处在于,以后如需扩展支持有ACPI, Windows 多媒体按键键盘时,只需要将键表中相应处修改即可。
如ACPI power按键通码为OxEO 0x37,修改 kbeO _ plain- map [ 0x37 ] = KB _ACPI_PWR即可。
特殊按键PAUSE使用单独程序处理,如果接收到OxEl就转入这段程序;而print screen键则将其看作是两个通码分别为OxEO 0x12和OxEO Ox7C的“虚键,,的组合键来处理。
在驱动程序中声明如下全局变量:led-status其bit0一scroll lock led关0、开 1; bitl一num lock led关为。
,开为1; bit2一caps lock led关为0,开为1; bit3-bit?总是。
;agcs_status记录左右shift ctrl gui alt状态,bit0一左shift键,bitl一左。
trl键,bit2一左gui键, bit3一左alt键,bit4-右shift键,bit5一右ctrl键,bit6一右gui键,bit7一右alt键,相应键按下则对应位为I,释放为。
EO_FLAG接到OxEO置1; El FLAG接收到OxEl置1; FO-FLAG接收到OxFO置1。
按键键值通过Keyval提供给上层使用。
PS/2键盘扫描码键值转换程序ps2_codetrans()流程如图5所示。
第一类按键的扫描码键值转换程序代码:if (FO-FLAG) t//接收扫描码为断码switch (mcu_revchar){//处理控制键case 0x11:agcs_status& = OxF7;break;//左alt释放case 0x12:agcs_status & =0xFE; break; //左shift释放case 0x14:agcs_status&=OxFD; break;//左ctrl释放case 0x58:if (1e走status&0x04)le走status&二0x03; //caps lock键else led_statusl =0x04;ps2_ledchange();break;case 0x59; agcs_status&二OxEF;break;//右shift释放case 0x77:if (led status&0x02 )led_status& = 0x05; //num lock键else led_status{ =0x02;ps2_ledchange();break;case Ox7E; if(led_status&0x01)led_status&=0x06; //scroll lock键else led_statusI =0x01;ps2_ledchange();break;default; break;}FO-FLAG = 0;lse { //接收扫描码为通码if (1e走status衣0x04) caps flag=1;else caps-flag二0;if (led-status & 0x02) num_flag=1;else num-flag二0;if (scga_status&0x11) shift flag=1;else shift flag=0;刀扫描码键值转换if ((caps flag==shift-flag)}1(!num_flag)) KeyVal=b_plain_map[mciLrevchar];else KeyVal二 kb-shift map[mcu_revcha];switch (mcu-revchar){ //处理控制键或状态键case 0x11: agcs_statusl二0x08;//左alt按下case 0x12: ages-status}二0x01;//左shift按下case 0x14: ages-status}二0x02;//左ctrl按下case 0x59:agcs_status}二0x10;//右shift按下default: break;}}740)this.width=740" border=undefined>第二类按键的扫描码键值转换程序与上相似。