13.Linux鍵盤按鍵驅動 (詳解)

来源:http://www.cnblogs.com/lifexy/archive/2017/09/19/7553861.html
-Advertisement-
Play Games

版權聲明:本文為博主原創文章,未經博主允許不得轉載。 在上一節分析輸入子系統內的intput_handler軟體處理部分後,接下來我們開始寫input_dev驅動 本節目標: 實現鍵盤驅動,讓開發板的4個按鍵代表鍵盤中的L、S、空格鍵、回車鍵 1.先來介紹以下幾個結構體使用和函數,下麵代碼中會用到 ...


 

版權聲明:本文為博主原創文章,未經博主允許不得轉載。

 


 

上一節分析輸入子系統內的intput_handler軟體處理部分後,接下來我們開始寫input_dev驅動

本節目標:

       實現鍵盤驅動,讓開發板的4個按鍵代表鍵盤中的L、S、空格鍵、回車鍵

 


 

1.先來介紹以下幾個結構體使用和函數,下麵代碼中會用到

1)input_dev驅動設備結構體中常用成員如下:

struct input_dev {      

       void *private;
       const char *name;  //設備名字
       const char *phys;  //文件路徑,比如 input/buttons
       const char *uniq;   
       struct input_id id;

 
       unsigned long evbit[NBITS(EV_MAX)];  //表示支持哪類事件,常用有以下幾種事件(可以多選)
       //EV_SYN      同步事件,當使用input_event()函數後,就要使用這個上報個同步事件
       //EV_KEY       鍵盤事件
       //EV_REL       (relative)相對坐標事件,比如滑鼠
       //EV_ABS       (absolute)絕對坐標事件,比如搖桿、觸摸屏感應
       //EV_MSC      其他事件,功能
       //EV_LED       LED燈事件
       //EV_SND      (sound)聲音事件

       //EV_REP       重覆鍵盤按鍵事件
  //(內部會定義一個定時器,若有鍵盤按鍵事件一直按下/鬆開,就重覆定時,時間一到就上報事件)   

       //EV_FF         受力事件
       //EV_PWR      電源事件
       //EV_FF_STATUS  受力狀態事件

       unsigned long keybit[NBITS(KEY_MAX)];   //存放支持的鍵盤按鍵值
                                    //鍵盤變數定義在:include/linux/input.h, 比如: KEY_L(按鍵L)

       unsigned long relbit[NBITS(REL_MAX)];    //存放支持的相對坐標值
       unsigned long absbit[NBITS(ABS_MAX)];   //存放支持的絕對坐標值
       unsigned long mscbit[NBITS(MSC_MAX)];   //存放支持的其它事件,也就是功能
       unsigned long ledbit[NBITS(LED_MAX)];    //存放支持的各種狀態LED
       unsigned long sndbit[NBITS(SND_MAX)];    //存放支持的各種聲音
       unsigned long ffbit[NBITS(FF_MAX)];       //存放支持的受力設備
       unsigned long swbit[NBITS(SW_MAX)];     //存放支持的開關功能

 ... ...

2)函數如下:

struct input_dev *input_allocate_device(void);  //向內核中申請一個input_dev設備,然後返回這個設備
  
input_unregister_device(struct input_dev *dev);   //卸載/sys/class/input目錄下的input_dev這個類設備, 一般在驅動出口函數寫
 
input_free_device(struct input_dev *dev);   //釋放input_dev這個結構體, 一般在驅動出口函數寫
 
 

set_bit(nr,p);                  //設置某個結構體成員p裡面的某位等於nr,支持這個功能
/* 比如:
set_bit(EV_KEY,buttons_dev->evbit);   //設置input_dev結構體buttons_dev->evbit支持EV_KEY
set_bit(KEY_S,buttons_dev->keybit);  //設置input_dev結構體buttons_dev->keybit支持按鍵”S”
*/

void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value);  //上報事件
 // input_dev *dev :要上報哪個input_dev驅動設備的事件
 // type : 要上報哪類事件, 比如按鍵事件,則填入: EV_KEY
 // code: 對應的事件里支持的哪個變數,比如按下按鍵L則填入: KEY_L
 //value:對應的變數里的數值,比如鬆開按鍵則填入1,鬆開按鍵則填入0
input_sync(struct input_dev *dev); //同步事件通知

為什麼使用了input_event()上報事件函數,就要使用這個函數?

