投稿人:Rand

公众号:Rand_cs

KEYBOARD

键盘,咱们做计算机这一行的自然不必多说,天天与它打交道。但熟归熟,清楚键盘背后的原理吗?键盘上都标有各键的名称,表明了各键所代表的意义,但是计算机是如何知道的?组合键是怎样实现的?按下一个代表字符的键,怎么变成平常使用的ASCII码的?

看完本文,相信你就能了解键盘的本质,知晓这些问题答案。

01

相关介绍

键盘编码器

键盘编码器(i8048),是键盘里的芯片,主要用来监控是否有键按下,弹起,然后向键盘控制器报告此键的相关信息。键盘编码器就像是键盘的嘴,让键盘能够说话,表达目前按键状态。Num Lock键和Caps Lock键的LED灯的开关也归它控制。

键盘扫描码

上述所说的信息就是键盘扫描码,一个键有按下就会有弹起,所以每个键会有两个状态,即每个键将会对应两个扫描码,键被按下时的编码叫做通码(makecode),弹起时的编码叫做断码(breakcode)。

大部分键的通码和断码都是 8 位 1 字节,但有些操作控制键如 ctrl、alt附加键如Insert小键盘区如 / ,方向键等是 2 字节甚至多个字节。有多个字节的扫描码都是以 0xe0 开头。只有Pause Break一个键是以 0xe1 开头

断码与通码的关系:断码 = 通码 + 0x80。0x80 二进制表示为 1000 0000,所以对于断码和通码可以这样理解,它们由8位比特组成,最高位第7位表示按键状态,1表示按下,0表示弹起。

注:上述为第一套键盘扫描码的情况,现下使用的键盘基本使用的第二套键盘扫描码,但是为了兼容,最终还是要将第二套扫描码转化为第一套扫描码,这也是键盘控制器工作的一部分。

键盘控制器

键盘控制器(i8042),不在键盘内部,被集成在南桥芯片上。它主要是接收键盘编码器发来的扫描码(第二套),解码(转成第一套)后保存到自己的寄存器中,然后通过中断控制器发送中断请求。

i8042有4个寄存器,如下所示:

其中输入缓冲区和输出缓冲区共用0x60端口,状态控制器和控制寄存器共用一个0x64端口。

共用不会冲突吗?注意读写状态的不同,CPU使用int指令从8042读数据时 0x60 代表输出缓冲区,CPU使用out指令将数据写入8042时 0x60代表输入缓冲区,状态寄存器和控制寄存器同理。

注:输入输出要视对象决定,对键盘控制器来说是输出,那么对CPU来说则是输入,使用 in 指令。

每个寄存器都是8位的,保存扫描码时最多只能保存8位1字节的扫描码,每次键盘中断服务程序也只能处理 1 字节的扫描码。也就是说键盘中断的次数不是你按键、弹起的次数,而是按键、弹起对应的通码、断码(第一套)字节数。由此可以看出平时我们敲键盘时那是发生了无数次的中断呐。

那有的按键信息不是多个字节的扫描码吗? 的确是,但硬件环境如此,不能改变,只能在软件上下功夫,这就是接下来要说的键盘的中断程序,先看看键盘中断的流程,好有个清晰的路线。

02

键盘中断流程

其实上述的相关介绍已经涉及了部分键盘中断流程,在此从头至尾具体说说,先看流程图:

1. 键盘编码器监控是否有键按下或弹起,若有键按下,向键盘控制器发送此键的通码;若有键弹起,则发送断码(基本发送第二套键盘扫描码)。

2. 键盘控制器接收来自键盘编码器发来的扫描码,解码转化成第一套扫描码,保存到自己的输出缓冲区中,然后通过中断控制器向CPU发送键盘中断信号。

3.  后面的流程基本和上文讲的中断流程一样了,在此简述:未关中断的情况下CPU响应,中断控制器再通过数据线发送中断向量号,CPU据向量号定位中断服务程序,期间检查特权级自动压栈,然后运行中断服务程序处理中断。

03

键盘中断服务程序

键盘中断在所有的可屏蔽中断中优先级仅次于时钟中断,也需要尽快的处理。在Linux 0.11里的整个键盘服务程序都是用汇编来写的,汇编语言直接操作底层的指令,没有编译器来增加额外的东西,所以运行起来比高级语言写的程序快,但也增加了编写程序的难度。

linux0.11版本的键盘中断服务程序的框架源码如下图所示:

这个框架程序主要做了以下事情:

保护现场——压栈

上文中写到压栈ss, esp,  eflags, cs, eip, error_code (若有特权级变化且中断带有错误码) 来保存现场,那只是CPU自动执行的部分,完全保存原任务的信息还是在中断处理程序中进行的。

