《電子技術(shù)應(yīng)用》
您所在的位置:首頁 > 嵌入式技術(shù) > 設(shè)計(jì)應(yīng)用 > 基于嵌入式Linux的矩陣鍵盤模塊的設(shè)計(jì)
基于嵌入式Linux的矩陣鍵盤模塊的設(shè)計(jì)
來源:微型機(jī)與應(yīng)用2012年第21期
傅 超1,,2,張昌華1,2,,孟勁松1,,2
(1.電子科技大學(xué) 能源科學(xué)與工程學(xué)院,四川 成都 611731,; 2.電子科技大學(xué) 電力系統(tǒng)廣域測
摘要: 針對嵌入式系統(tǒng)的鍵盤驅(qū)動(dòng)特點(diǎn),,以Linux 2.6.21內(nèi)核為例,提出了一種基于嵌入式Linux的矩陣鍵盤的實(shí)現(xiàn)方案,。介紹了矩陣鍵盤的結(jié)構(gòu)及原理,,設(shè)計(jì)了基于Platform機(jī)制的矩陣鍵盤驅(qū)動(dòng)程序,并解決了按鍵去抖及重鍵問題,。通過測試實(shí)踐,,證明該驅(qū)動(dòng)程序工作高效、穩(wěn)定可靠,。
Abstract:
Key words :

摘  要: 針對嵌入式系統(tǒng)的鍵盤驅(qū)動(dòng)特點(diǎn),,以Linux 2.6.21內(nèi)核為例,,提出了一種基于嵌入式Linux矩陣鍵盤的實(shí)現(xiàn)方案,。介紹了矩陣鍵盤的結(jié)構(gòu)及原理,設(shè)計(jì)了基于Platform機(jī)制的矩陣鍵盤驅(qū)動(dòng)程序,,并解決了按鍵去抖及重鍵問題,。通過測試實(shí)踐,證明該驅(qū)動(dòng)程序工作高效,、穩(wěn)定可靠,。
關(guān)鍵詞: 嵌入式Linux;Platform機(jī)制,;矩陣鍵盤,;鍵盤驅(qū)動(dòng)程序

 在嵌入式系統(tǒng)中,Linux操作系統(tǒng)由于具有開放源碼,、良好的可移植性,、多任務(wù)等優(yōu)勢,已成為開發(fā)嵌入式產(chǎn)品的優(yōu)秀操作平臺(tái),,其中鍵盤是人機(jī)交互設(shè)備中重要的輸入設(shè)備,,用于向設(shè)備輸入數(shù)據(jù)和信息[1]。在嵌入式系統(tǒng)中,,一般使用簡易的鍵盤作為輸入設(shè)備[2],,它由一系列開關(guān)矩陣排列而成(包括數(shù)字鍵、字母鍵,、符號(hào)鍵,、功能鍵等)。實(shí)現(xiàn)鍵盤掃描的方法有采用特定芯片和軟件方法兩種。
 采用特定芯片實(shí)現(xiàn)鍵盤掃描,,會(huì)增加嵌入式系統(tǒng)開發(fā)的成本,。而利用ARM處理器強(qiáng)大的功能,采用軟件的方法實(shí)現(xiàn)鍵盤掃描不僅可以降低成本,,還可以節(jié)省CPU的資源開銷,。因此,本文提出的鍵盤方案是以嵌入式Linux和AT91RM9200為軟硬件平臺(tái),,設(shè)計(jì)了基于Platform機(jī)制的矩陣鍵盤驅(qū)動(dòng)程序,,解決了按鍵去抖及重鍵問題,在實(shí)際應(yīng)用中表明該方案具有很好的穩(wěn)定性和實(shí)時(shí)性,。
1 矩陣式鍵盤的結(jié)構(gòu)及原理
 硬件平臺(tái)是基于CE9200架構(gòu)的AT91RM9200處理器,,工作于180 MHz時(shí)性能高達(dá)200 MIPS,功耗較低,,適用于高性能的嵌入式系統(tǒng),。
 在鍵盤中,排列開關(guān)最常用,、也最有效的方法是二維矩陣,,所需的開關(guān)數(shù)目根據(jù)需求而定,開關(guān)放置在行與列的交點(diǎn)上,。本系統(tǒng)設(shè)計(jì)的是一個(gè)4×4矩陣鍵盤(k1~k16),,由4根行線和4根列線組成,分別使用CPU的8個(gè)通用輸入/輸出GPIO(General Purpose I/O port)口,,利用排阻作為上拉電阻,。鍵盤按鍵使用鍋片式,當(dāng)按下某鍵,,對應(yīng)行和列的GPIO口相互導(dǎo)通[3],。驅(qū)動(dòng)程序初始化時(shí),所有行均為輸入端,,并設(shè)置為高電平,;所有列均為輸出端,置為低電平,。其電路原理圖如圖1所示,。

2 Platform總線模型下鍵盤驅(qū)動(dòng)
2.1 Platform總線模型

 Platform總線是Linux 2.6 kernel中引入的一種虛擬總線,Platform機(jī)制中將設(shè)備本身的資源注冊進(jìn)內(nèi)核,,由內(nèi)核統(tǒng)一管理,。在驅(qū)動(dòng)程序中通過platform_device提供的標(biāo)準(zhǔn)接口進(jìn)行申請并使用這些資源。platform_driver通過platform bus獲取platform_device,,platfrom_driver的根本目的是為了統(tǒng)一管理系統(tǒng)的外設(shè)資源,,為驅(qū)動(dòng)程序提供統(tǒng)一的接口來訪問系統(tǒng)資源,,將驅(qū)動(dòng)和資源分離,從而來提高程序的可移植性[4],。
2.2 鍵盤驅(qū)動(dòng)
 在Platform總線模型下,,鍵盤驅(qū)動(dòng)通常是采用層次式結(jié)構(gòu),由上層鍵盤抽象層和下層鍵盤硬件處理層來實(shí)現(xiàn)[5],。上層是鍵盤驅(qū)動(dòng)程序中的核心部分,,實(shí)現(xiàn)將掃描碼轉(zhuǎn)換成鍵碼,再將鍵碼轉(zhuǎn)換成目標(biāo)碼存放到鍵值緩沖區(qū)等功能,。上層鍵盤抽象層中還定義了一些系統(tǒng)調(diào)用函數(shù),,而這些系統(tǒng)調(diào)用功能是由下層硬件處理層來實(shí)現(xiàn)的。下層是直接對硬件進(jìn)行操作,,其具體實(shí)現(xiàn)是由不同的硬件所決定的,。
3 鍵盤驅(qū)動(dòng)程序的實(shí)現(xiàn)
 鍵盤驅(qū)動(dòng)程序的實(shí)現(xiàn)可分為初始化函數(shù)的實(shí)現(xiàn)、系統(tǒng)調(diào)用函數(shù)的實(shí)現(xiàn)以及鍵盤掃描的實(shí)現(xiàn)三部分,。
3.1 初始化函數(shù)的實(shí)現(xiàn)
 初始化中主要完成設(shè)備注冊到系統(tǒng)內(nèi)核,、資源申請、鍵盤設(shè)備檢測等工作,。其具體實(shí)現(xiàn)過程可分為兩步:platform_device與platform_driver的定義及初始化,、系統(tǒng)探測函數(shù)at91key_probe的實(shí)現(xiàn)。
3.1.1 platform_device與platform_driver的定義及初始化
 首先,,注冊,、初始化platform_device結(jié)構(gòu)變量,并將platform_device添加到platform總線,;然后再進(jìn)行設(shè)備號(hào)的申請;最后對platform_driver進(jìn)行注冊,,注冊函數(shù)如下:
 Ret=platform_driver_register(&at91key_driver),;
 platform_driver_register()注冊時(shí),會(huì)將當(dāng)前注冊的platform_driver中的name變量的值和已注冊的所有platform_device中的name變量的值進(jìn)行比較,,只有找到具有相同名稱的platform_device才能注冊成功,。當(dāng)注冊成功時(shí),會(huì)調(diào)用platform_driver結(jié)構(gòu)元素probe函數(shù)指針(即at91key_probe),。
3.1.2 系統(tǒng)探測函數(shù)at91key_probe的實(shí)現(xiàn)
 在函數(shù)指針at91key_probe所指向的系統(tǒng)探測函數(shù)里,,主要完成以下工作:
 (1)鍵盤端口(即8個(gè)GPIO端口)進(jìn)行初始化,,初始化函數(shù)如下:
Init_Keyboard(),;
 在函數(shù)Init_Keyboard中,所有行的管腳均為輸入端,,并設(shè)置為高電平,;所有列的管腳為輸出端,,并置為低電平。初始化函數(shù)如下:
void Init_Keyboard(void)
{
      //行線
at91_set_gpio_input(AT91_PIN_PB0,,1),;
at91_set_gpio_input(AT91_PIN_PB1,1),;
at91_set_gpio_input(AT91_PIN_PB2,,1);
at91_set_gpio_input(AT91_PIN_PB3,,1),;
     //列線
at91_set_gpio_output(AT91_PIN_PB4,0),;
at91_set_gpio_output(AT91_PIN_PB5,,0);
at91_set_gpio_output(AT91_PIN_PB11,,0),;
at91_set_gpio_output(AT91_PIN_PB12,0),;
at91_sys_write(AT91_PMC_PCER,,(0x1<<AT91RM9200_ID_PIOB));    
}
?。?)將已分配到的設(shè)備號(hào)以及設(shè)備操作接口(即為struct file_operations結(jié)構(gòu))賦予struct cdev結(jié)構(gòu)變量,,用cdev_init()函數(shù)初始化已分配到的結(jié)構(gòu)并與file_operations結(jié)構(gòu)關(guān)聯(lián)起來,再調(diào)用cdev_add()函數(shù)將設(shè)備號(hào)與struct cdev結(jié)構(gòu)進(jìn)行關(guān)聯(lián)并向內(nèi)核正式報(bào)告新設(shè)備的注冊,。其注冊函數(shù)如下:
cdev_init(&at91_key->chrdev,,&key_fops);
ret=cdev_add(&at91_key->chrdev,,dev_id,,1);
?。?)利用函數(shù)class_create和class_device_create自動(dòng)創(chuàng)建設(shè)備節(jié)點(diǎn),。其函數(shù)如下:
key_class=class_create(THIS_MODULE,KEY_NAME),;
cls_key_dev=class_device_create(key_class,,NULL,dev_id,,&pdev->dev,,KEY_NAME);
?。?)啟動(dòng)系統(tǒng)內(nèi)核定時(shí)器key_run_timer(),,按固定的時(shí)間間隔(即定時(shí)器處理函數(shù)觸發(fā)時(shí)間為100 ms)來執(zhí)行鍵盤掃描函數(shù)Scan_Keyboard(),。
3.2 系統(tǒng)調(diào)用函數(shù)的實(shí)現(xiàn)
 Linux為字符設(shè)備提供了統(tǒng)一的操作函數(shù)接口,內(nèi)核使用file_operations結(jié)構(gòu)建立主設(shè)備號(hào)和設(shè)備驅(qū)動(dòng)程序的連接[6],。file_operations數(shù)據(jù)結(jié)構(gòu)指明能夠?qū)ζ湓O(shè)備文件進(jìn)行的操作,,其中大部分是指向用戶自己編寫的設(shè)備操作函數(shù)的函數(shù)指針,其相當(dāng)于一個(gè)指針跳轉(zhuǎn)表,。在Linux系統(tǒng)中,,設(shè)備驅(qū)動(dòng)程序以文件系統(tǒng)結(jié)構(gòu)的方式為I/O設(shè)備提供一組入口點(diǎn)。因此,,對此結(jié)構(gòu)的訪問就相當(dāng)于操作設(shè)備文件,。
 在內(nèi)核中是使用file_operation結(jié)構(gòu)中函數(shù)指針來訪問驅(qū)動(dòng)程序的函數(shù),文件可以認(rèn)為是一個(gè)“對象”,,操作它的函數(shù)是“方法”,,這些方法主要負(fù)責(zé)系統(tǒng)調(diào)用的實(shí)現(xiàn)。
 下面介紹本鍵盤驅(qū)動(dòng)中打開函數(shù),、關(guān)閉函數(shù)及讀函數(shù)等系統(tǒng)調(diào)用的具體實(shí)現(xiàn),。
