閱讀 | 訂閱
閱讀 | 訂閱
控製係統

基於嵌入式 Linux的鍵盤驅動設計

星之球激光 來源:21ic2012-02-13 我要評論(0 )   

1 鍵盤驅動程序的設計 隨著電子信息技術飛速發展,嵌入式係統構成的各種設備得到了廣泛的應用, 嵌入式 Linux是一種開放源碼、 軟實時、 多任務的操作係統,是開發嵌入式產(chan) ...

1 鍵盤驅動程序的設計

隨著電子信息技術飛速發展,嵌入式係統構成的各種設備得到了廣泛的應用, 嵌入式  Linux是一種開放源碼、 軟實時、 多任務的操作係統,是開發嵌入式產(chan) 品的優(you) 秀操作係統平台,其中鍵盤是人機界麵中人類監控計算機重要數據輸入設備。實現鍵盤有兩(liang) 種方法:一種是采用現有的一些芯片實現鍵盤掃描;二是用軟件實現鍵盤掃描。目前許多芯片可用來實現鍵盤掃描,但是鍵盤掃描的軟件實現方法有助於(yu) 縮減係統的重複開發成本, 而隻需很少的 CPU 開銷。嵌入式控製器的功能很強,可以充分利用這一資源。本課題提出的鍵盤方案是以嵌入式  Linux和 PXA255為(wei) 軟硬件平台, 通過測試,表明其具有良好的穩定性和實時性。

2 矩陣式鍵盤的結構與(yu) 工作原理

本課題采用矩陣鍵盤, 如圖 1所示。四根行線四根列線組成 4 *4矩陣鍵盤, 分別用 CPU 的 4個(ge) GPIO口。當有鍵按下,某個(ge) 列 GPI O 口電平被下拉從(cong) 而產(chan) 生下降沿, 觸發中斷。其中按鍵行陣列必須提供上拉信號,列陣列加二極管,防止瞬間電流過大對 GPI O口造成衝(chong) 擊。

圖 1 矩陣鍵盤原理圖.

3  Linux鍵盤驅動簡介

在 Linux中, 鍵盤驅動被劃分成兩(liang) 層來實現。上層是一個(ge) 通用鍵盤抽象層, 下層則是硬件處理層, 主要對硬件進行直接的操作。鍵盤驅動程序上層公共部分在 driver /keyboard . c裏。文件中最重要的是內(nei) 核用 EXPORT _SYM BOL這個(ge) 宏導出的 handle_scancode函數 。在這個(ge) 文件中還定義(yi) 了其它的幾個(ge) 回調函數,它們(men) 由鍵盤驅動程序中上層公共部分調用, 並且由底層硬件處理函數實現。鍵盤驅動程序的底層硬件處理部分則根據不同硬件有不同實現。

4 鍵盤驅動程序的實現

4 . 1  宏定義(yi) module init和 module exit

通過宏定義(yi) module init和module exit可以看出,驅動程序的入口從(cong) kd_ctrl_init( )開始。當內(nei) 核模塊加載的時候, 默認調用 module_ i nit( kd_c trl_init) ,在 kd_ctr l_ i nit( )中將完成一些初始化工作, 主要如下:

( 1) 把 GPI O 口的起始虛擬地址映射到 GPI O _BASE _PHY ( 0x1000b000),數據長度為(wei) 0x400 :

  GPI O _ BASE = ( i nt) ioremap ( GPI O _ BASE _ P HY,0x400);

( 2) 利用 requeST_ irq函數將外設的中斷服務例程掛載到外部中斷處理程序中。本係統中利用 request_irq函數分別為(wei) 4個(ge) 列 GPI O口申請中斷資源, 分別占用了中斷號 1 、2 、3、 4 。其中 i是中斷號; kd_ctr l_irq是 UCB1400的中斷處理程序, kd_ctr l代表鍵盤設備名, MAGIC _DEVID是申請時告訴係統設備標誌, 用於(yu) 共享中斷線。返回值為(wei) 0表示申請成功。

( 3) 通過函數 m isc_reg ister注冊(ce) 一個(ge) 鍵盤設備, 並分配主設備號和從(cong) 設備號, 初始化一個(ge) 環形隊列以及定義(yi) 一個(ge) 鍵盤控製的數據結構。其中包括鍵值、 鍵的狀態和長按標誌。

應用程序對設備驅動的調用實際是對相應設備文件進行操作, 利用 mknod命令將此節點與(yu) 對應設備建立聯係。

