Intel 8042鍵盤控制器詳細介紹

本文轉自http://shanzy.bokee.com/834368.html

ps/2 鍵盤硬件概述

    對於驅動來說,和鍵盤相關的最重要的硬件是兩個芯片。一個是 intel 8042 芯片,位於主板上,CPU 通過 IO 端口直接和這個芯片通信,獲得按鍵的掃描碼或者發送各種鍵盤命令。另一個是 intel 8048 芯片或者其兼容芯片,位於鍵盤中,這個芯片主要作用是從鍵盤的硬件中得到被按的鍵所產生的掃描碼,與 i8042 通信,控制鍵盤本身。

    當鍵盤上有鍵被按下時,i8048 直接獲得鍵盤硬件產生的掃描碼。i8048 也負責鍵盤本身的控制,比如點亮 LED 指示燈,熄滅 LED 指示燈。i8048 通過 ps/2 口和 i8042 通信,把得到的掃描碼傳送給 i8042。CPU 通過讀寫端口,可以直接把 i8042 中的數據讀入到 CPU 的寄存器中,或者把 CPU 寄存器中的數據寫入 i8042 中。ps/2 口一共有 6 個引腳,可以拔下 ps/2 插頭看一下,這 6 個引腳分別爲,時鐘,數據,電源地,電源+5V,還有2個引腳沒有被用到。由於只有一個引腳傳輸數據,所以 ps/2 口上的數據傳輸是串行的。

    下面幾幅圖是一個鍵盤的內部,可以看到用來產生掃描碼的按鍵矩陣( Key Martix ),可以看到鍵盤中的芯片(這裏不是i8048,是一個兼容的其他型號的芯片)。 

詳細的圖見:http://jiurl.nease.net/document/KbdDriver/JiurlKbd1.htm


i8042 鍵盤控制器

    鍵盤驅動直接讀寫 i8042 芯片,通過 i8042 間接的向鍵盤中的 i8048 發命令。所以對於驅動來說,直接發生聯繫的只有 i8042 ,因此我們只介紹 i8042 ,不介紹 i8048。

    象 i8042,i8048 這樣的芯片,本身就是一個小的處理器,它的內部有自己的處理器,有自己的 Ram,有自己的寄存器,等等。

    i8042 有 4 個 8 bits 的寄存器,他們是 Status Register(狀態寄存器),Output Buffer(輸出緩衝器),Input Buffer(輸入緩衝器),Control Register(控制寄存器)。使用兩個 IO 端口,60h 和 64h。


Status Register(狀態寄存器)

狀態寄存器是一個8位只讀寄存器,任何時刻均可被cpu讀取。其各位定義如下

Bit7: PARITY-EVEN(P_E): 從鍵盤獲得的數據奇偶校驗錯誤
Bit6: RCV-TMOUT(R_T): 接收超時,置1
Bit5: TRANS_TMOUT(T_T): 發送超時,置1
Bit4: KYBD_INH(K_I): 爲1,鍵盤沒有被禁止。爲0,鍵盤被禁止。
Bit3: CMD_DATA(C_D): 爲1,輸入緩衝器中的內容爲命令,爲0,輸入緩衝器中的內容爲數據。
Bit2: SYS_FLAG(S_F): 系統標誌,加電啓動置0,自檢通過後置1
Bit1: INPUT_BUF_FULL(I_B_F): 輸入緩衝器滿置1,i8042 取走後置0
BitO: OUT_BUF_FULL(O_B_F): 輸出緩衝器滿置1,CPU讀取後置0


Output Buffer(輸出緩衝器)

輸出緩衝器是一個8位只讀寄存器。驅動從這個寄存器中讀取數據。這些數據包括,掃描碼,發往 i8042 命令的響應,間接的發往 i8048 命令的響應。


Input Buffer(輸入緩衝器)

輸入緩衝器是一個8位只寫寄存器。緩衝驅動發來的內容。這些內容包括,發往 i8042 的命令,通過 i8042 間接發往 i8048 的命令,以及作爲命令參數的數據。


