第七章 输入输出系统之键盘

一、键盘的基础知识

1.键盘种类

XT 很悠久了

AT

第七章 输入输出系统之键盘_第1张图片

PS/2

第七章 输入输出系统之键盘_第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 
SCRN

E0,2A, 
E0,37 

 E0,B7, 
E0,AA

.

34

B4

7

08

88

SCROLL

46

C6

/

35

B5

8

09

89

PAUSE

E1,1D,45 
E1,9D,C5

-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

E-Mail

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.ctinix_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 CodeMake 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

你可能感兴趣的:(自己动手写操作系统)