本文轉自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/ (英文)