鍵盤如何工作的前文曾經說過,當時是以 Linux 0.11 為基礎講的但不系統,本文以 xv6 的鍵盤驅動程序為例來系統地講述鍵盤是如何工作的。關于驅動程序前文磁盤那一篇說過了,它就是硬件物理接口的封裝,所以了解鍵盤驅動程序,同樣的還是先來了解鍵盤的一些物理接口。
與鍵盤相關的芯片有兩個,一個是鍵盤編碼器 i8048,另一個是鍵盤控制器 i8042,分別來看。
鍵盤編碼器位于鍵盤,它的作用主要是監測鍵的按下和彈起,然后將兩種狀態編碼,發送給鍵盤控制器。
上述說的碼叫做鍵盤掃描碼,編碼方式一共有三種,相應的也就有三套鍵盤掃描碼,各套鍵盤掃描碼具體怎么編碼的就不說了,見后面的鏈接?,F今的鍵盤大多數都是用的第二套鍵盤掃描碼,但也不排除使用第一套和第三套的,所以為了兼容,鍵盤控制器會統統地轉換為第一套掃描碼。當然這是默認的情況,具體使用哪一套掃描碼,控制器是否轉化,還是要看硬件是否支持與具體怎么設置,有興趣的詳見文末鏈接。
因此第一套鍵盤掃描碼還是得說道說道,一個鍵有按下就會有彈起,所以每個鍵會有兩個狀態,即每個鍵將會對應兩個掃描碼,鍵被按下時的編碼叫做通碼(),彈起時的編碼叫做斷碼()。
大部分鍵的通碼和斷碼都是 8 位 1 字節,但有些操作控制鍵如 Ctrl、Alt,附加鍵如Insert,小鍵盤區如/ ,方向鍵等是 2 字節甚至多個字節。有多個字節的掃描碼通常都是以 開頭。只有 一個鍵是以 開頭。
斷碼與通碼的關系:斷碼通碼。 二進制表示為 ,所以對于斷碼和通碼可以這樣理解,它們由 8 位比特組成,最高位第 7 位表示按鍵狀態,1 表示按下,0 表示彈起。
鍵盤控制器(i8042),不在鍵盤內部,被集成在南橋芯片上。主要接收鍵盤編碼器發來的鍵盤掃描碼,做一些處理(比如第二套掃描碼轉第一套),然后觸發中斷通知 CPU 來讀取掃描碼。
鍵盤控制器有 4 個 8 bits 寄存器,Status Register 和 Control Register,兩者共用一個端口 0x64,讀的時候是狀態寄存器,寫的時候是控制寄存器。Input Buffer 和 Output Buffer,兩者共用一個端口 0x60,讀的時候是輸出緩沖器,寫的時候是輸入緩沖器。
bit0:1 表示輸出緩存器滿,CPU 讀取后清零。從編碼器發過來的掃描碼就放在這里。
bit1:1 表示輸入緩存器滿,控制器讀取后清零。
通過寫 0x64 端口來向控制器發送命令,注意是向控制器本身發命令而不是向硬件設備鍵盤發命令,對于鍵盤的控制就是通過控制器來間接控制,所以只需要操作鍵盤就是了。
命令控制器就是將命令字節寫入 0x64 端口,一般命令就是一字節,如果有兩字節,則將第二個字節寫入 0x60 端口。因為要寫 0x60 端口表示的緩存區,所以要先判斷該緩存區是否為空。
比如進入保護模式設置 時,先判斷輸入緩存區是否為空,空的話表示控制器已取走數據,可以繼續進行,否則不空的話循環等待:
inb $0x64,%al # Wait for not busy 等待i8042緩沖區為空
testb $0x2,%al
jnz seta20.1
再向 0x64 端口寫入命令 ,表示準備寫 Output 端口,隨后寫入 0x60 端口的字節將放入 Output 端口。
inb $0x64,%al # Wait for not busy 同上
testb $0x2,%al
jnz seta20.2
movb $0xdf,%al # 0xdf -> port 0x60 向端口0x60寫入0xdf,打開A20
outb %al,$0x60
同樣的先判斷輸入緩存區是否為空,然后寫入命令第二字節 ,這個字節會被送到 Output 端口,這個端口也是一個控制端口,bit2 控制著 的開關,所以如果是命令字節 表示關閉 。
關于鍵盤控制器就說這么多,只講述了與 xv6 相關的部分,其他部分同樣的感興趣的見文末的鏈接。
驅動程序就是硬件物理接口的封裝,鍵盤驅動程序也是如此,它的主要功能就是將讀取掃描碼轉換成計算機所需要的信息,比如說轉換成字符,信號等等。xv6 在這方面實現的比較簡單,只實現了字符轉化,一些功能控制鍵,我們來看看。
首先在 頭文件中定義了端口號,控制鍵如 Ctrl,特殊鍵如 UP,以及最重要的映射表,來看個普通情況下的映射表:
static uchar normalmap[256]={
NO, 0x1B, '1', '2', '3', '4', '5', '6', // 0x00
'7', '8', '9', '0', '-', '=', '\b', '\t',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', // 0x10
'o', 'p', '[', ']', '\n', NO, 'a', 's',
'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', // 0x20
'\'', '`', NO, '\\', 'z', 'x', 'c', 'v',
'b', 'n', 'm', ',', '.', '/', NO, '*', // 0x30
NO, ' ', NO, NO, NO, NO, NO, NO,
NO, NO, NO, NO, NO, NO, NO, '7', // 0x40
'8', '9', '-', '4', '5', '6', '+', '1',
'2', '3', '0', '.', NO, NO, NO, NO, // 0x50
[0x9C] '\n', // KP_Enter
[0xB5] '/', // KP_Div
[0xC8] KEY_UP, [0xD0] KEY_DN,
[0xC9] KEY_PGUP, [0xD1] KEY_PGDN,
[0xCB] KEY_LF, [0xCD] KEY_RT,
[0x97] KEY_HOME, [0xCF] KEY_END,
[0xD2] KEY_INS, [0xD3] KEY_DEL
};
鍵盤掃描碼是一個鍵的代表,但不是我們想要的,我們想要的是這個鍵表示的意義,比如數字鍵 1 的通碼是 , 顯然不是我們想要的,我們想要的是數字 1,所以需要一個映射關系來轉換,將所有鍵的映射關系集合在一起就是上述的映射表。是個大數組,下標就是這個鍵的掃描碼,內容就是所表達的意思。
上述是一般情況,那當然還有非一般的情況,比如有按下 Shift,CapsLock,Ctrl 等控制鍵,當按下這些控制鍵后,其他鍵按下之后表達的意義就不一樣了,所以還需要另外的映射表,這里就不列出來了,太多了,可以直接參考代碼。舉個例子,當按下 Shift 鍵之后再按下數字鍵 1,通碼 則應該映射成 !而不是1。
有了這些了解之后來看 里面的源碼:
int kbdgetc(void)
{
static uint shift; //shift用bit來記錄控制鍵,比如shift,ctrl
static uchar *charcode[4]={
normalmap, shiftmap, ctlmap, ctlmap
}; //映射表
uint st, data, c;
st=inb(KBSTATP);
if((st & KBS_DIB)==0) //輸出緩沖區未滿,沒法用指令in讀取
return -1;
data=inb(KBDATAP); //從輸出緩沖區讀數據
if(data==0xE0){ //通碼以e0開頭的鍵
shift |=E0ESC; //記錄e0
return 0;
} else if(data & 0x80){ //斷碼,表鍵彈起
// Key released
data=(shift & E0ESC ? data : data & 0x7F);
shift &=~(shiftcode[data] | E0ESC);
return 0;
} else if(shift & E0ESC){ //緊接著0xE0后的掃描碼
// Last character was an E0 escape; or with 0x80
data |=0x80;
shift &=~E0ESC;
}
shift |=shiftcode[data]; //記錄控制鍵狀態,如Shift,Ctrl,Alt
shift ^=togglecode[data]; //記錄控制鍵狀態,如CapsLock,NumLock,ScrollLock
c=charcode[shift & (CTL | SHIFT)][data]; //獲取映射表的內容,也就是該鍵表示的意義
if(shift & CAPSLOCK){
if('a' <=c && c <='z')
c +='A' - 'a';
else if('A' <=c && c <='Z')
c +='a' - 'A';
}
return c;
}
這個程序就可以看作極簡的鍵盤驅動程序,也是鍵盤中斷的服務程序的主體,完成鍵盤掃描碼到所需信息的轉化。下面就來仔細分析分析:
前面說過有多張映射表多種映射方式,那怎么知道用哪張?用哪張得看看有沒有相應的控制鍵按下,所以得有個東西來記錄控制鍵的按下與否,這個東西就是變量 ,雖然變量名是 ,但不代表只記錄 Shift 鍵的狀態,記錄的信息有:
#define SHIFT (1<<0)
#define CTL (1<<1)
#define ALT (1<<2)
#define CAPSLOCK (1<<3)
#define NUMLOCK (1<<4)
#define SCROLLLOCK (1<<5)
#define E0ESC (1<<6) //通碼斷碼以E0開頭
從這種定義控制鍵的方式就可以看出使用 來記錄控制鍵的方式應該是使用位運算。
表示一個二維數組,可以看作是映射表的集合,根據 的記錄信息來選擇映射表,后面用到的時候就明白了。
st=inb(KBSTATP);
if((st & KBS_DIB)==0) //輸出緩沖區為空,沒法用指令in讀取
return -1;
data=inb(KBDATAP); //從輸出緩沖區讀數據
這幾句用來讀取鍵盤掃描碼,從鍵盤發過來的掃描碼就放在輸出緩沖區中。要讀取掃描碼首先從狀態寄存器讀取當前狀態到 ,再做與運算取出第 0 位,表示輸出緩沖區的狀態,如果為 0 表示輸出緩沖區寄存器為空,沒法讀取返回 -1。如果為 1 表示輸出緩沖區寄存器已滿有內容,可以讀取,所以接著從端口 0x60 輸出緩沖區讀出掃描碼到 。
if(data==0xE0){ //通碼以e0開頭的鍵
shift |=E0ESC; //記錄e0
return 0;
}
如果這個掃描碼為 0xE0,說明按下的鍵是特殊鍵,掃描碼不止 8 字節,這種情況在 變量中做好標記就可以直接返回了,等待下一個數據的到來再做具體處理
else if(data & 0x80){ //斷碼,表鍵彈起
// Key released
data=(shift & E0ESC ? data : data & 0x7F);
shift &=~(shiftcode[data] | E0ESC);
return 0;
}
為 1 的話,表示第 7 位為 1,說明此數據為斷碼,收到斷碼不需要額外的做什么事,但如果這個斷碼是某個控制鍵的斷碼,則應該將該控制鍵在 里面的記錄信息給清除掉。
所以得知道讀出的 表示哪一個控制鍵,所以有了 映射:
static uchar shiftcode[256]={
[0x1D] CTL,
[0x2A] SHIFT,
[0x36] SHIFT,
[0x38] ALT,
[0x9D] CTL,
[0xB8] ALT,
};
私以為這個定義方式很不對頭啊,實在不太明白一些控制鍵用通碼,一些用斷碼,這也就導致了那條使用了條件表達式的 賦值語句必須存在,因為 中映射 Shift 鍵的時候沒有用斷碼,所以得轉換成通碼。私以為這么映射很混亂,導致后面 中有些語句意義也不太明確,要么就應該將映射關系給補全,然后可以省掉那句 賦值語句,使后面的語句書寫變得更明確一點。當然這不是重點,能理解這過程意思就行,總而言之如果 是個斷碼,不需要干其他的事,如果是控制鍵的斷碼,將記錄在 中的控制鍵信息給清除掉就行。
else if(shift & E0ESC){
// Last character was an E0 escape; or with 0x80
data |=0x80;
shift &=~E0ESC;
}
這種情況對應的是緊接著 后面的鍵盤掃描碼,鍵盤掃描碼有多個字節的,都是成對存在也就是 這種形式,每次收到 ,都要將 鍵中記錄的 信息給清除掉。至于前面還有一句 還是與 xv6 設計的映射表有關,鍵盤上有著許多相同意義的鍵,xv6 將一些鍵的映射關系用斷碼來映射,比如除號鍵 /。
shift |=shiftcode[data]; //記錄控制鍵狀態,如Shift,Ctrl,Alt
shift ^=togglecode[data]; //記錄控制鍵狀態,如CapsLock,NumLock,ScrollLock
這兩句來記錄控制鍵的狀態,分了兩種情況,兩種運算方式。應該能看出它們之間的區別吧,實現組合鍵的時候,Shift,Ctrl,Alt 需要按住不放才能生效,彈起后不再生效。而像 CapsLock 之類的控制鍵,只需要按下一次即可,即便彈起之后同樣生效。所以一個使用或運算,一個使用異或運算,自己模擬一下過程應該很容易明白。
c=charcode[shift & (CTL | SHIFT)][data]; //獲取映射表的內容,也就是該鍵表示的意義
if(shift & CAPSLOCK){ //如果有 CapsLock 存在
if('a' <=c && c <='z') //小寫變大寫
c +='A' - 'a';
else if('A' <=c && c <='Z') //大寫變小寫
c +='a' - 'A';
}
根據 中記錄的控制鍵信息,來選取映射表,根據 去獲取該鍵盤掃描碼所表示的意義。因為 CapsLock 和 Shift 鍵的功能有相同之處,所以如果 c 就是個普通 26 個英文字母字符的話,需要額外判斷大小寫。
關于 xv6 的鍵盤驅動程序差不多就是這么多,當然還有一些功能沒說,比如 Ctrl 組合鍵功能,鍵盤的緩沖區等等,這在另一個文件里面涉及到了另外的知識,咱們放在后面再詳述吧。
在此再聊聊常見的一些問題,在第一篇鍵盤里也說過,再來看看:
使用組合鍵時需要先按下控制鍵。鍵盤的中斷程序為這些控制鍵設置了標識()。先按下控制鍵,程序為控制鍵設置好按下狀態,再處理后到來的鍵時會檢查這些標識,是否有控制鍵按下,以便做出不同的操作。
組合鍵按鍵時有順序,但彈起無順序要求。由上面的鍵處理程序可知,只有通碼的鍵處理程序在做事,而斷碼的鍵處理程序除了控制鍵的標識位需要復位之外其他鍵都是直接返回的。所以使用鍵盤控制輸入時重要的是按鍵,而不是鍵彈起,所以只要按鍵對了,怎樣彈起并不重要。
一直按著某個鍵時會一直觸發鍵盤中斷,若是普通的字符鍵,電腦屏幕可能會出現一直打印某個字符的現象。若是一些控制鍵,則驅動程序可能會不停地將這個鍵設為按下狀態。當然,驅動程序是否記錄上次按鍵取決于具體實現,大多是不記錄的,xv6 也是如此,觸發一次鍵盤中斷就處理一個掃描碼。
最后總結一番,鍵盤驅動程序同樣的是封裝鍵盤的物理接口使用,比如讀取狀態,讀取掃描碼等等。鍵盤本身使用的是鍵盤掃描碼,每個鍵都有自己的鍵盤掃描碼,一個是通碼表按下,一個表斷碼表彈起。這個鍵盤掃描碼只是唯一標識一個鍵,可以將鍵盤掃描碼看作是一個鍵的物理意義,但這不是我們想要的,我們想要的是這個鍵代表的邏輯意義。所以物理意義和邏輯之間需要一個轉化,這就是映射表存在的意義。
鍵盤上有各種各樣的鍵,還能組合使用,它們所代表的意義、具有的功能很多也很雜,xv6 只實現了其中一部分,但也足以讓我們明白其中的本質。不論要通過按鍵實現什么功能,還是就只是簡單的使用一個鍵所代表的邏輯意義,都是要先獲取鍵的掃描碼,再通過映射表轉化成所需要的信息,后續什么功能再在其上做文章。
好了本文就到這里結束,有什么錯誤還請批評指正,也歡迎大家來同我討論交流一起學習進步。
參考:
https://www.win.tue.nl/~aeb/linux/kbd/scancodes-11.html
https://wiki.osdev.org/"8042"_PS/2_Controller#Command_Register
盤驅動怎么重新安裝?鍵盤雖然具有著非常重要功能,但也偶爾會出現問題的情況。比如說原本用的還好好的,突然間就失靈了,相信大家遇在遇到該問題時一定非常的惱火吧。這是怎么回事呢?造成該鍵盤失靈的原因有很多,比如說因為驅動程序受損的原因,那遇到這樣的問題怎么辦呢。今天小編就為大家帶來了鍵盤驅動怎么重新安裝的相關內容,一起來看看吧。#win10使用技巧#
鍵盤驅動怎么重新安裝的方法:
1.首先,進入“設備管理器”;
2.如果在鍵盤設備旁邊出現感嘆號時,那就是鍵盤驅動出現問題了;
3.然后,快捷鍵”Win+r“進入“運行“,并輸入“regedit “;
4.在找到以下這個位置“HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class \{4D36E96B-E325-11CE-BFC1-08002BE10318} “;
5.然后右側找到"UpperFilters"項把它給刪除掉,然后在“設備管理器”里卸載“鍵盤”設備,然后重新“掃描檢測硬件改動”,接著會找到鍵盤硬件但是旁邊還是有感嘆號不用著急,查看下設備狀態變成了“代碼10:該設備無法啟動”,如若不是重啟電腦再次查看,接著還是在注冊表剛剛的位置新建我們剛剛刪除的項新建添加“多字符串值UpperFilters"項,修改內容為kbdclass,重啟設備就會發現可以使用鍵盤了;
6.在操作期間電腦的鍵盤是使用不了的,我們可以使用電腦自帶的屏幕見鍵盤,先打開“控制面板“然后“輕松訪問“最后“啟動屏幕鍵盤“就能打開電腦自帶的屏幕鍵盤了。
以上就是小編今天為大家帶來的鍵盤驅動怎么重新安裝以及win10鍵盤驅動重新安裝的方法的全部內容,希望能幫助大家吧。
電腦已成為我們日常生活和工作中不可或缺工具的今天。無論是處理文檔、上網沖浪,還是進行娛樂休閑,鍵盤都是我們與電腦溝通的重要橋梁。然而,有時我們會遇到臺式電腦鍵盤打不了字的問題,這給我們的工作和生活帶來了諸多不便。本文將詳細探討鍵盤失靈的可能原因,并提供相應的恢復方法,幫助大家快速解決問題。
一、鍵盤失靈的可能原因
臺式電腦鍵盤打不了字的原因多種多樣,以下是一些常見的可能原因:
1. 鍵盤連接問題
如果鍵盤與電腦的連接不穩定或損壞,就可能導致鍵盤失靈。例如,USB接口松動、PS/2接口接觸不良等。
2. 鍵盤硬件故障
長時間使用或外力損壞可能導致鍵盤按鍵失靈或損壞。例如,按鍵卡住、電路板短路等。
3. 驅動程序問題
如果鍵盤驅動程序未正確安裝或損壞,也可能導致鍵盤失靈。此時,我們需要重新安裝或更新鍵盤驅動程序。
4. 系統設置問題
有時,系統設置不當也可能導致鍵盤失靈。例如,輸入法設置錯誤、鍵盤布局設置不正確等。
5. 軟件沖突或病毒干擾
某些軟件沖突或病毒干擾也可能導致鍵盤失靈。此時,我們需要檢查系統日志或使用殺毒軟件進行排查和解決。
二、按哪個鍵恢復?
針對以上可能的原因,我們可以采取以下相應的恢復方法:
1. 檢查鍵盤連接
首先,我們需要檢查鍵盤與電腦的連接是否穩定。如果是USB接口,可以嘗試更換其他USB接口;如果是PS/2接口,可以嘗試重新插拔或更換鍵盤線。
2. 檢查鍵盤硬件
如果鍵盤連接正常但仍然失靈,那么可能是鍵盤硬件出現故障。此時,我們可以嘗試清理鍵盤按鍵、檢查電路板連接等。如果問題依然存在,可能需要更換新的鍵盤。
3. 重新安裝或更新鍵盤驅動程序
進入設備管理器,找到鍵盤設備并右鍵選擇“更新驅動程序”或“卸載設備”。然后重新啟動電腦,系統會自動重新安裝鍵盤驅動程序。如果問題依然存在,可以嘗試從官方網站下載最新版本的驅動程序并手動安裝。
4. 檢查系統設置
進入控制面板,檢查輸入法和鍵盤布局設置是否正確。如有需要,可以重新配置輸入法和鍵盤布局。
5. 檢查軟件沖突和病毒干擾
如果懷疑是軟件沖突或病毒干擾導致的問題,可以嘗試重啟電腦、卸載最近安裝的軟件或使用殺毒軟件進行全盤掃描和清理。
三、需要注意什么?
在處理鍵盤失靈問題時,我們需要注意以下幾點:
1. 備份重要數據:在進行任何系統或軟件更改之前,建議先備份重要數據以防萬一。
2. 遵循官方指南:盡量參考官方文檔或在線教程進行操作,以確保正確性和安全性。
3. 謹慎卸載軟件:在卸載可能引起沖突的軟件時,要謹慎操作并確保不會影響其他程序的正常運行。
4. 及時更新系統和軟件:保持系統和軟件的最新版本有助于修復已知的問題和漏洞。
總之,臺式電腦鍵盤打不了字雖然是一個小問題,但卻會給我們的工作和生活帶來諸多不便。通過了解可能的原因、掌握恢復方法和注意事項,我們可以輕松地解決這一問題。希望本文能對大家有所幫助,讓電腦重新成為我們高效工作和學習的得力助手。