3.2.1 打開函數(shù)及關(guān)閉函數(shù)的實(shí)現(xiàn)
 應(yīng)用程序打開設(shè)備文件時(shí),會(huì)執(zhí)行驅(qū)動(dòng)中的打開設(shè)備文件描述符的操作,。通過file_opreation結(jié)構(gòu)中設(shè)備文件操作結(jié)構(gòu)的映射,,調(diào)用驅(qū)動(dòng)中的key_open函數(shù)。此函數(shù)主要是使用try_module_get(THIS_MODULE),,去增加管理此設(shè)備的THIS_MODULE模塊的使用計(jì)數(shù),。
 同樣地,當(dāng)應(yīng)用程序中使用close函數(shù)來關(guān)閉設(shè)備文件時(shí),,實(shí)質(zhì)是通過對應(yīng)文件的file_opreation結(jié)構(gòu)中的release函數(shù)指針來執(zhí)行系統(tǒng)調(diào)用函數(shù)key_release,。在函數(shù)key_release中使用module_put(THIS_MODULE)減少對管理此設(shè)備的THIS_MODULE模塊的使用計(jì)數(shù)。
這樣,,當(dāng)設(shè)備在使用時(shí),,管理此設(shè)備的模塊就不能被卸載,只有設(shè)備不再使用時(shí)模塊才能被卸載,。
3.2.2 讀函數(shù)的實(shí)現(xiàn)
 鍵盤讀函數(shù)通過copy_to_user()函數(shù)將從緩沖區(qū)讀取的鍵值復(fù)制到用戶數(shù)據(jù)區(qū),上層應(yīng)用程序通過調(diào)用讀函數(shù)即可獲取該鍵值,。鍵盤讀函數(shù)執(zhí)行流程如圖2所示,。

 在鍵盤驅(qū)動(dòng)中進(jìn)行讀操作時(shí),聲明等待隊(duì)列之后,,判斷當(dāng)前循環(huán)隊(duì)列是否有數(shù)據(jù)可讀,,若無數(shù)據(jù)可讀,則直接跳出等待隊(duì)列,,得到緩沖區(qū)的數(shù)據(jù),,調(diào)用函數(shù)cope_to_user,,將得到的鍵值拷貝到用戶數(shù)據(jù)區(qū);若有數(shù)據(jù)可讀,,設(shè)置當(dāng)前進(jìn)程的狀態(tài),,利用中斷狀態(tài)來等待數(shù)據(jù)循環(huán)隊(duì)列。當(dāng)有數(shù)據(jù)到循環(huán)隊(duì)列,,設(shè)置狀態(tài)為任務(wù)運(yùn)行狀態(tài)并跳出等待隊(duì)列,,緩沖區(qū)的數(shù)據(jù)拷貝到用戶數(shù)據(jù)區(qū)。一旦上層用戶程序進(jìn)行讀操作,,系統(tǒng)調(diào)用將通過key_read()函數(shù)來獲取用戶數(shù)據(jù)區(qū)的鍵值,。
 等待隊(duì)列是由等待某些事件發(fā)生的進(jìn)程組成的簡單鏈表。內(nèi)核中每個(gè)等待隊(duì)列都要一個(gè)等待隊(duì)列頭(wake_queue_head),,等待隊(duì)列頭是一個(gè)類型為wake_queue_head_t的數(shù)據(jù)結(jié)構(gòu),。等待隊(duì)列可通過DECLARE_WAITQUEUE()靜態(tài)創(chuàng)建。