如上图所示,键盘中断服务程序里通用寄存器只保存了4个,eax, ebx, ecx, edx,若为了省事不追求效率完全可以无脑操作pushad压栈所有的通用寄存器,但人家是Linux系统嘛,虽然只是0.11版本,但也要追求精确,效率,只压栈中断程序需要用到,可能破坏的寄存器。

读取扫描码

inb $0x60, al  从键盘控制器的输出缓存区0x60端口读取扫描码。若不从输出缓冲区读取数据的话,键盘控制器是不会继续工作的,意思是无论你怎么按键,键盘控制器不会响应键盘操作,不会存下新的扫描码发送中断信号等。当然不读取扫描码后续的键盘中断程序也没法工作没有意义,在此只是说明一下。

判断是否为 0xe0 或 0xe1

如果扫描码是 0xe0 或者 0xe1,那说明这个键的扫描码是有多个字节的,需要先保存下来等待接下来的扫描码组合成完整的扫描码。 

寻址、调用相应的键处理程序

拿到完整的扫描码之后就该去寻找相应的键处理程序了,源码中有个key_table,table, 说明它是一张表,或者说一个数组,这里面就按照扫描码大小存放了各个键的实际处理程序地址。

如何找到相应的键处理程序呢?其实跟数组用下表获取元素一样,只是汇编里面有一些听起来高大上的名词:据源码所示采用比例变址寻址的方式,即key_table(, %eax, 4),也就是说相应的键处理程序的地址是key_table + eax * 4。key_table,相当于数组首地址;eax里面存放的扫描码,扫描码可以看成数字索引号,相当于数组下标;地址32位,4字节,所以乘4。

回复现场——出栈

压栈保护现场的逆过程,在此不再赘述,需要注意执行到 iret 时的栈顶应是 eip。

04

键处理程序

键的扫描码有通码和断码,有着不同的处理,主要的键处理程序我分为了以下几类(各点开头出现的名字都是Linux0.11中实际键处理程序的函数名称):

1、 ctrl,alt,caps,shift,num等控制键处理程序,整个键盘中断程序维护了两个8位的变量mode和leds。它们的每一位(没用完)代表着一个键,1表示按下,0表示弹起。mode 代表的键有caps, alt, ctrl, shift。leds代表的键有NumLock, CapsLock, ScrollLock。所以操作控制键的键处理程序就是设置变量的相应位。

2、 do_self,处理普通键的程序,主要的功能就是将扫描码转换成ASCII码,然后放进键盘缓冲区中。键盘中断程序维护了一张扫描码到ASCII码,名为key_map的映射表,do_self依据这张表做转换。

3、 func, 处理功能键如Fxx键的程序

4、 cursor,设置光标位置,它是处理方向键,PgUp,Backsp等键的程序

5、 unctrl, unshift等,将mode和leds复位,如unctrl将mode中的ctrl位置0。

6、 none,除开特殊键的断码对应的键处理程序,什么都不做,直接返回。而特殊键的断码处理程序就是上述的5,复位就行。

由上,我们也能得知平时可能成为习惯但没具体关注的几个问题:

1. 使用组合键时需要先按下控制键。键盘的中断程序为这些控制键设置了标识(mode/leds)。先按下控制键,程序为控制键设置好按下状态,再处理后到来的键时会检查这些标识,是否有控制键按下,以便做出不同的操作。

2. 组合键按键时有顺序,但弹起无顺序要求。由上面的键处理程序可知,只有通码的键处理程序在做事,而断码的键处理程序除了特殊键需要复位之外其他键都是直接中断返回的。所以使用键盘控制输入时重要的是按键,而不是键弹起,所以只要按键对了,怎样弹起并不重要。

3. 一直按着某个键时会一直触发键盘中断,若是普通的字符键,电脑屏幕可能会出现一直打印某个字符的现象。若是一些控制键,则中断程序可能会不停地将这个键设为按下状态。当然,键盘中断程序是否记录上次按键取决于具体实现,大多是不记录的,触发一次键盘中断就处理一个扫描码。

END

关于键盘控制输入的原理就是这样,这条线应该还是很清楚的。键盘输入是以键盘中断为核心的,如果还不是很清楚,可以回头看看键盘中断的流程图。

好啦,本文就到这里,如果有什么错误还请批评指正。如果有所帮助,还请多多关注支持。

