上篇我们着重介绍项目上所需的显示方面的知识,既然谈到人机交互,总要有人对机器下达指令,所以本篇就讨论键盘输入方面的相关知识,以期读者能够从底层理解键盘输入全过程的处理,当然我们还要围绕这个小程序xu继续展开完善,边完善边扩充知识:)
1.键盘的发展
![]()
![](https://img-blog.csdn.net/20151004143730793?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![]()
![](https://img-blog.csdn.net/20151004143751922?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](https://img-blog.csdn.net/20151004143846015)
![]()
![](https://img-blog.csdn.net/20151004143928338)
截至的目前为止,我们可以获取的键盘输入方法很多,总结一下:
![]()
![](https://img-blog.csdn.net/20151004143953826?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1.键盘的发展
键盘的发展大致经历XT键盘到AT键盘再到PS2键盘(USB键盘不再本文讨论之列),这也是随着PC个人电脑发展阶段而演进的。
今天我们使用的键盘大约就是像是
AT
键盘和
PS2
键盘的合体:使用
PS2
键盘引入的小口,又支持
AT
扫描码(Scan Code Set2
)。为便于对比,笔者以图表相似展现,参考下图1。如果
读者有兴趣了解详情,可以参阅prostar的博文“
键盘简史”连接如下:
http://blog.csdn.net/prostar/article/details/4144036
图1 键盘发展与对比
2.键盘基础原理
2.1 键盘数据传输
在最底层,键盘是有横纵矩阵组成的,这个跟51单片机里的矩阵键盘(matrix
)是一致的。目前,键盘的具体实现形式多样,有机械接触式,电容式,轻触式等,但键盘基本上都是由单片机8048和按键组成,而主板端一般是8042键盘控制器用来与键盘链接,通常其被集成到Supper IO中或桥片里面。
图2 键盘工作示意图
8048负责检查哪个键按下,并把对应位置码(扫描码Scan code,这里分通码和断码)转换为传输码:1位起始位(0)+ 8位扫描码 + 1位奇偶校验 + 1位结束位。把并行的数码转变成串行的数据,通过键盘线传送到计算机主板端的键盘控制器
804
2,8042
再把串行数据转换成并行数据,提供给对应的IO端口,供CPU来使用。这里提到的IO端口,正是8042在X86系统里占用的 0x60和0x64 Port。
图3 键盘IO寄存器功能
2.2 键盘数据的接收
下面再说下CPU是怎样来用这个端口获取键盘输入的,有两种途径接收键盘控制器过来的数据,Polling和IRQ中断。
Polling轮询
轮询就是CPU等待0x64状态寄存器Bit0被置起,然后读取IO端口0x64的数据字节,Polling便于理解和实现,但存在两个问题,1是正如所有的轮询方式的共同弊端,浪费了很多CPU的时间;2是当PS2控制器支持两个设备时,没有办法确定是哪个设备发过来的,除非是关闭其中一个或是其根本不支持发送数据过来。就如PS/2的键盘和鼠标同时接到系统PS/2控制器上,就不合适用这种方式。
IRQ中断
键盘的中断是IRQ1,当IRQ1触发时,触发CPU进行中断响应,调入对应的中断处理向量(
INT 9h)
地址,该中断处理程序是由BIOS提供的,中断程序直接从IO端口0x60读取设备端发过来的数据,这里无需再判断状态寄存器的Bit0了,因为接收到IRQ1就代表键盘设备有数据发过来要处理,当读完IO端口数据后,向中断控制器发送EOI,从中断处理中返回。这不言而喻,会比Polling节省CPU的资源暂用,另外,对于两个以上的PS/2设备,其不同的IRQ已经将其区分,无需担心不知道是谁发过来的数据了。当然,仍然存在一种情况避免不了,就是当主机端需要给PS/2控制器发送发送命令并需要返回时,PS/2控制器会将数据放到IO端口中,但无法产生IRQ终端的,因为IRQ中断是由其下面的PS/2设备发过来的,因此,在这种情况下,只能用Polling轮询来解决,同时需要关闭所有的PS/2设备,以确保是PS/2控制器返回的数据,当然这样操作仅是在PS/2控制器初始化时才需要的,初始化完毕后无需再去Polling PS/2控制器返回的数据了。IRQ1对应的中断响应正是INT 9h,即向量号是9,为什么是INT 9h,可以参考PIC中断控制器的初始化相关介绍。
图4 键盘中断处理过程
INT 9h
用户按键,由8042发出IRQ1中断,BIOS调用INT 09中断服务程序。INT 09先从8042取得系统扫描码,然后根据扫描码值判断用户按键并作相应处理,最后通知中断控制器8259本次中断结束并实现中断返回。
若用户按下双态键(如:Caps Lock、Num Lock和Scroll Lock等),则在键盘上和BDA上相应LED指示灯的状态将发生改变;
若用户按下控制键(如:Ctrl、Alt和Shift等),则在BDA上相应的键盘标志位将发生改变;
若用户按下功能键(如:F1、F2、…等),再根据当前是否又按下控制键来确定其软件扫描码,并得到该按键所对应的ASCII码;
若用户按下字符键(如:A、1、+、…等),再根据当前是否又按下控制键来确定其软件扫描码,并得到该按键所对应的ASCII码;
若用户按下功能请求键(如:Print Screen等),则系统直接产生一个具体的动作。
对某些键或键的组合定义为单字节的ASCII码和扫描码,供系统中断INT 16H使用。
对某些键仅设置变量,反映其被按下或释放的状态。
对某些键或键的组合产生特殊操作。
2.3 键盘输入处理的封装
INT 16h
为了方便上层软件或系统调用,除了必须的硬件终端服务程序外,Legacy BIOS还将键盘数据读取操作封装到BIOS软中断服务 INT 16h 中,为传统系统如DOS或Boot Loader等软件调用。
00H、10H —从键盘读入字符 03H —设置重复率
01H、11H —读取键盘状态 04H —设置键盘点击
02H、12H —读取键盘标志 05H —字符及其扫描码进栈
BIOS中断方便程序员不必了解硬件I/O接口的特性,可直接用INT指令参数,读取键盘按键信息。
INT 21h
使用DOS操作比使用相应功能的BIOS操作更简易,而且对硬件的依赖性更少些。对一般的键盘操作,我们也可以使用INT 21H中断提供的键盘功能调用。
01H — 从键盘输入一个字符并回显在屏幕上
06H — 读键盘字符
07H — 从键盘输入一个字符,不回显
08H — 从键盘输入一个字符,不回显,检测Ctrl_Break组合键
0AH — 输入字符到缓冲区
0BH — 读键盘状态
0CH — 清键盘缓冲区,并调用一种键盘功能
C相关库函数
C语言库函数也提供了一系列的键盘接收函数,如下表
表1 C库提供的关于键盘输入的函数
1.通过C提供的库函数
;
2.通过DOS中断INT 21h;
3.调用BIOS软中断INT 16h;
4.直接通过IO端口获取对应按键的Scan Code
2.4 BDA键盘数据缓冲区
BIOS在内存低端的BIOS数据存储区开设一个键盘缓冲区,其作用如下:
(1)可以满足键盘实时输入要求。
(2)适应随机访问的应用要求。
(3)能容纳快速键盘输入要求。
图5 BDA键盘数据缓冲区操作示意图
键盘缓冲区是一个先进先出的环形队列,0000:041Eh~ 0000:043Eh,16个字。不管是硬中断INT 9h还是软中断INT 16h都对这个BDA键盘数据区域进操作。INT 9h负责送入元素,而INT 16h负责取走元素。所谓的元素就是键盘的ASCII码和scan code.而这正好是INT 16h的function 0号功能返回的数据格式。
3. XU中的键盘处理方式
前面提到过,对键盘的处理,BIOS已经封装过一层,DOS系统也提供对应的封装,而在C语言库函数也提供一些接口给到应用程序调用。在C库中,有一类专门为DOS等Legacy系统提供IBM兼容机的BIOS软中断调用的库<bios.h>, 当然,我也可以通过嵌汇编或调用<i86.h>提供的Intel中断调用接口函数int386(int86)直接指定要调用的中断号,在这里,我们用<bios.h>提供的_bios_keybrd实际上就是BIOS软中断提供的INT 16h服务。
这里我们对通过_bios_keybrd 的_KEYBRD_READ 服务返回的数据稍作处理,因为这里返回的高字节是扫描码,低字节是ASCII码(如果有的话), 我们不妨采取如下策略,当输入的键盘有ASCII码时,就只保留低字节的ASCII码,当没有时,只保留高字节的扫描码。这样处理特殊功能键会方便些。至于对多种键盘输入可能的处理,用switch语句会明朗些。本文先只针对主页面菜单作输入处理,在按方向键时,菜单随之变化,来选择准备查询的系统信息。
如下为XU基本流程图
图6 XU主框架流程图
具体实现请参考附件代码(本系列参考代码均可以在CSDN资源中找到),本文介绍键盘输入的原理和一些处理键盘输入的函数及中断,最后介绍了XU软件键盘处理的方法,虽然只是针对主界面的菜单项的键盘输入处理,后续各子功能模块的键盘处理方法类似,不再赘述。下一节,我们将开始介绍memory的读写方法,敬请期待。
4.参考资料
PS2键盘介绍 百度文库
图解键盘的内部结构与原理 http://www.docin.com/p-832958.html
The PS/2 Keyboard Interface http://www.computer-engineering.org/
"8042" PS/2 Controller
http://wiki.osdev.org/"8042"_PS/2_Controller