3.3 鍵盤掃描的實(shí)現(xiàn)
 矩陣鍵盤通常是采用逐行(或列)掃描的方式識(shí)別按鍵,,通常分兩步進(jìn)行:(1)識(shí)別鍵盤有無鍵按下,;(2)在有鍵按下時(shí)識(shí)別出具體的按鍵。鍵盤的工作方式有3種:編程掃描,、定時(shí)掃描和中斷掃描,。本方案采用高效率的定時(shí)掃描,定時(shí)掃描按照內(nèi)核定時(shí)器指定的時(shí)間間隔來執(zhí)行掃描工作,。
 鍵盤掃描算法流程圖如圖3所示,。

 

 

 鍵盤掃描過程是微處理器通過定時(shí)查看鍵盤矩陣以確定是否有鍵按下,并查詢被按下的鍵,。驅(qū)動(dòng)給每個(gè)按鍵分配一個(gè)鍵值,,即按鍵的唯一標(biāo)識(shí)符。應(yīng)用程序通過按鍵鍵值識(shí)別被按下的鍵,。初始化時(shí),,所有行均為輸入端,并設(shè)置為高電平,,所有的列為輸出端,,置為低電平;當(dāng)無鍵按下時(shí),,將從所有作為輸入端的行中讀到高電平,。只要有按鍵閉合,其中一行將變?yōu)榈碗娖?。因此,,微處理器只需檢測是否有某行電平變?yōu)榈碗娖郊纯纱_定是否有鍵按下。例如,,在圖1中,,如果PB1變?yōu)榈碗娖?,則表示k7、k8,、k9和k15中至少有一個(gè)按鍵被按下,。
 在確定按鍵操作所在行的位置之后,下一步就是要查看按鍵操作所在列的位置,。在4個(gè)列輸出端口中,,輪流將其中某一個(gè)端口的輸出置為低電平,其他3個(gè)端口的輸出置為高電平,。這樣逐列進(jìn)行掃描,,直到按鍵所在的列端口輸出為低電平,此時(shí)按鍵操作所在行的管腳的輸入端口的值會(huì)變成低電平,。例如,,在確認(rèn)k7、k8,、k9和k15這行中有按鍵按下之后,,進(jìn)行逐列掃描。若發(fā)現(xiàn)在PB5為低電平時(shí)(其他端口輸出均為高電平),,PB1管腳的輸入端口變?yōu)榈碗娖?,則可以斷定按鍵k8被按下了。因此可從行號(hào)和列號(hào)對應(yīng)的二維數(shù)組(也就是鍵值映射表)中找到該鍵的鍵值,。
