1 引 言
許多嵌入式系統,尤其是一些人機交互(HMI)較頻繁的嵌入式系統,鍵盤是一種應用最為廣泛的輸入設備。由于嵌入式設備的功能互異性,為其提供一種通用性鍵盤是不可行的,一般都需要根據嵌入式系統的實際功能來設計所需的特殊鍵盤,并實現相應的驅動程序。www.51kaifa.com
在嵌入式設備上擴展鍵盤的常用方式是通過使用CPU的GPIO端口掃描實現的,顯然,這種方式會占用系統的GPIO資源,特別是在GPIO資源比較緊張而按鍵又較多的系統,這個問題就特別突出。當然,也可以通過外擴GPIO(如8255等)或外擴專用的鍵盤接口(如8279等)方式實現,但這種方式顯然增加了系統的復雜度,在實際系統設計中頗感不便。
本文以在ARM9(AT91RM9200)嵌入式微處理器上實現一個POS機鍵盤(8×8)為例,呈現了一種在嵌入式設備上擴展多行列鍵盤的新設計思路,并在ARM-Linux系統實現了鍵盤的驅動程序。
2、接口電路的硬件設計
本文通過一個設計實例,說明如何使用一種比較簡單的方式,來實現一個8×8的POS機矩陣鍵盤,POS機所采用的微處理器是AT91RM9200芯片。AT91RM9200是ATMEL公司生產的一款高性能的32位ARM9處理器,它是一款通用工業級ARM芯片,在工業控制、智能儀器儀表等領域內得到了大量的應用[3,4],其詳細芯片特性可參見文獻[2]。
在AT91RM9200上擴展鍵盤,一般都是通過其GPIO端口來實現。AT91RM9200雖提供了4×32個可編程的GPIO端口。但為減小芯片體積和功耗,其許多GPIO端口都是與系統的外圍設備控制器端口或地址線、數據線進行復用的,所以實際可用于擴展的GPIO端口是很少的。而對于一個8×8鍵盤,若采用傳統的GPIO端口擴展方式,則需要16個GPIO,這在一個比較復雜的POS系統中是很難滿足的,因此需要采用其他方式來解決這個問題。
圖1 鍵盤接口原理圖
本文通過數據鎖存的方式,充分利用32位處理器的數據寬度優勢,使用數據線來替代鍵盤擴展所需的GPIO端口,從而減少對系統GPIO資源的占用。鍵盤接口的實現原理如圖1所示。在圖1所示的電路中,U1301(74LVCC4245)為三態緩沖器,U1302(74HC574)為鎖存器,系統工作原理描述如下:
U1301的nOE端連接系統的譯碼輸出nKey_CS,部件地址由系統譯碼電路決定,當向該地址寫數據時,nKey_CS信號為低電平,數據可以通過U1301,同時,nKey_CS信號經兩級反相器延時后作為鎖存信號將數據鎖存到U1302的輸出端,作為鍵盤的行掃描信號,而鍵盤的列掃描信號則仍然使用系統的GPIO。依次向每行送出低電平信號,同時檢測連接在GPIO的列信號,即可實現對鍵盤的掃描。在本系統中,只使用了系統32位數據的低8位作為行掃描信號,在實現8×8矩陣鍵盤掃描的情況下,僅需要占用8個GPIO口,如果采用同樣的方式,分別使用16位數據或32位數據作為行掃描信號,則只需要占用4個或2個GPIO,顯然,與傳統的方式相比較,該方式可以大大節省系統的GPIO資源。
3、鍵盤的驅動模塊設計
完成接口電路的設計之后,還需要編寫相應的鍵盤驅動模塊。本文采用AT91RM9200芯片中已經運行了ARM-Linux操作系統,因此給鍵盤的驅動程序開發提供了很大的方便。鍵盤的驅動模塊可分為硬件初始化、文件操作函數的實現以及鍵盤掃描程序三個部分。
3.1 硬件初始化
鍵盤驅動程序的開發模式與Linux系統中一般字符設備的驅動開發步驟相似,關于Linux設備驅動開發的詳細分析可參考文獻[1]。首先需完成的是驅動程序模塊的初始化函數和清除函數。在初始化函數中,除完成模塊注冊外,還應進行硬件初始化,下面是本文根據AT91RM9200芯片的GPIO控制器的特性[2],在模塊的初始化函數中的硬件初始化的偽代碼。
……
設置所使用GPIO端口為GPIO控制器控制
設置所使用GPIO端口的類型為輸入
使能所使用GPIO端口的輸入毛刺濾波功能
使能相應的GPIO控制器時鐘
取指定地址的虛擬地址并向地址寫入數據0x0
……
3.2 文件操作函數的實現
因為鍵盤在系統中一般只起輸入作用,因此在鍵盤驅動程序的file_operations結構中,必須實現的文件操作函數只有文件讀函數。
另外在實際應用中,為防止鍵盤按鍵的丟失,被按下鍵的掃描代碼通常都放置在一個緩沖區內,直到應用程序準備處理一個按鍵為止。緩沖區大小一般視應用系統的需求而定,本例中緩沖區大小取為20個按鍵代碼。而緩沖區的實現是以一個環形隊列的形式實現。當按鍵按下后,掃描代碼將被放置在隊列的下一個空位置,若緩沖區已滿,則下一按鍵將會被丟棄。應用程序則通過鍵盤的讀函數read()從緩沖區的位置指針處起,讀取所需個數的鍵碼;完成讀取操作后,還需將已被讀取的鍵碼從緩沖隊列中刪除,并更新緩沖區的位置指針。下面給出了本例中,實現的鍵盤讀函數key_read()的偽代碼:
ssize_t key_read(……)
{
定義并初始化變量;
if 緩沖區中可被讀取的鍵碼數大于0;
取得鍵碼放置緩沖區的自旋鎖;
計算此次讀操作可讀取的代碼個數M(緩沖區中可讀取的代碼個數與程序要求個數之間的較小者);
從緩沖區位置指針開始,從緩沖區中拷貝M個的鍵碼到用戶空間緩沖區內;
更新緩沖區的位置指針和緩沖區中還剩余的鍵碼個數;
釋放自旋鎖
返回此次讀操作成功讀取的代碼個數。
Else
返回-1
}
3.3 鍵盤掃描程序
鍵盤的工作原理是通過鍵盤的行線和列線的狀態來判斷鍵盤中有無按鍵被按下。鍵盤掃描程序的功能就是用來判斷處于按下狀態的按鍵的具體位置及取得相應的鍵碼值,因此掃描程序的設計是鍵盤驅動模塊實現的核心。
鍵盤掃描程序的實現主要有兩種,即輪詢方式和中斷方式[5]。在本例中,利用操作系統定時器隊列與輪詢掃描方式結合的方法對鍵盤的驅動程序進行了設計,主要是基于以下兩個方面的原因。其一是AT91RM9200芯片的中斷信號線是非常寶貴的硬件資源,每一組GPIO端口只配置了一根中斷信號線,即32個GPIO端口共享一條信號線。這樣若采用中斷方式,則至少需要占用一條芯片中斷信號線,對多行列的鍵盤,如果其所采用的GPIO端口不是來自于同一組時,就需要占用多條中斷信號線。而且若其他設備使用的GPIO端口與鍵盤使用的GPIO口屬于同一組,那么在兩種設備的驅動程序設計中,必須進行中斷共享,這樣不僅使系統的軟件設計更為復雜,且易產生中斷丟失和中斷竟態等問題,使設備性能受到影響。其二鍵盤是系統中屬于一種相對低速的設備,采用輪詢方式完全可以滿足鍵盤的輸入要求。
ARM-Linux操作系統提供了良好的定時器機制,因此通過簡單定時器操作,就可以實現以固定間隔對鍵盤的狀態進行掃描并對按鍵事件進程處理,固定間隔的大小可根據系統需求進行配置,定義器的詳細操作可參見文獻[1]。如前所述,鍵盤掃描程序的功能就是對鍵盤的狀態進行判斷和處理。若無按鍵按下,則掃描直接返回;若有按鍵按下,則對被按下鍵的位置進行判斷,并將相應的鍵碼值寫入緩沖區中。因為本例中的鍵盤是為POS機配置,因此按鍵的準確性是至關重要,因此在掃描代碼中對按鍵值進行了多次驗證,下面是本例中使用的鍵盤掃描程序的偽代碼:
int Scan_Keyboard()
{
定義并初始化變量;
取得鍵碼放置緩沖區的自旋鎖;
if 緩沖區中還有空;
① 依次判斷各GPIO口的狀態,若無低電平,則無鍵按下,直接退出if語句;否則,有鍵按下,且當前檢驗的GPIO口連接的行線即為按鍵所在的行;
② 給鍵盤列線連接的數據線依次送入高電平,再通過判斷按鍵行線所在的GPIO端口的電平狀態,得到按鍵所在的列;
延時一小段時間,以消除鍵盤抖動;
③ 再向給鍵盤列線連接的數據線全送低電平,使用代碼段①再次判斷是否有鍵按下,若有,則取得按鍵所在的行;
④ 同樣使用代碼段②重新判斷按鍵所在的列;
⑤ 判斷第一次得到的按鍵的行與列是否與第二次完全一樣,若完全相同,則可進入下一步,否則退出if語句;
⑥ 重新向給鍵盤列線連接的數據線全送低電平,并判斷按鍵是否彈起,若仍處于按下狀態,則繼續等待,否則根據行與列,轉化為相應鍵值,并寫入緩沖區;
if語句結束;
釋放自旋鎖;
函數返回 0;
}
完成驅動程序代碼編寫后,就可以將鍵盤的驅動程序加載到ARM-Linux內核中了,既可以采用靜態加載方式,也可以采用動態方式進行加載。加載后,在應用程序中鍵盤的編程使用方式與其他字符設備一樣。采用本文所述方式設計的鍵盤,目前已配置在筆者參與開發的POS機中交用戶使用,據用戶測試,鍵盤的輸入準確率和反應時間都達到了設計要求。
4、結束語
本文以運行ARM-Linux的AT91RM9200系統為基礎,提出了一種在ARM9上擴展特殊鍵盤的新設計方法,并對鍵盤擴展的硬件設計和驅動軟件開發都作了詳細說明。本設計方法利用數據鎖存方式替代了常規的GPIO擴展,提高了系統硬件的資源利用率,這一思想也為在其他嵌入式設備擴展多行列鍵盤提供了一種新的設計思路。
評論