新聞中心

        EEPW首頁 > 嵌入式系統 > 設計應用 > 基于嵌入式Linux的鍵盤驅動設計

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

        作者: 時間:2010-12-22 來源:網絡 收藏

        4 程序的實現
        4.1 宏定義module init和module exit
        通過宏定義module init和module exit可以看出,程序的入口從kd_ctrl_init()開始。當內核模塊加載的時候,默認調用module_ jnit(kd_ctrl_init),在kd_ctrl_init()中將完成一些初始化工作,主要如下:
        (1)把GPIO口的起始虛擬地址映射到GPIO_BASE_PHY(0x1000b000),數據長度為0x400:
        GPIO_BASE=(int)ioremap(GPIO_BASE_PHY,0x400);
        (2)利用request_irq函數將外設的中斷服務例程掛載到外部中斷處理程序中。本系統中利用request_irq函數分別為4個列GPIO口申請中斷資源,分別占用了中斷號1、2、3、4。其中i是中斷號;kd_ctrl_irq是UCB1400的中斷處理程序,kd_ctrl代表設備名,MAGIC_DEVID是申請時告訴系統設備標志,用于共享中斷線。返回值為0表示申請成功。
        (3)通過函數misc_register注冊一個設備,并分配主設備號和從設備號,初始化一個環形隊列以及定義一個鍵盤控制的數據結構。其中包括鍵值、鍵的狀態和長按標志。應用程序對設備的調用實際是對相應設備文件進行操作,利用mknod命令將此節點與對應設備建立聯系。
        (4)通過init_waitqueue_head(sats.read_wait)初始化讀信號量。
        4.2 打開鍵盤設備
        應用程序打開設備文件時,會調用驅動中的OPEN函數,此函數會對鍵盤所用到的行列GPIO口進行配置。打開的設備在內核中通過file 結構進行標識,內核使用fileopreation,通過上面的結構中設備文件操作結構的映射,來調用驅動中的kd_ctrl_open。接下來要做的是:
        (1)通過sema_init(kdc->irq_wait,0)初始化在后面用來喚醒后臺線程的信號量。
        (2)調用初始化函數init_pxa_kdc()來初始化GPIO口,具體是把“行”的GPIO口設為輸出模式并設定值為O,把“列”GPIO口設為中斷模式,下降沿有效。如下所示:
        c.jpg

        (3)以嚴格的串行方式執行任務的效率并不高,如果把它們放在后臺調度,不管是對它們的函數還是對終端用戶進程都能得到較好的響應。所以初始化GPIO口后,開啟一個內核線程kd_ctrl_thread專門用于處理鍵盤事件,其實也就是向系統申請了軟硬件資源。為了確保在該線程創建完成,使用 completion,在內核中,completion是一種簡單的同步機制,利用completion機制可以使兩個任務同步。我們利
        用init_completion(kdc->init_exit)動態初始化一個線程創建信號量init_exit,以及用 wait_for_completion(kdc->init_exit)來等待進程創建完成,然后在進程創建結束后通過 complete(kdc->init_exit)確定事件已經完成即后臺線程創建成功,繼續執行函數wait_for_comp- letion之后的任務。通過ret=kernel_thread(kd_ctrl_thread,kdc,CLONE_FS|CLONE_FILES) 創建后臺線程。
        4.3 等待鍵盤事件
        后臺線程一旦創建和初始化完成,就會進入一個無條件的for循環,通過 set_task_state(tsk,TASK_INTERRUPTIBLE)將此線程推入可中斷睡眠的隊列,調用schedule timeout(Hz/100)來實現15毫秒的進程掛起。此時讓出CPU,直到中斷事件來臨或睡眠超過規定時間后再重新執行。線程一旦被喚醒即按照順序先利用set_kdc_gpio(KDC_COL_PINS,1,PINS_MODE_ENABLEINTERRUPT,0)使所有列GPIO口中斷,接著調用down_interruptible(kdc->irq_wait):該函數的作用是獲得信號量irq_wait,把 irq_wait的值減掉1,如果信號量irq_wait的值非負,就直接返回,如果獲取失敗鍵盤線程將以TASK_INTERRUPTIBLE狀態進入可中斷睡眠,直到下次鍵盤事件利用信號量irq_wait喚醒此線程才能繼續運行。因此,驅動程序在沒有按鍵按下時將阻塞自己的執行,不消耗任何的CPU 資源。
        4.4 鍵盤事件發生
        一旦有按鍵事件發生也就是產生一個中斷,則進入中斷處理程序kd_ctrl_irq(),在這個函數中所做的工作如圖2。
        b.JPG

        喚醒后臺線程后,把列GPIO口中斷禁止,隨即調用kd_ctrl_event()進行處理鍵盤事件。其中又調用pxa_kdc_scan()進行鍵值的掃描:設定4×4小鍵盤的所有行GPIO口為輸出狀態,并設定它的值為1,而所有列GPIO口作為輸入狀態,然后采用逐行掃描的方法,依次去讀取四根列 GPIO口狀態,如果某列GPIO口電平為低,就表示此行此列有鍵按下,根據行號和列號從對應的二維數組(也就是鍵值映射表)中找到該鍵的鍵值。具體實現方法為:先設第一行(GPIO7)為0,掃描列的值(GPIO3、GPIO2、GPIO1、GPIO0),如果其中一個列的值為O,比如GPIO3,則按下的鍵是Key_5。掃描完列后,把第一行設為1。第二行設為0,再次掃描所有列的值。掃描結束后,設定所有行(GPIO7、GPIO6、GPIO5、 GPIO4)的值為0,并且再次恢復所有列為中斷方式,設定下降沿有效。最后返回的是代表按鍵是否按下的參數pressure值。得到此值以后,調用 sta-tic inline void kd_ctrl_evt_add(struct kd_ctrl*kdc,u8 pressure,u8 keyvalue)函數把所得值保存在對應的結構中,并將其添加到事件隊列中,最后調用 wake_up_interruptible(kdc->read_wait)利用信號量read_wait通知read程序到緩沖區讀取新數據。
        4.5 應用程序讀取鍵盤數據
        由于用戶程序需要不斷輪詢設備,以查詢是否有數據讀取,如果程序不處于休眠狀態,則將會占用很多CPU的資源。因此當沒有觸摸數據時,就阻塞此任務。此時用戶空間則需要和內核同步,代碼會需要睡眠,使用信號量是唯一的選擇,并且它適用于鎖會被長時間持有的情況。如果有一個任務試圖獲得一個已經被占用的信號量時,信號量會先將其中推進一個等待隊列,然后讓其睡眠。這時CPU能重獲自由,從而可以執行其他代碼。當持有信號量的進程將信號量釋放時,處于等待隊列中的那個任務將會被喚醒,并獲得該信號量。
        等待隊列是由等待某些事件發生的進程組成的簡單鏈表。內核用wake_queue_head_t來表示等待隊列。等待隊列可通過 DECLARE_WAITQUE-UE()靜態創建。一旦上層用戶程序進行讀操作,系統調用將通過kd_ctrl_read()函數來實現。
        4.6 模塊卸載
        當內核需要卸載本驅動程序時,最后會從本函數退出。此時通過module_init(kd_ctrl_init)函數需要將在驅動程序運行期間申請的系統資源全部釋放掉,可以防止資源浪費。

        5 結束語
        本文介紹的的一種矩陣小鍵盤,成功實現了多鍵齊按和重復按鍵的功能,已經用于手持設備中,實驗證明性能穩定可靠。

        本文引用地址:http://www.104case.com/article/151165.htm
        linux操作系統文章專題:linux操作系統詳解(linux不再難懂)

        上一頁 1 2 下一頁

        評論


        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 南昌市| 博白县| 郸城县| 正阳县| 黎城县| 保德县| 丰镇市| 平阳县| 宜黄县| 侯马市| 湟中县| 南投市| 合江县| 阜平县| 旬邑县| 新昌县| 边坝县| 仙桃市| 广河县| 确山县| 东方市| 甘泉县| 巴林左旗| 丹江口市| 宣恩县| 安义县| 中江县| 皋兰县| 山西省| 靖宇县| 浮梁县| 龙岩市| 嘉鱼县| 明水县| 佛学| 凤庆县| 萍乡市| 北辰区| 沈丘县| 永寿县| 阳新县|