一、键盘的基础知识
1.键盘种类
XT 很悠久了
AT
PS/2
USB 现在用的
1.总共有3套扫描码
XT键盘Scan code set1
现在AT和ps/2是Scan code set2
如果是set2最终还是会转换成set1的为了兼容,所以我们管好set1就好了
Keyboard Scan Codes: Set 1
*Alle Angaben Hexadezimal
101-, 102-, and 104-key keyboards:
KEY |
MAKE |
BREAK |
KEY |
MAKE |
BREAK |
KEY |
MAKE |
BREAK |
A |
1E |
9E |
9 |
0A |
8A |
[ |
1A |
9A |
B |
30 |
B0 |
` |
29 |
89 |
INSERT |
E0,52 |
E0,D2 |
C |
2E |
AE |
- |
0C |
8C |
HOME |
E0,47 |
E0,97 |
D |
20 |
A0 |
= |
0D |
8D |
PG UP |
E0,49 |
E0,C9 |
E |
12 |
92 |
\ |
2B |
AB |
DELETE |
E0,53 |
E0,D3 |
F |
21 |
A1 |
BKSP |
0E |
8E |
END |
E0,4F |
E0,CF |
G |
22 |
A2 |
SPACE |
39 |
B9 |
PG DN |
E0,51 |
E0,D1 |
H |
23 |
A3 |
TAB |
0F |
8F |
U ARROW |
E0,48 |
E0,C8 |
I |
17 |
97 |
CAPS |
3A |
BA |
L ARROW |
E0,4B |
E0,CB |
J |
24 |
A4 |
L SHFT |
2A |
AA |
D ARROW |
E0,50 |
E0,D0 |
K |
25 |
A5 |
L CTRL |
1D |
9D |
R ARROW |
E0,4D |
E0,CD |
L |
26 |
A6 |
L GUI |
E0,5B |
E0,DB |
NUM |
45 |
C5 |
M |
32 |
B2 |
L ALT |
38 |
B8 |
KP / |
E0,35 |
E0,B5 |
N |
31 |
B1 |
R SHFT |
36 |
B6 |
KP * |
37 |
B7 |
O |
18 |
98 |
R CTRL |
E0,1D |
E0,9D |
KP - |
4A |
CA |
P |
19 |
99 |
R GUI |
E0,5C |
E0,DC |
KP + |
4E |
CE |
Q |
10 |
90 |
R ALT |
E0,38 |
E0,B8 |
KP EN |
E0,1C |
E0,9C |
R |
13 |
93 |
APPS |
E0,5D |
E0,DD |
KP . |
53 |
D3 |
S |
1F |
9F |
ENTER |
1C |
9C |
KP 0 |
52 |
D2 |
T |
14 |
94 |
ESC |
01 |
81 |
KP 1 |
4F |
CF |
U |
16 |
96 |
F1 |
3B |
BB |
KP 2 |
50 |
D0 |
V |
2F |
AF |
F2 |
3C |
BC |
KP 3 |
51 |
D1 |
W |
11 |
91 |
F3 |
3D |
BD |
KP 4 |
4B |
CB |
X |
2D |
AD |
F4 |
3E |
BE |
KP 5 |
4C |
CC |
Y |
15 |
95 |
F5 |
3F |
BF |
KP 6 |
4D |
CD |
Z |
2C |
AC |
F6 |
40 |
C0 |
KP 7 |
47 |
C7 |
0 |
0B |
8B |
F7 |
41 |
C1 |
KP 8 |
48 |
C8 |
1 |
02 |
82 |
F8 |
42 |
C2 |
KP 9 |
49 |
C9 |
2 |
03 |
83 |
F9 |
43 |
C3 |
] |
1B |
9B |
3 |
04 |
84 |
F10 |
44 |
C4 |
; |
27 |
A7 |
4 |
05 |
85 |
F11 |
57 |
D7 |
' |
28 |
A8 |
5 |
06 |
86 |
F12 |
58 |
D8 |
, |
33 |
B3 |
6 |
07 |
87 |
PRNT |
E0,2A, |
E0,B7, |
. |
34 |
B4 |
7 |
08 |
88 |
SCROLL |
46 |
C6 |
/ |
35 |
B5 |
8 |
09 |
89 |
PAUSE |
E1,1D,45 |
-NONE- |
|
|
|
ACPI Scan Codes:
Key |
Make Code |
Break Code |
Power |
E0, 5E |
E0, DE |
Sleep |
E0, 5F |
E0, DF |
Wake |
E0, 63 |
E0, E3 |
Windows Multimedia Scan Codes:
Key |
Make Code |
Break Code |
Next Track |
E0, 19 |
E0, 99 |
Previous Track |
E0, 10 |
E0, 90 |
Stop |
E0, 24 |
E0, A4 |
Play/Pause |
E0, 22 |
E0, A2 |
Mute |
E0, 20 |
E0, A0 |
Volume Up |
E0, 30 |
E0, B0 |
Volume Down |
E0, 2E |
E0, AE |
Media Select |
E0, 6D |
E0, ED |
|
E0, 6C |
E0, EC |
Calculator |
E0, 21 |
E0, A1 |
My Computer |
E0, 6B |
E0, EB |
WWW Search |
E0, 65 |
E0, E5 |
WWW Home |
E0, 32 |
E0, B2 |
WWW Back |
E0, 6A |
E0, EA |
WWW Forward |
E0, 69 |
E0, E9 |
WWW Stop |
E0, 68 |
E0, E8 |
WWW Refresh |
E0, 67 |
E0, E7 |
WWW Favorites |
E0, 66 |
E0, E6 |
2.在键盘中存在着一个Intel 8048(键盘编码器)的芯片监控输入
寄存器
从8048种获取数据
t_8 scan_code =in_byte(KB_DATA)// KB_DATA=0x60
in_byte:
mov edx,[esp + 4] ; port
xor eax,eax
in al, dx //其实就是in al,0x60
nop ;一点延迟
nop
ret
二、程序B处理键盘输入打印出来
1.按键的产生
当按键和保持按下时产生Make Code,当弹起产生Break Code
8048检测到按键然后把按键信息发送给8042
8042将按键信息转换成Scan code set1扫描码,把他存入自己的缓冲区中,缓冲区不清空将不再接受8048
8042告诉8259A产生中断IRQ1
2.增加的代码
[1]Kernel/main.c的tinix_main函数
[2]\kernel\keyboard.cinit_keyboard函数
/*======================================================================*
init_keyboard
*======================================================================*/
PUBLIC void init_keyboard()
{
kb_in.count= 0;
kb_in.p_head= kb_in.p_tail = kb_in.buf;
put_irq_handler(KEYBOARD_IRQ, keyboard_handler); /* 设定键盘中断处理程序 */ // KEYBOARD_IRQ=0x1
enable_irq(KEYBOARD_IRQ); /* 开键盘中断 */
}
[4]
\kernel\i8259.c
PUBLIC void put_irq_handler(int irq, t_pf_irq_handlerhandler)
{
disable_irq(irq); // disable_irq(1)
irq_table[irq]= handler; irq_table[1] // extern t_pf_irq_handler irq_table[];//typedef void (*t_pf_irq_handler) (int irq)
}
[5]kernel\kernel.asm
%macro hwint_master 1
call save
in al, INT_M_CTLMASK ; ┓
or al, (1 << %1) ; ┣屏蔽当前中断
out INT_M_CTLMASK, al ; ┛
mov al, EOI ;┓置EOI位
out INT_M_CTL, al ;┛
sti ; CPU在响应中断的过程中会自动关中断,这句之后就允许响应新的中断
push %1 ;┓
call [irq_table + 4 *%1] ; ┣中断处理程序
pop ecx ;┛
cli
in al, INT_M_CTLMASK ; ┓
and al, ~(1 << %1) ; ┣恢复接受当前中断
out INT_M_CTLMASK, al ; ┛
ret
%endmacro
这样每次当按键键盘触发8259A产生IRQ1中断都会调用irq_table[1],也就是函数keyboard_handler
[6] keyboard_handler这个函数每次都把获得的Break Code和Make Code放到一个链表里面
\kernel\keyboard.c
PUBLIC void keyboard_handler(int irq)
{
t_8scan_code = in_byte(KB_DATA); //读取缓冲区内容
if(kb_in.count < KB_IN_BYTES)// KB_IN_BYTES=32,链表最大长度32
{
*(kb_in.p_head)= scan_code;
kb_in.p_head++;
if(kb_in.p_head == kb_in.buf + KB_IN_BYTES) {
kb_in.p_head= kb_in.buf;
}
kb_in.count++;
}
}
链表结构\include\keyboard.h
typedef struct s_kb {
char* p_head; /*指向缓冲区中下一个空闲位置 */
char* p_tail; /*指向键盘任务应处理的字节 */
int count; /* 缓冲区中共有多少字节 */
char buf[KB_IN_BYTES]; /* 缓冲区 */
}KB_INPUT;
[1] kernel\global.c中有,多了一个进程执行函数task_tty
PUBLIC TASK task_table[NR_TASKS]= {{task_tty, STACK_SIZE_TTY, "tty"},
{TestA,STACK_SIZE_TESTA, "TestA"},
{TestB,STACK_SIZE_TESTB, "TestB"},
{TestC,STACK_SIZE_TESTC, "TestC"}};
[2] \kernel\main.c中有,这样每次这个进程切换到tty进程都会执行函数task_tty
[3] task_tty进程负责调用keyboard_read()
kernel\tty.c
PUBLIC void task_tty()
{
while(1) {/* forever. yes, forever, there's something which is some kind offorever... */
keyboard_read();
}
}
[4]我们要关注的重点函数,从键盘缓冲区中读取内容
\kernel\keyboard.c
/*======================================================================*
keyboard_read
*======================================================================*/
PUBLIC voidkeyboard_read()
{
t_8 scan_code;
t_bool make; /*TRUE : make */
/*FALSE: break */
t_32 key = 0;/* 用一个整型来表示一个键。 */
/*比如,如果 Home 被按下,则 key 值将为定义在 keyboard.h 中的 'HOME'。*/
t_32* keyrow; /*指向 keymap[] 的某一行 */
if(kb_in.count> 0)
{
code_with_E0= FALSE;
scan_code= get_byte_from_kb_buf();
/*下面开始解析扫描码 */
if(scan_code == 0xE1) //解析PAUSE键,此键只有按下6个0xE1,0x1D,0x45,0xE1,0x9D,0xC5
{
inti;
t_8pausebreak_scan_code[] = {0xE1, 0x1D, 0x45, 0xE1, 0x9D, 0xC5};
t_boolis_pausebreak = TRUE;
for(i=1;i<6;i++){
if(get_byte_from_kb_buf() != pausebreak_scan_code[i]) {
is_pausebreak= FALSE;
break;
}
}
if(is_pausebreak) {
key= PAUSEBREAK;
}
}
elseif (scan_code == 0xE0)
{
scan_code= get_byte_from_kb_buf();
/*PrintScreen 被按下 */
if(scan_code == 0x2A)
{
if(get_byte_from_kb_buf() == 0xE0)
{
if(get_byte_from_kb_buf() == 0x37)
{
key= PRINTSCREEN;
make= TRUE;
}
}
}
/*PrintScreen 被释放 */
if(scan_code == 0xB7)
{
if(get_byte_from_kb_buf() == 0xE0)
{
if(get_byte_from_kb_buf() == 0xAA)
{
key= PRINTSCREEN;
make= FALSE;
}
}
}
/*不是 PrintScreen。此时 scan_code 为 0xE0 紧跟的那个值。 */
if(key == 0) {
code_with_E0= TRUE;
}
}
if((key != PAUSEBREAK) && (key != PRINTSCREEN))
{
/*首先判断Make Code 还是 Break Code */
//#defineFLAG_BREAK 0x0080
//A按下1E&0x80=0x00
//A释放9E&0x80=0x80
make= (scan_code & FLAG_BREAK ? FALSE : TRUE);
//0x1E&0x7F=0x1E
//MAP_COLS=3
/* 先定位到 keymap 中的行 */
keyrow= &keymap[(scan_code & 0x7F) * MAP_COLS];
column= 0;
if(shift_l || shift_r) //如果按下左边的shift和右边的shift就是第二行
{
column= 1;
}
if(code_with_E0) //其它键入ctrl和alt等,就是第二行
{
column= 2;
}
key= keyrow[column];
switch(key){
caseSHIFT_L:
shift_l = make;
break;
caseSHIFT_R:
shift_r = make;
break;
caseCTRL_L:
ctrl_l = make;
break;
caseCTRL_R:
ctrl_r = make;
break;
caseALT_L:
alt_l = make;
break;
caseALT_R:
alt_l = make;
break;
default:
break;
}
}
if(make){/* 忽略 Break Code */
key|= shift_l ? FLAG_SHIFT_L : 0;
key|= shift_r ? FLAG_SHIFT_R : 0;
key|= ctrl_l ? FLAG_CTRL_L : 0;
key|= ctrl_r ? FLAG_CTRL_R : 0;
key|= alt_l ? FLAG_ALT_L : 0;
key|= alt_r ? FLAG_ALT_R : 0;
in_process(key);
}
}
}
/*======================================================================*
get_byte_from_kb_buf
*======================================================================*/
PRIVATE t_8get_byte_from_kb_buf() /* 从键盘缓冲区中读取下一个字节 */
{
t_8 scan_code;
while(kb_in.count <= 0) {} /* 等待下一个字节到来 */
disable_int();
scan_code= *(kb_in.p_tail);
kb_in.p_tail++;
if(kb_in.p_tail == kb_in.buf + KB_IN_BYTES) {
kb_in.p_tail= kb_in.buf;
}
kb_in.count--;
enable_int();
returnscan_code;
}
[5] 最终键盘处理
Kernel
PUBLIC voidin_process(t_32 key)
{
char output[2] = {'\0', '\0'};
if(!(key & FLAG_EXT)) {
output[0]= key & 0xFF;
disp_str(output);
}
}
三、奇怪,我按小写键盘不能可是于渊已经处理了
可能他的键盘跟我们有点区别吧,还是粗心,咳咳,我改成如下
Keymap.h文件
/*0x47 - Home */ PAD_HOME, '7', HOME,
/*0x48 - CurUp */ PAD_UP, '8', UP,
/*0x49 - PgUp */ PAD_PAGEUP, '9', PAGEUP,
/*0x4A - '-' */ PAD_MINUS, '-', 0,
/*0x4B - Left */ PAD_LEFT, '4', LEFT,
/*0x4C - MID */ PAD_MID, '5', 0,
/*0x4D - Right */ PAD_RIGHT, '6', RIGHT,
/*0x4E - '+' */ PAD_PLUS, '+', 0,
/*0x4F - End */ PAD_END, '1', END,
/*0x50 - Down */ PAD_DOWN, '2', DOWN,
/*0x51 - PgDown */ PAD_PAGEDOWN, '3', PAGEDOWN,
/*0x52 - Insert */ PAD_INS, '0', INSERT,
/*0x53 - Delete */ PAD_DOT, '.', DELETE,
改成
/* 0x47 - Home */ '7', PAD_HOME, HOME,
/* 0x48 - CurUp */ '8', PAD_UP, UP,
/* 0x49 - PgUp */ '9', PAD_PAGEUP, PAGEUP,
/* 0x4A - '-' */ '-', PAD_MINUS, 0,
/* 0x4B - Left */ '4', PAD_LEFT, LEFT,
/* 0x4C - MID */ '5', PAD_MID, 0,
/* 0x4D - Right */ '6', PAD_RIGHT, RIGHT,
/* 0x4E - '+' */ '+', PAD_PLUS, 0,
/* 0x4F - End */ '1', PAD_END, END,
/* 0x50 - Down */ '2', PAD_DOWN, DOWN,
/* 0x51 - PgDown */ '3', PAD_PAGEDOWN, PAGEDOWN,
/* 0x52 - Insert */ '0', PAD_INS, INSERT,
/* 0x53 - Delete */ '.', PAD_DOT, DELETE,
Ok可以正常按了
附录
查看完整的三套扫描码
http://www.computer-engineering.org/chinese.pdf 第52页
或者
https://www.marjorie.de/ps2/scancode-set1.htm