Control Register(控制寄存器)

也被稱作 Controller Command Byte (控制器命令字節)。其各位定義如下

Bit7: 保留,應該爲0
Bit6: 將第二套掃描碼翻譯爲第一套
Bit5: 置1,禁止鼠標
Bit4: 置1,禁止鍵盤
Bit3: 置1,忽略狀態寄存器中的 Bit4
Bit2: 設置狀態寄存器中的 Bit2
Bit1: 置1,enable 鼠標中斷
BitO: 置1,enable 鍵盤中斷

2個端口 0x60,0x64

驅動中把 0x60 叫數據端口
驅動中把 0x64 叫命令端口

1.5 命令

驅動可以直接給 i8042 發命令,可以通過 i8042 間接給 i8048 發命令。命令這部分內容直接來自 < 參考資料 [1] >。


1.5.1 發給i8042的命令

驅動對鍵盤控制器發送命令是通過寫端口64h實現的,共有12條命令,分別爲

20h 
準備讀取8042芯片的Command Byte;其行爲是將當前8042 Command Byte的內容放置於Output Register中,下一個從60H端口的讀操作將會將其讀取出來。

60h 
準備寫入8042芯片的Command Byte;下一個通過60h寫入的字節將會被放入Command Byte。

A4h 
測試一下鍵盤密碼是否被設置;測試結果放置在Output Register,然後可以通過60h讀取出來。測試結果可以有兩種值:FAh=密碼被設置;F1h=沒有密碼。

A5h 
設置鍵盤密碼。其結果被按照順序通過60h端口一個一個被放置在Input Register中。密碼的最後是一個空字節(內容爲0)。

A6h
讓密碼生效。在發佈這個命令之前,必須首先使用A5h命令設置密碼。

AAh
自檢。診斷結果放置在Output Register中,可以通過60h讀取。55h=OK。

ADh
禁止鍵盤接口。Command Byte的bit-4被設置。當此命令被髮布後,Keyboard將被禁止發送數據到Output Register。

AEh
打開鍵盤接口。Command Byte的bit-4被清除。當此命令被髮布後,Keyboard將被允許發送數據到Output Register。

C0h
準備讀取Input Port。Input Port的內容被放置於Output Register中,隨後可以通過60h端口讀取。

D0h
準備讀取Outport端口。結果被放在Output Register中,隨後通過60h端口讀取出來。

D1h
準備寫Output端口。隨後通過60h端口寫入的字節,會被放置在Output Port中。


D2h
準備寫數據到Output Register中。隨後通過60h寫入到Input Register的字節會被放入到Output Register中,此功能被用來模擬來自於Keyboard發送的數據。如果中斷被允許,則會觸發一箇中斷。


1.5.2 發給8048的命令

共有10條命令,分別爲

EDh
設置LED。Keyboard收到此命令後,一個LED設置會話開始。Keyboard首先回復一個ACK(FAh),然後等待從60h端口寫入的LED設置字節,如果等到一個,則再次回覆一個ACK,然後根據此字節設置LED。然後接着等待。。。直到等到一個非LED設置字節(高位被設置),此時LED設置會話結束。

EEh
診斷Echo。此命令純粹爲了檢測Keyboard是否正常,如果正常,當Keyboard收到此命令後,將會回覆一個EEh字節。

F0h
選擇Scan code set。Keyboard系統共可能有3個Scan code set。當Keyboard收到此命令後,將回復一個ACK,然後等待一個來自於60h端口的Scan code set代碼。系統必須在此命令之後發送給Keyboard一個Scan code set代碼。當Keyboard收到此代碼後,將再次回覆一個ACK,然後將Scan code set設置爲收到的Scan code set代碼所要求的。