( 4) 通過 init_ w a it queue_head(& sa ts . read _ w a it)初始化讀信號量。

4 . 2  打開鍵盤設備

應用程序打開設備文件時, 會(hui) 調用驅動中的 OPEN 函數, 此函數會(hui) 對鍵盤所用到的行列 GPI O 口進行配置。打開的設備在內(nei) 核中通過 file結 構進行標識, 內(nei) 核 使用 fileopreaTI ON ,通過上麵的結構中設備文件操作結構的映射, 來調用驅動中的 kd_c trl_open。接下來要做的是:

( 1) 通過 se m a_ i n it( & kdc- > irq_w ait , 0)初始化在後麵用來喚醒後台線程的信號量。

( 2) 調用初始化函數 i n it_pxa_kdc( )來初始化 GPI O口,具體(ti) 是把   行!的 GPI O 口設為(wei) 輸出模式並設定值為(wei) 0 , 把列!GPIO口設為(wei) 中斷模式,下降沿有效。如下所示:

  re t = se t_kdc_gp i o( KDC_ROW _PINS , 1 , PI NS_MODE _OUT , 0) ;

  ret = set_kdc_gp i o ( KDC _COL _PI NS , 1 , PI NS _ MODE _FALLI NG_I NTTERUPT , 0);

( 3) 以嚴(yan) 格的串行方式執行任務的效率並不高, 如果把它們(men) 放在後台調度,不管是對它們(men) 的函數還是對終端用戶進程都能得到較好的響應。所以初始化 GPIO口後,開啟一個(ge) 內(nei) 核線程 kd_ctrl_thread專(zhuan) 門用於(yu) 處理鍵盤事件, 其實也就是向係統申請了軟硬件資源。為(wei) 了確保在該線程創建完成,使用 co m pleti on ,在  Linux內(nei) 核中, co m pletion是一種簡單的同步機製,利用 co m pleti on機製可以使兩(liang) 個(ge) 任務同步。我們(men) 利用 i n it_comp l e ti on(& kdc- > i n it_ex it)動態初始化一個(ge) 線程創建信號量 i n it_ex it , 以及用 wa it_fo r_co m pleti on (& kdc- >i n it_ex it)來等待進程創建完成, 然後在進程創建結束後通過co m plete(& kdc- > i nit_ex it)確定事件已經完成即後台線程創建成功, 繼續執行函數 w ait_ for_ comp l e ti on之後的任務。

通過 ret = kerne l_t h read( kd_c trl_ t hread , kdc , CLONE_FS |CLONE_FILES)創建後台線程。

4 . 3 等待鍵盤事件

後台線程一旦創建和初始化完成, 就會(hui) 進入一個(ge) 無條件的 for循 環, 通 過 set _ task _ state ( tsk , TASK _ INTERRUPTIBLE) 將此線程推入可中斷睡眠的隊列,調用 schedule ti m eou t (H Z/100)來實現 15毫秒的進程掛起。此時讓出 CPU,直到中斷事件來臨(lin) 或睡眠超過規定時間後再重新執行。線程一旦被喚醒即按照順序先利用 set_kdc_gp io ( KDC _COL_PI NS , 1 , PI NS _MODE _ENABLEI NTERRUPT, 0) 使 所有列GPI O 口中斷, 接著調用 down _ i nterrupti b l e ( & kdc- > irq _wa it): 該函數的作用是獲得信號量 irq_wa it , 把 irq_ w a i t的值減掉 1 , 如果信號量 irq_wa it的值非負, 就直接返回,如果獲取失敗鍵盤線程將以 TASK_I NTERRUPTIBLE狀態進入可中斷睡眠,直到下次鍵盤事件利用信號量 irq_ w a it喚醒此線程才能繼續運行。因此,驅動程序在沒有按鍵按下時將阻塞自己的執行,不消耗任何的 CPU資源。

4 . 4 鍵盤事件發生

一旦有按鍵事件發生也就是產(chan) 生一個(ge) 中斷, 則進入中斷處理程序 kd_ctr l _ irq( ), 在這個(ge) 函數中所做的工作如圖 2。#p#分頁標題#e#

圖 2 中斷處理程序 kd_ ctrl_irq( )