天天当键盘侠,你知道从按键到响应的底层原理吗?相关推荐

  1. 苹果怎么设置手写键盘_Mac使用技巧:苹果键盘的一个或多个按键没有响应怎么解决?...

    苹果键盘的一个或多个按键没有响应怎么解决??请先使用"虚拟键盘"测试键盘按键是否在按下时正常响应.具体步骤,请参考下方: 选取苹果菜单 >"系统偏好设置" ...

  2. Mac苹果键盘多个按键没响应该如何解决呢

    1.选取苹果菜单 >"系统偏好设置" 2.然后选择"语言与地区". 3.点按"键盘偏好设置"按钮. 4.点按"输入法&quo ...

  3. VC++中单个键盘按键的响应

    关于如何在VC++程序中自定义热键(或称组合键)及其响应,我在之前的博客中已经作了详细的介绍,那么当遇到的是单个键盘按键的响应时,我们又该怎么解决呢?这时,我们可以采用afx_msg BOOL Pre ...

  4. RK3399 Android7.1按键进入Loader模式的原理

    烧录系统固件,一般是接着USB线的时候同时按下电源按键+音量按键(音量+/-),具体是"音量+"按键还是"音量-"按键根据硬件设计确定.原理就是uboot中检测 ...

  5. 新版按键精灵X版 底层使用代码分享

    按键精灵X亮点多多,这里分四方面简要说明一下: 1.性能提升:代码行效率提升70%以上,函数调用效率提升40%以上,同脚本整体性能比按键精灵2014提升10%以上. 2.体验更好:调试反应迅速,多次调 ...

  6. android电话按钮无响应,华为手机死机、按键无响应怎么办? 华为手机死机无响应解决方法...

    手机死机.卡死.按键无响应的时候您着急,咱先解决了问题再寻找手机卡死的原因. 华为手机死机无响应解决方法 一.卡顿等待无结果,先重启! 大家都会吧!长按电源键3秒,然后选择"重启" ...

  7. android gridview 按钮事件处理,Android TV gridview 的按键事件响应巧变 事件分发机制...

    在gridview点击事件之后,打log看了下, 看到gridview的onclick事件响应的竟然排在onkeyup之后,这造成100~200ms的延迟啊, 后来换了在其它响应事件里面截取,但是在o ...

  8. 笔记连载 | Day15【按键检测、按键消抖】 【原理及verilog仿真】篇

    听说99%的同学都来这里充电吖 本系列为线下学员学习笔记整理分享,如有想要报名参加线下培训,可以点击以下超链接文章了解,购买开发板可以到叁芯智能科技企业淘宝店下单. 各类研修班 | FPGA就业研修班 ...

  9. 网摘-按键精灵屏幕找色原理分析

    一.数据提取 位图其实可以看成是一个由象素组成的矩阵,找图找色可以看成是象素值的比对.很多新手在设计这类的程序时喜欢使用TBitmap.Canvas.Pixels属性,这个属性其实是对API函数Get ...

最新文章

  1. QT开发(五十)——QT串口编程基础
  2. 如何设置电脑自动锁屏_如何把视频设置为 iPhone 动态锁屏壁纸?
  3. java 事件驱动模式_事件驱动
  4. pcb成型板aoi检测_一种PCB板的AOI检测控制系统的制作方法
  5. SQL循环执行while控制
  6. [Charset]UTF-8, UTF-16, UTF-16LE, UTF-16BE的区别
  7. mysql必知必会学习笔记(一)
  8. 北京 | Aibee 爱笔智能 招聘 计算机视觉算法实习生
  9. jquery元素插入、删除、清空
  10. C语言编程七层塔381,【计算机类职业资格】二级C语言-381 (1)及答案解析.doc
  11. V8 7.4释出!支持JIT-less不需可执行内存也能执行
  12. iOS底层探索之KVO(一)—KVO简介
  13. 中美深度对比,资产管理行业的核心是什么?
  14. SVM支持向量机-——希尔伯特空间解释
  15. 随e行wlan v9.2.0930
  16. 宝塔linux面板-安装golang环境,宝塔面板Linux环境-安装Golang:Go语言环境
  17. 全“芯”赋能,SOM3568核心板
  18. 撤回/修改已经发送的Outlook邮件
  19. Codeforces1388 D. Captain Flint and Treasure(贪心)
  20. 算法仿真----分治法找假币

热门文章

  1. 【SIFT,FPGA】基于FPGA的图片sift算法,图像的配准融合在上位机上实现
  2. 我的世界卸载java_《我的世界:地下城》出现恶性BUG 卸载会清空SSD
  3. Libtool library used but ‘LIBTOOL‘ is undefined
  4. 源码下载丨如何制作炫酷又高效的 3D 技能特效?带你轻松放大招!
  5. win7 sp1 x64 离线安装.net framework 4.6.1
  6. o(1)复杂度之双边滤波算法的原理、流程、实现及效果
  7. ORIGYN 推出的 NFTs For Good计划将拍卖伊万·拉基蒂奇等欧洲足球巨星的数字足球球衣并捐献给欧足联儿童基金会
  8. ACCESS实例2 资料管理4——资料管理的报表
  9. 潍坊刘洁新东方计算机学校,美本申请,最强访校攻略来了
  10. AT91RM9200---电源管理控制器(PMC)介绍