【轉】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/ (英文)