4 按鍵抖動(dòng)及重鍵問題的解決
 嵌入式系統(tǒng)中常用機(jī)械式按鍵,,由于受到彈性作用的影響,鍵盤在被按下或釋放時(shí),,通常會(huì)產(chǎn)生機(jī)械抖動(dòng),,需經(jīng)過一段時(shí)間后才能穩(wěn)定下來,因此處理器不能隨著按鍵的按下或釋放而產(chǎn)生明確的電平1或者0,。雖然肉眼看來開關(guān)能夠快速穩(wěn)定地閉合,,但與處理器運(yùn)行的速度相比,開關(guān)的動(dòng)作則相對較慢,。
 為了消除按鍵抖動(dòng)的問題,,根據(jù)開關(guān)的回彈特性,處理器按照一定的時(shí)間間隔對鍵盤進(jìn)行掃描,,該時(shí)間間隔被稱為去除回彈周期,,一般為30 ms~100 ms。
 鍵盤去抖的流程圖如圖4所示,,流程描述如下:

 (1)初始化時(shí),,將鍵盤的狀態(tài)標(biāo)志變量Bsflag置為1,。按內(nèi)核定時(shí)器設(shè)置的100 ms時(shí)間間隔對鍵盤進(jìn)行逐行掃描,,若發(fā)現(xiàn)有鍵按下的信號(hào)出現(xiàn)時(shí),此時(shí)就要確定是正常擊鍵行為還是抖動(dòng),。
