前言
探讨键盘的实现,从底层到上层每个层次做了什么
引入
键盘输入是如何被感知的.键盘输入的几个阶段:一是键盘和操作系统交互,二是操作系统传给CPU处理,三是CPU将处理的结果反馈给用户.
键盘到端口
键盘和端口现在通常是USB或者Type-C接口连接,以前是那种U型凹口.网上查了一下,数据位宽是8bit,即8位数据.USB接口采用4线制,2根电源线2根数据线,具体怎样传8bit数据可以不管.只需要明白两点:1.键盘的键位通常是86~104键,有7位(128)就够了.2.想象成有8根线连接到键盘上.
==========================以下↓内容属于发散思维=============================
键盘的识别
USB口和主板南桥的++USB控制器++相连,南桥通过PCI总线和CPU相连.当USB的接口插上去之后,电压发生变化,产生一系列动作.AI搜索"cpu如何识别usb设备"得到的流程简化为:
1.硬件握手:接口电压变化,++主机控制器++得到信息,由PCI总线送给CPU并触发CPU中断程序
以下的动作都和CPU中断有关,中断程序默认写在操作系统中,由CPU引脚触发.
2.读取信息.
包含设备基本信息,设备描述符等信息
3.匹配驱动
调用已写好的驱动程序
这部分内容很底层,和硬件联系紧密.笔者感觉应该由硬件工程师完成.同时给写软件的人一个思路:硬件是如何与CPU产生联系的,操作系统在其中扮演了什么角色.
1.硬件设备通电可以传输信息.这些信息事先已存入设备中
2.读取信息和匹配驱动都是由CPU中断产生的.这些代码和硬件直接相关.例如假设usb控制器连接CPU的中断引脚为100,操作系统中给引脚编地址为0x64,那么识别信息就从下面开始---用上了绝对地址.
/*检测到引脚信号*/
if(*(0x64)=1)或者对应机器指令
键盘驱动
把键盘输入转换成数据.USB控制器通过PCI总线和内存相连(内存有200多个引脚).他的代码类似这样,这里的数据引脚地址是8bit的抽象,得到一个1~255之间的数值.
/*伪代码*/
value=*(数据引脚地址)
int main(void){
if(value==1)
检测到"a";
else if(value==2)
检测到"b";
.....
}
实际上键盘的输入可以是多样的,8bit不止255个键位输入,例如在单片机中有除抖动,或者长按 .类似shift这种键,单按下去不给定义动作,但可以添加组合动作
/*伪代码*/
value=*(数据引脚地址)
int main(void){
if(value==65)
{
delay(100); //延时100象征1ms,除去抖动确认是按下了这个键
if(value==65)
检测到"a";
}
else if(value==32)
{
delay(2000); //延时10000象征延时2秒,表示长按
if(value==0)
无动作;
else if(value==97)
检测到"A"; //组合键,像shift+a
}
.....
}
value是由引脚地址采集到的数值 ---这里给个推理:ASCII码中:a用65表示,A用97表示,中间一个shift差距32,shift在8bit中相当于0B00100000,即第5位的按键(单个无组合)
==========================以上↑内容属于发散思维=============================
操作系统对键盘驱动的支持
1.键盘驱动编译后在操作系统中占一部分空间,CPU匹配驱动要给出驱动程序的地址
有的书本明确提到,操作系统越来越庞大的原因是各种驱动程序占的空间越来越大.
2.操作系统要用一个字节(8bit)来表示采集到的键盘数值.
内核应有专门的数据区,存储硬件数据.
3.操作系统把键盘驱动程序放到CPU缓存区(如果是没有缓存的CPU也没办法).
这点很容易想到,键盘输入不管在哪个程序运行时都有很高优先级,需要CPU及时响应.
4.操作系统给出和键盘相关的接口函数供其他程序调用,如:
//接口函数
/*伪代码*/
value=*(0x00000001); //假设0x00000001是操作系统采集键盘数据的地址
char keyboard_scan()
{
if(value==65)
按下了"a";
return a;
else if(value==66)
按下了"b";
return b;
...
}
当然如果操作系统提供全局变量更好,直接采用绝对地址不符合代码习惯.
其他程序如何用键盘输入
假设有一个游戏程序
/*伪代码*/
int main(void)
{
while(1)
{
keyboard_handle(); //按键处理
if(press alt+f4) //按下alt+f4退出
break;
}
return 0;
}
用上述的接口函数,或者直接用操作系统给的全局变量(如果有的话),
/*伪代码*/
void keyboard_handle()
{
if((keyborad_scan==65)||value==97) //检测到a或者A,按下a或者A表示开火键
开火;
else if ...
}
小结
从键盘的实现推导出硬件到软件的一些过程.笔者对硬件部分也不是很熟,只谈一点思路.看起来硬件部分没有那么灵活,但也不简单,和CPU,总线,内存,操作系统联系紧密.硬件程序的输入条件电平(0或者1),还有对绝对地址的访问.在软件编程中,一般会采用虚拟地址或者底层给出的全局参数,几乎不会访问绝对地址.硬件程序在这点上的好处是绝对地址表示一种很实在的东西,
分开来看,硬件给信号,操作系统给驱动程序,给接口函数.应用程序(或者框架)用接口函数.