F2h
讀取Keyboard ID。由於8042芯片後不僅僅能夠接Keyboard。此命令是爲了讀取8042後所接的設備ID。設備ID爲2個字節,Keyboard ID爲83ABh。當鍵盤收到此命令後,會首先回復一個ACK,然後,將2字節的Keyboard ID一個一個回覆回去。 

F3h
設置Typematic Rate/Delay。當Keyboard收到此命令後,將回復一個ACK。然後等待來自於60h的設置字節。一旦收到,將回復一個ACK,然後將Keyboard Rate/Delay設置爲相應的值。

F4h
清理鍵盤的Output Buffer。一旦Keyboard收到此命令,將會將Output buffer清空,然後回覆一個ACK。然後繼續接受Keyboard的擊鍵。

F5h
設置默認狀態(w/Disable)。一旦Keyboard收到此命令,將會將Keyboard完全初始化成默認狀態。之前所有對它的設置都將失效——Output buffer被清空,Typematic Rate/Delay被設置成默認值。然後回覆一個ACK,接着等待下一個命令。需要注意的是,這個命令被執行後,鍵盤的擊鍵接受是禁止的。如果想讓鍵盤接受擊鍵輸入,必須Enable Keyboard。

F6h
設置默認狀態。和F5命令唯一不同的是,當此命令被執行之後,鍵盤的擊鍵接收是允許的。 

FEh
Resend。如果Keyboard收到此命令,則必須將剛纔發送到8042 Output Register中的數據重新發送一遍。當系統檢測到一個來自於Keyboard的錯誤之後,可以使用自命令讓Keyboard重新發送剛纔發送的字節。

FFh
Reset Keyboard。如果Keyboard收到此命令,則首先回復一個ACK,然後啓動自身的Reset程序,並進行自身基本正確性檢測(BAT-Basic Assurance Test)。等這一切結束之後,將返回給系統一個單字節的結束碼(AAh=Success, FCh=Failed),並將鍵盤的Scan code set設置爲2。


1.5.3 讀到的數據


00h/FFh
當擊鍵或釋放鍵時檢測到錯誤時,則在Output Bufer後放入此字節,如果Output Buffer已滿,則會將Output Buffer的最後一個字節替代爲此字節。使用Scan code set 1時使用00h,Scan code 2和Scan Code 3使用FFh。

AAh
BAT完成代碼。如果鍵盤檢測成功,則會將此字節發送到8042 Output Register中。

EEh
Echo響應。Keyboard使用EEh響應從60h發來的Echo請求。

F0h
在Scan code set 2和Scan code set 3中,被用作Break Code的前綴。

FAh
ACK。當Keyboard任何時候收到一個來自於60h端口的合法命令或合法數據之後,都回復一個FAh。

FCh
BAT失敗代碼。如果鍵盤檢測失敗,則會將此字節發送到8042 Output Register中。

FEh
Resend。當Keyboard任何時候收到一個來自於60h端口的非法命令或非法數據之後,或者數據的奇偶交驗錯誤,都回復一個FEh,要求系統重新發送相關命令或數據。

83ABh
當鍵盤收到一個來自於60h的F2h命令之後,會依次回覆83h,ABh。83AB是鍵盤的ID。

Scan code
除了上述那些特殊字節以外,剩下的都是Scan code。


1.6 端口操作

    首先介紹一下端口的讀寫操作,驅動中使用函數 READ_PORT_UCHAR 進行讀操作,READ_PORT_UCHAR 中使用CPU讀端口指令,in。驅動中使用函數 WRITE_PORT_UCHAR 進行寫操作,WRITE_PORT_UCHAR 中使用CPU寫端口指令,out。

1.6.1 讀取狀態寄存器

讀取狀態寄存器的方法,對64h端口進行讀操作。

1.6.2 讀數據

需要讀取的數據有,i8042從i8048得到的按鍵的掃描碼,i8042命令的ACK,i8042從i8048得到的i8048命令的ACK,需要命令重發的RESEND,一些需要返回結果的命令得到的結果。