?。?)在檢測是不是抖動(dòng)時(shí),先啟動(dòng)一個(gè)延時(shí)20 ms的定時(shí)器,,20 ms之后再次對鍵盤進(jìn)行掃描,,判斷硬件上是否有鍵按下,若沒有,,則顯然是抖動(dòng),;若有鍵按下,則是用戶正常擊鍵行為,,因此將此鍵值iscancode存入鍵值緩沖區(qū)里,,同時(shí)Bsflag=0。之后就啟動(dòng)一個(gè)100 ms的定時(shí)器,,這個(gè)定時(shí)器的作用是判斷用戶何時(shí)松開鍵盤(注意這里是100 ms的定時(shí)器,,與剛才的20 ms不同)。
?。?)在100 ms定時(shí)器定時(shí)時(shí)間到了之后,,要判斷此鍵是否已經(jīng)彈起。若還是按下,,繼續(xù)啟動(dòng)延時(shí)100 ms的定時(shí)器,,在下一個(gè)100 ms時(shí)再進(jìn)行判斷。若是彈起,,則要進(jìn)一步判斷是抖動(dòng)現(xiàn)象還是已完全彈起,,進(jìn)行一個(gè)20 ms的延遲去抖就可以完全判斷出來。當(dāng)判斷出鍵是完全彈起,,則將此鍵值iscancode加上0x80存入鍵值緩沖區(qū)里,,同時(shí)Bsflag=1。此時(shí)按鍵已經(jīng)完全地被松開彈起了,。
 在程序中對鍵盤標(biāo)志變量Bsflag和鍵值緩沖區(qū)鍵值(是否小于0x80)進(jìn)行有效的判斷,,完全可以解決按鍵的防抖及重鍵問題。