因為input_event()函數只是個事件函數,所以需要這個input_sync()同步事件函數來通知系統,然後系統才會知道

input_sync()代碼如下:

static inline void input_sync(struct input_dev *dev)
{
input_event(dev, EV_SYN, SYN_REPORT, 0); //就是上報同步事件,告訴內核:input_event()事件執行完畢
}

2.然後開始寫代碼

1)向內核申請input_dev結構體

2)設置input_dev的成員

3)註冊input_dev 驅動設備

4)初始化定時器和中斷

5)寫中斷服務函數

6)寫定時器超時函數

7)在出口函數中 釋放中斷函數,刪除定時器,卸載釋放驅動

具體代碼如下(都加了註釋):

#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <linux/gpio_keys.h>
#include <asm/gpio.h>

 
struct input_dev *buttons_dev;            //  定義一個input_dev結構體  
static struct ping_desc *buttons_id;          //保存dev_id,在定時器中用
static struct timer_list buttons_timer;    //定時器結構體  

struct  ping_desc{

       unsigned  char  *name;          //中斷設備名稱
       int            pin_irq;          //按鍵的外部中斷標誌位
       unsigned  int    pin;                //引腳
       unsigned int  irq_ctl;           //觸發中斷狀態:   IRQ_TYPE_EDGE_BOTH
       unsigned  int    button;         //dev_id,對應鍵盤的 L ,  S,  空格,  enter      
};

        // KEY1 -> L
        // KEY2 -> S
        // KEY3 -> 空格
        // KEY4 -> enter
static  struct ping_desc   buttons_desc[5]=
{
       {"s1", IRQ_EINT0,   S3C2410_GPF0,  IRQ_TYPE_EDGE_BOTH,KEY_L},
       {"s2", IRQ_EINT2,   S3C2410_GPF2,  IRQ_TYPE_EDGE_BOTH,KEY_S},
       {"s3", IRQ_EINT11, S3C2410_GPG3 , IRQ_TYPE_EDGE_BOTH,KEY_SPACE},
       {"s4", IRQ_EINT19, S3C2410_GPG11,IRQ_TYPE_EDGE_BOTH,KEY_ENTER},
};

 

/*5. 寫中斷服務函數*/
static irqreturn_t  buttons_irq (int irq, void *dev_id)       //中斷服務函數
{
       buttons_id=(struct ping_desc *)dev_id;             //保存當前的dev_id
       mod_timer(&buttons_timer, jiffies+HZ/100 );   //更新定時器值 10ms 
       return 0;
}

 

/*6.寫定時器超時函數*/
void buttons_timer_function(unsigned long i)
{
   int val;
   val=s3c2410_gpio_getpin(buttons_id->pin);             //獲取是什麼電平 
  if(val)         //高電平,鬆開
       {
         /*上報事件*/
         input_event(buttons_dev,EV_KEY,buttons_id->button, 0);  //上報EV_KEY類型,button按鍵,0(沒按下)
         input_sync(buttons_dev);         // 上傳同步事件,告訴系統有事件出現                       
      }

  else      //低電平,按下
      {
         /*上報事件*/
         input_event(buttons_dev, EV_KEY, buttons_id->button, 1);  //上報EV_KEY類型,button按鍵,1(按下)
         input_sync(buttons_dev);       // 上傳同步事件,告訴系統有事件出現
     }
}


static int buttons_init(void)   //入口函數
{
       int i;      
       buttons_dev=input_allocate_device();  //1.向內核 申請input_dev結構體
       /*2.設置input_dev ,  */
       set_bit(EV_KEY,buttons_dev->evbit);       //支持鍵盤事件
       set_bit(EV_REP,buttons_dev->evbit);       //支持鍵盤重覆按事件
    
       set_bit(KEY_L,buttons_dev->keybit);                  //支持按鍵 L
       set_bit(KEY_S,buttons_dev->keybit);                //支持按鍵 S
       set_bit(KEY_SPACE,buttons_dev->keybit);      //支持按鍵 空格
       set_bit(KEY_ENTER,buttons_dev->keybit);     //支持按鍵 enter

       /*3.註冊input_dev */
       input_register_device(buttons_dev);

      
       /*4. 初始化硬體:初始化定時器和中斷*/      
       // KEY1 -> L
       // KEY2 -> S
       // KEY3 -> 空格
       // KEY4 -> enter
       init_timer(&buttons_timer);
       buttons_timer.function=buttons_timer_function;
       add_timer(&buttons_timer);

       for(i=0;i<4;i++)
       request_irq(buttons_desc[i].pin_irq, buttons_irq, buttons_desc[i].irq_ctl, buttons_desc[i].name, &buttons_desc[i]);

       return 0;
}