當有數據需要被驅動讀走的時候,數據被放入輸出緩衝器,同時將狀態寄存器的bit0(OUTPUT_BUFFER_FULL)置1,引發鍵盤中斷(鍵盤中斷的IRQ爲1)。由於鍵盤中斷,引起由鍵盤驅動提供的鍵盤中斷服務例程被執行。在鍵盤中斷服務例程中,驅動會從i8042讀走數據。一旦數據讀取完成,狀態寄存器的bit0會被清0。

讀數據的方法,首先,讀取狀態寄存器,判斷bit0,狀態寄存器bit0爲1,說明輸出緩衝器中有數據。保證狀態寄存器bit0爲1,然後對60h端口進行讀操作,讀取數據。

這裏我們要談一點很有用的題外話,前面提到的 IRQ,是 Interrupt Request line,中斷請求線,是一個硬件線,它和中斷向量是不同的。中斷向量是用來在中斷描述符表(IDT)中查找中斷服務例程的那個序號。鍵盤的 IRQ 是1,鍵盤中斷服務例程對應的中斷向量可不是1。這點要弄清楚。

1.6.3 向i8042發命令

當命令被髮往i8042的時候,命令被放入輸入緩衝器,同時引起狀態寄存器的 Bit1 置1,表示輸入緩衝器滿,同時引起狀態寄存器的 Bit2 置1,表示寫入輸入緩衝器的是一個命令。

向i8042發命令的方法,首先,讀取狀態寄存器,判斷bit1,狀態寄存器bit1爲0,說明輸入緩衝器爲空,可以寫入。保證狀態寄存器bit1爲0,然後對64h端口進行寫操作,寫入命令。


1.6.4 間接向i8048發命令

向i8042發這些命令,i8042會轉發i8048,命令被放入輸入緩衝器,同時引起狀態寄存器的Bit1 置1,表示輸入緩衝器滿,同時引起狀態寄存器的 Bit2 置1,表示寫入輸入緩衝器的是一個命令。這裏我們要注意,向i8048發命令,是通過寫60h端口,而後面發命令的參數,也是寫60h端口。i8042如何判斷輸入緩衝器中的內容是命令還是參數呢,我們在後面發命令的參數中一起討論。

向i8048發命令的方法,首先,讀取狀態寄存器,判斷bit1,狀態寄存器bit1爲0,說明輸入緩衝器爲空,可以寫入。保證狀態寄存器bit1爲0,然後對60h端口進行寫操作,寫入命令。

1.6.5 發命令的參數

某些命令需要參數,我們在發送命令之後,發送它的參數,參數被放入輸入緩衝器,同時引起狀態寄存器的Bit1 置1,表示輸入緩衝器滿。這裏我們要注意,向i8048發命令,是通過寫60h端口,發命令的參數,也是寫60h端口。i8042如何判斷輸入緩衝器中的內容是命令還是參數呢。i8042是這樣判斷的,如果當前狀態寄存器的Bit3 爲1,表示之前已經寫入了一個命令,那麼現在通過寫60h端口放入輸入緩衝器中的內容,就被當做之前命令的參數,並且引起狀態寄存器的 Bit3 置0。如果當前狀態寄存器的 Bit3 爲0,表示之前沒有寫入命令,那麼現在通過寫60h端口放入輸入緩衝器中的內容,就被當做一個間接發往i8048的命令,並且引起狀態寄存器的 Bit3 置1。

向i8048發參數的方法,首先,讀取狀態寄存器,判斷bit1,狀態寄存器bit1爲0,說明輸入緩衝器爲空,可以寫入。保證狀態寄存器bit1爲0,然後對60h端口進行寫操作,寫入參數。



"1 ps/2 鍵盤的硬件" 主要參考下面的資料,關於 ps/2 鍵盤硬件更多的內容也請參考下面的資料

[1] http://pagoda-ooos.51.net/os_book/driver/driver-keyboard_2.htm (中文)
[2] http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/PS2.pdf (中文)
[3] http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/ (英文)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章