HACK#49 关闭键盘的LED来省电
为了尽量减少电能消耗,关闭键盘LED灯等,对键盘进行控制。
小时候,父母经常教育我“房间里没人的时候要关灯”。突然看到键盘上还有LED灯闪着美丽的光芒却无人欣赏。要省电就必须关掉这些没有必要的LED。使用无线键盘等时,为了抑制电能消耗,可能会想要关闭显示键盘NumLock是否启用的LED等。这种情况下禁用NumLock也可以,这里介绍的是启用NumLock的同时关闭LED的方法。首先介绍怎样控制键盘,即PS/2键盘的规格。
PS/2键盘
我第一次接触电脑的时候,说起键盘,都是PS/2连接的。我家的电脑至今还有PS/2连接的键盘。AT键盘和PS/2键盘是IBM发布的主流键盘。PS/2键盘是作为能够与AT键盘互换的键盘发布的。PS/2是AT的扩展,添加了一些命令。笔者家用的键盘是PS/2兼容的键盘,但是操作系统启动时显示的是AT互换键盘。也有不少键盘不支持PS/2的所有命令。IBM原本只是将Intel i8042作为键盘输入的编码用控制器使用。
因此,PS/2兼容键盘一般是以PS/2兼容模式连接到Intel i8042互换芯片组的。PS/2连接的键盘,是通过PC主板上的i8042互换控制器(板载的微控制器)与键盘中的微控制器(键盘微控制器)进行通信来控制键盘。可以使用in命令、out命令经由0x60、0x64的I/O端口从CPU访问控制器。
通过读出(in命令)I/O端口0x64,就可以检查板载微控制器的状态。板载微控制器的Status寄存器各位的意义如表6-26所示。
针对I/O端口0x64执行Write(out命令)就可以向板载控制器发送命令。针对I/O端口0x60的Write命令被发送到键盘微控制器。
从键盘微控制器发送的数据,从I/O端口0x60读出。用于读取一般键盘输入等的数据。Status的第0位和第1位用于信号交换(握手)。在向I/O端口0x60、0x64进行写入(发送命令)前需要确认第0位是否为0(Output Buffer empty)。当第1位为1(Input Buffer full)时,可以从I/O端口0x60读取数据。细节参见表6-27。
命令0x20和0x60对板载微控制器的命令字节进行读写。这个命令字节的各位含义如表6-28所示。
向I/O端口0x60写入表示直接向键盘微控制器发送命令。向I/O端口0x60写入必须在确认Status的第0位为0后进行。向键盘微控制器发送的命令如表6-29所示。
通过上面的介绍,你应该已经了解LED的控制方法了。下面介绍如何控制LED的代码作为参考。
1#include<stdio.h>
2#include<sys/io.h>
3#define KBD_CMD_PORT 0x60
4#define OBD_STS_PORT 0x64
5
6#define OBS_FULL 0x1
7#define SEND_LED 0xED
8
9#define SCROLL_LOCK(1<<0)
10#define NUM_LOCK(1<<1)
11#define CAPS_LOCK(1<<2)
12
13 void send_cmd(cmd, port)
14{
15 char sts;
16 do{
17 sts=inb(OBD_STS_PORT);
18}while(sts&OBS_FULL);
19 outb(cmd, port);
20 usleep(100000);
21}
22
23 int main(void)
24{
25 ioperm(KBD_CMD_PORT,1,1);
26 ioperm(OBD_STS_PORT,1,1);
27
28/Turn on a LED of CAPS_LOCK/
29 send_cmd(SEND_LED, KBD_CMD_PORT);
30 send_cmd(CAPS_LOCK, KBD_CMD_PORT);
31
32/Turn on a LED of NUM_LOCK/
33 send_cmd(SEND_LED, KBD_CMD_PORT);
34 send_cmd(NUM_LOCK, KBD_CMD_PORT);
35
36/Turn on a LED of SCROLL_LOCK/
37 send_cmd(SEND_LED, KBD_CMD_PORT);
38 send_cmd(SCROLL_LOCK, KBD_CMD_PORT);
39
40/Turn off LEDs/
41 send_cmd(SEND_LED, KBD_CMD_PORT);
42 send_cmd(0,KBD_CMD_PORT);
43
44 ioperm(KBD_CMD_PORT,1,0);
45 ioperm(OBD_STS_PORT,1,0);
46 return 0;
47}
首先在第25、26行允许I/O端口0x60、0x64的访问。然后使用send_cmd()函数,在写入指定的I/O端口(port)前获取板载控制器的Status,确认Output Buffer为empty,确认后向I/O端口写入值(cmd)。在第29行为了点亮CAPS_LOCK的LED灯,发送命令(0xED)。然后写入用来亮灯的CAPS_LOCK的LED值(4)。按同样方法依次点亮NUM_LOCK、SCROLL_LOCK后,将所有的LED熄灭。