static int buttons_exit(void)  //出口函數
{
       /*7.釋放中斷函數,刪除定時器,卸載釋放驅動*/
       int i;
       for(i=0;i<4;i++)
              free_irq(buttons_desc[i].pin_irq,&buttons_desc[i]);    //釋放中斷函數

       del_timer(&buttons_timer);   //刪除定時器

       input_unregister_device(buttons_dev);     //卸載類下的驅動設備
       input_free_device(buttons_dev);                //釋放驅動結構體
       return 0; 
}

module_init(buttons_init);
module_exit(buttons_exit);
MODULE_LICENSE("GPL v2");

 

3.測試運行:

掛載鍵盤驅動後, 如下圖,可以通過  ls -l /dev/event*   命令查看已掛載的設備節點:

 

上一節輸入子系統里分析到:輸入子系統的主設備號為13,其中event驅動本身的此設備號是從64開始的,所以我們的鍵盤驅動的此設備號=64+1

3.1測試運行有兩種,一種是直接打開/dev/tyy1,第二種是使用exec命令

(exec命令詳解入口地址: http://www.cnblogs.com/lifexy/p/7553228.html)

方法1:

cat /dev/tty1      //就會通過tty_io.c來訪問鍵盤驅動

方法2:

exec 0</dev/tty1    

 

3.2 調試:

若測試不成功,板子又在QT下進行的:

1)可以使用vi命令,在記事本中按按鍵試

2)或者刪除/etc/init.d/rcS 裡面有關QT自啟動的命令,然後重啟

 

若板子沒在QT下進行,也無法測試成功:

1)可以使用hexdump命令來調試代碼

(hexdump命令調試代碼詳解地址:http://www.cnblogs.com/lifexy/p/7553550.html)

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 公司服務用的mysql,最近在查詢時時間很慢,經常會上10多秒,查看了一下查詢的執行計劃,發現索引沒有生效。 存儲引擎使用InnoDB。 一開始在主庫查詢,一直很好奇為什麼索引不生效,切換到備庫之後,發現備庫是有效的。 開始考慮是不是因為索引出問題,後對索引重建,發現效率高了不少。 簡單記錄一下對比 ...
  • 又重新安裝Hive,記錄一下吧: hadoop早已經裝上了。 cdh5的hive下載地址: http://archive.cloudera.com/cdh5/cdh/5/ 下載文件:hive-1.1.0-cdh5.10.0.tar.gz 操作系統版本:CentOS CentOS Linux rele ...
  • 之前在使用SQLyog的時候也沒有發現這樣問題,就是在使用NavicatPremium的時候這個問題讓我不能忍受,就是每次執行一條sql語句之後,即使是成功之後,下麵仍然有一條報錯信息,雖然是對總的結果來說沒有什麼影響,但是對於有些強迫症的一些人來說,這樣的報錯還是不能容忍的,我就再網上搜瞭解決方案 ...
  • 本篇文章是介紹的hadoop2.6.0的源碼編譯過程,經過實踐驗證是可以編譯成功的。 ...
  • 五筆碼 五筆碼 select comm.fun_spellcode_wb('資料庫') from dual 結果:ORY 函數 函數 ...
  • 原文發表於cu:2017-02-10 參考文檔: Elasticsearchyum文檔:https://www.elastic.co/guide/en/elasticsearch/reference/current/rpm.html Logstashyum文檔:https://www.elastic ...
  • 一,環境說明 dell vostro 1400筆記本,winxp sp3操作系統,ubuntu-9.10-desktop-i386.iso 寫這篇隨筆的時候我用的已經是ubuntu了。 我是在我的移動硬碟上的I盤中安裝的ubuntu,所以我先把這個盤的內容移到其它盤上了。 二,安裝過程 1,首先進入 ...
  • 用su 獲取root許可權 用yum -y install dhcp命令安裝dhcp服務(yum是基於RPM包管 理,自動下載RPM包並且安裝) 查看安裝後生成的配置文件 rpm -qc dhcp 編輯dhcp的配置文件 vim /etc/dhcp/dhcpd.conf 全局配置詳情: 使用subne ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...