5 鍵盤驅(qū)動(dòng)的測試
 驅(qū)動(dòng)開發(fā)完成后,,用insmod將模塊加載入內(nèi)核,,在PC和目標(biāo)板之間搭建好嵌入式交叉編譯環(huán)境。在硬件平臺(tái)CE9200目標(biāo)板中的Linux系統(tǒng)下進(jìn)行測試,,通過PC上的超級(jí)終端將測試結(jié)果信息打印顯示出來,。
在圖5中顯示了鍵盤驅(qū)動(dòng)設(shè)備的成功打開與關(guān)閉,以及對按鍵動(dòng)作信號(hào)的高效準(zhǔn)確的響應(yīng),并成功解決了按鍵防抖及重鍵問題,,證明本設(shè)計(jì)的矩陣鍵盤工作高效,、穩(wěn)定。

 本文提出的一種基于CE9200平臺(tái)和嵌入式Linux鍵盤驅(qū)動(dòng)的實(shí)現(xiàn)方案,,實(shí)現(xiàn)了操作的高效和穩(wěn)定,,已成功應(yīng)用于工程實(shí)踐中的多款嵌入式設(shè)備,證明了在一定的要求下該方案完全能夠滿足性能要求,。
參考文獻(xiàn)
[1] 怯肇乾.嵌入式人機(jī)界面中的鍵盤及其接口設(shè)計(jì)[J].單片機(jī)與嵌入式應(yīng)用系統(tǒng),,2006,20(4):24-27.
[2] Tool interface standard executable and linking format specification(Version 1.2)[S]. 1995.
[3] 華清遠(yuǎn)見嵌入式培訓(xùn)中心.嵌入式Linux應(yīng)用程序開發(fā)標(biāo)準(zhǔn)教程(第2版)[M].北京:人民郵電出版社,,2009:335-356.
[4] 宋寶華.Linux設(shè)備驅(qū)動(dòng)開發(fā)詳解(第2版)[M].北京:人民郵電出版社,,2010:243-248.
[5] 林樹新,吳朝暉.Linux鍵盤驅(qū)動(dòng)的移植分析及實(shí)現(xiàn)[J].計(jì)算機(jī)工程,,2005,,31(2):211-213.
[6] Liu Kang, Qian Xu,, Li Yaxu,, et al. Research of matrix keyboard device driver based on embedded Linux [C]. 2010 Asia-Pacific Conference on Information Network and Digital Content Security (2010APCID), Scientific Research,, 17-19 December 2010:239-243.

此內(nèi)容為AET網(wǎng)站原創(chuàng),,未經(jīng)授權(quán)禁止轉(zhuǎn)載。