键盘作入计算机系统中的人机交互输入设备,其重要性不言而喻。USB键盘驱动较为复杂,需要USB控制器驱动,USB总线驱动和USB键盘驱动,内容比较繁杂。以后有空再来整理这方面的内容。今天重点整理一下笔记本中的矩阵键盘相关知识。
嵌入式BIOS部分:
1,配置矩阵键盘表:
查看如上图所示的矩阵键盘丝印图,对照键盘接口配置矩阵键盘表。虽然各个EC厂商代码的矩阵键盘表配置方式都不一样,但核心思想是一致的。矩阵键盘功能一般分为三部分:
(1)普通键,直接将键值填入相应RC表中,有些矩阵位置没有按键,但有IBM值,这时候也应该将其值录入,一劳永逸,避免以后因使用不同国家的键盘而重复劳动。
(2)FN功能键,按住FN键的时候,置FN标志位,和其他键组合使用。此时,该键值一般作为索引,在代码中,再写FN功能键的各个功能函数。
(3)Overlay键,该键值也是索引,按NUMLOCK键时,驱动会下命令,置NUMLOCK指示灯,我们根据灯的状态来区别所按下键是数字键功能还是做字母键功能。
注:如果OEM主板厂商不差钱,可以自已弄丝印图和键盘接口,然后丢给键盘厂商,让键盘厂商根据它们来开模。当然如果主板厂商不想这样做,买别人现成的产品,根据键盘线排键盘接口也是可以的。
2,配置寄存器
除了配置矩阵键盘表,EC与HOST端访问的“通路”也要打开。有一些相关寄存器需要配置:像键盘IBF的中断使能,SCANIN中断使能,还有Keyboard的逻辑设备初始化所牵涉的寄存器(当然,这部分也可以让系统BIOS来配置)。
一般来说,嵌入式BIOS工程师做完以上两种事,键盘的功能也就做完了。至于矩阵键盘扫描以及与HOST端的交互,不了解它们也不妨碍我们做项目,当然了解最好。当我们遇到比较棘手的问题,了解整个代码流程,对于我们快速解决问题有大大的帮助。
键盘扫描流程:
1) SCANIN中断,SCANIN有8根PIN,共亨一个中断。平时此8根PIN电平为高,一旦按下矩阵键盘,就会触发中断,此时扫描程序正式开始。
2) 扫描时间并不是固定的,我们可以用1ms,2ms也可以用5ms的时间间隔来进行一次键盘扫描。
3) 扫描按照逐列扫描进行,每次将要扫描的列置低,其他列置高。如果有检测到8个SCANIN有某个变低,就说明该列和该行有按键按下。按键被按键下,我们需要去抖,以防止有静电或其他原因导致键盘扫描被误触发。去抖过后,我们就可以将扫描到的行列交叉点记录的键盘送给相关处理函数,相关处理函数会将传过来的行数列数值变成SCANCODE第二套码值送给键盘BUFFER。
4) 键盘BUFFER传送函数会将SCANCODE SET2转换成SCANCODE SET1传给上层驱动。虽说驱动可以决定接收SET1还是SET2的码值,但我见过的驱动都要SET1,没有要SET2的。不知道有没有驱动能认SET2的?
系统BIOS部分:
1,解码IO资源并使能键盘逻辑设备
解码IO资源前,应该先看ISA配置空间是否支持subtractive decoding,如果支持,不需要我们做解码工作,反之,我们需要手动decoding。
如果是EC,可以自已使能键盘逻辑设备,但如果是SuperIO,我们就必须在系统BIOS中添加使能逻辑设备的代码。
使能之前,一定要先确定Base Address是2E/2F,4E/4F还是164E/164F。然后根据寄存器对来访问逻辑设备。
OutPortB(0x2E,LDN);OutPortB(0x2F,LDNNumber);
OutPortB(0x2E,ActiveByte); OutPortB(0x2F,1);
2,支援ACPI OS加载PS2键盘驱动
为了使ACPI OS能够正常加载PS2键盘驱动,我们需要在ASL中加入PS2 Device的宣告。这样操作系统内核就在AML中找到HID为PNP0303的Device后,变会加载PS2键盘驱动。
3,确保PS2键盘驱动在EFI环境下正常挂载
作为系统BIOS工程师,首先要确保PS2驱动能够正常挂载到controller上。为此我们要确保两件事:第一,我们的驱动会被正常编译,即dsc文件有包含键盘驱动inf文件;第二,PS2键盘的DevicePath会被正常创建。对于针对Platform的BIOS工程师来说,只要这两个条件满足了,PS2键盘就能在EFI环境下正常使用。
下面我们来看下,DevicePath是如何被创建的:
在Isabus.c的IsaCreateDevice()函数中,有如下代码段:
这里就是创建DevicePath的地方了。但如何创建PS2键盘的DevicePath呢?根据代码可以知道,IsaBus驱动在start时,会枚举ISA设备,并读取资源配置。
据此,我们追踪DeviceEnumerate()或GetCurResource()函数的原型。
让我们再来看下IsaDeviceLoopUp()的函数体:
很明显, gPcatIsaAcpiDeviceList[]数组就是我们所要找寻的源头。我们只需要将PS2键盘的资源和设备ID放入这个数组就可以了。
UDK2014是这样来添加PS2键盘的DevicePath的。虽然各个BIOS厂商的做法和这个并不一样,但思路都是一样的。
那么PS2键盘驱动Start函数做了什么动作呢?
其实它主要工作就是为控制台提供一个键盘型输入设备。EFI设计有一条理念就是模块化。各个输入设备驱动都是一个模块,最终都是为控制台服务的。用户只会使用gRT->ConIn而不会使用Ps2Keyboard.c的协议。PS2键盘作为一个输入设备,要为控制台提供的主要服务主要有:Reset(),ReadKeyStroke(),WaitForKey事件以及TimerEvent事件。
1,Reset
这个函数主要处理PS2键盘的初始化,并且重置键盘相关的变量和寄存器。这些过程需要下一些PS2命令来完成,这就牵涉到4个寄存器,当然从系统BIOS的角度来说,这四个寄存器可以看成是2个IO端口。这4个寄存器如下所示:
SystemBIOS读命令步骤:
1)读Data寄存器60H(有没有这一步骤均可,严谨说来应该需要)
2)读Status寄存器64H,OBF=1执行3,否则等待,超时退出
3)读Data寄存器60H
SystemBIOS写命令步骤:
1) 读Status寄存器64H,IBF=0执行2,否则等待,超时退出
2) 写Command寄存器64H
这些命令主要有:
#defineKEYBOARD_8042_COMMAND_READ 0x20
#defineKEYBOARD_8042_COMMAND_WRITE 0x60
#defineKEYBOARD_8042_COMMAND_DISABLE_MOUSE_INTERFACE 0xA7
#defineKEYBOARD_8042_COMMAND_ENABLE_MOUSE_INTERFACE 0xA8
#defineKEYBOARD_8042_COMMAND_CONTROLLER_SELF_TEST 0xAA
#defineKEYBOARD_8042_COMMAND_KEYBOARD_INTERFACE_SELF_TEST 0xAB
#defineKEYBOARD_8042_COMMAND_DISABLE_KEYBOARD_INTERFACE 0xAD
#defineKEYBOARD_8048_COMMAND_CLEAR_OUTPUT_DATA 0xF4
#defineKEYBOARD_8048_COMMAND_RESET 0xFF
#defineKEYBOARD_8048_COMMAND_SELECT_SCAN_CODE_SET 0xF0
以及0xED,设置LED的命令
如果这些命令检测没有通过,这个设备就无法在EFI Console环境下使用。这个阶段一般不会出问题。我曾遇到VxWorks下使用WindML库,PS2键盘无法使用的问题,经过调试,我去掉一个Command检测就好了(键盘firmware并不一定每个command都回应)。所以为了兼容更多的键盘,这些命令保留最基本的就好,不需要用太多。
2,TimerEvent事件
这个事件一般设置为20ms一次。把读到的PS2 Set1 scancode转成EFI所认识的UnicodeChar。
*****马太福音25章25节:凡有的,还要加给他,叫他有余。没有的,连他所有的,也要夺过来*****