喚醒後台線程後,把列 GPI O口中斷禁止, 隨即調用 kd_ctrl_event( )進行處理鍵盤事件。其中又調用 pxa _kdc _scan( )進行鍵值的掃描: 設定 4  4小鍵盤的所有行 GPI O 口為(wei) 輸出狀態,並設定它的值為(wei) 1 ,而所有列 GPIO口作為(wei) 輸入狀態,然後采用逐行掃描的方法, 依次去讀取四根列 GPI O 口狀態,如果某列 GPIO 口電平為(wei) 低, 就表示此行此列有鍵按下,根據行號和列號從(cong) 對應的二維數組 (也就是鍵值映射表 )中找到該鍵 的鍵值。具體(ti) 實現方法 為(wei) : 先設第 一行( GPI O7)為(wei) 0 , 掃描列的值 ( GPI O3 、 GPI O2 、GPI O1、 GPI O0),如果其中一個(ge) 列的值為(wei) 0 , 比如 GPI O3 , 則按下的鍵是 Key _5。掃描完列後,把第一行設為(wei) 1。第二行設為(wei) 0 , 再次掃描所有列 的值。掃描結 束後, 設 定所有 行 ( GPI O7 、GPI O6 、GPI O5 、 GPI O4)的值為(wei) 0 , 並且再次恢複所有列為(wei) 中斷方式,設定下降沿有效。最後返回的是代表按鍵是否按下的參數pressure值。得到此值以後,調用 stati c i n line vo i d kd_c trl_ev t_add( struc t kd_ctrl* kdc , u8 pressure , u8 keyva l ue )函數把所得值保存在對應的結構中,並將其添加到事件隊列中, 最後調用 w ake_up_ i nterrupti ble( & kdc- > read _ w a it)利用信號量 read_ w a it通知 read程序到緩衝(chong) 區讀取新數據。

4 . 5  應用程序讀取鍵盤數據

由於(yu) 用戶程序需要不斷輪詢設備,以查詢是否有數據讀取, 如果程序不處於(yu) 休眠狀態, 則將會(hui) 占用很多 CPU 的資源。因此當沒有觸摸數據時, 就阻塞此任務。此時用戶空間則需要和內(nei) 核同步, 代碼會(hui) 需要睡眠, 使用信號量是唯一的選擇, 並且它適用於(yu) 鎖會(hui) 被長時間持有的情況。如果有一個(ge) 任務試圖獲得一個(ge) 已經被占用的信號量時,信號量會(hui) 先將其中推進一個(ge) 等待隊列, 然後讓其睡眠。這時 CPU 能重獲自由, 從(cong) 而可以執行其他代碼。當持有信號量的進程將信號量釋放時, 處於(yu) 等待隊列中的那個(ge) 任務將會(hui) 被喚醒, 並獲得該信號量。

等待隊列是由等待某些事件發生的進程組成的簡單鏈表。內(nei) 核用 w ake_queue_head_t來表示等待隊列。等待隊列可通過 DECLARE _WAI TQUEUE ( )靜態創建。一旦上層用戶程序進行讀操作, 係統調用將通過 kd_ctrl_read ( )函數來實現。

4 . 6  模塊卸載

當內(nei) 核需要卸載本驅動程序時, 最後會(hui) 從(cong) 本函數退出。

此時通過 modul e_ i nit( kd_ctrl_ i n it)函數需要將在驅動程序運行期間申請的係統資源全部釋放掉,可以防止資源浪費。

5 結束語

本文介紹的嵌入式  Linux的一種矩陣小鍵盤, 成功實現了多鍵齊按和重複按鍵的功能, 已經用於(yu) 手持嵌入式設備中, 實驗證明性能穩定可靠。

轉載請注明出處。

免責聲明

① 凡本網未注明其他出處的作品,版權均屬於(yu) fun88网页下载,未經本網授權不得轉載、摘編或利用其它方式使用。獲本網授權使用作品的,應在授權範圍內(nei) 使 用,並注明"來源:fun88网页下载”。違反上述聲明者,本網將追究其相關(guan) 責任。
② 凡本網注明其他來源的作品及圖片,均轉載自其它媒體(ti) ,轉載目的在於(yu) 傳(chuan) 遞更多信息,並不代表本媒讚同其觀點和對其真實性負責,版權歸原作者所有,如有侵權請聯係我們(men) 刪除。
③ 任何單位或個(ge) 人認為(wei) 本網內(nei) 容可能涉嫌侵犯其合法權益,請及時向本網提出書(shu) 麵權利通知,並提供身份證明、權屬證明、具體(ti) 鏈接(URL)及詳細侵權情況證明。本網在收到上述法律文件後,將會(hui) 依法盡快移除相關(guan) 涉嫌侵權的內(nei) 容。

網友點評
0相關評論
精彩導讀