一、键盘的基础知识
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页
或者