GPIO-KEY的實現原理及使用方法

本文將以imx6q的板子和相應BSP代碼來詳細描述在linux下, 使用GPIO當做按鍵的實現原理及使用方法。

Linux 內核下的 drivers/input/keyboard/gpio_keys.c實現了一個體繫結構無關的GPIO按鍵驅動,使用此按鍵驅動,只需在相應的板級支持包中(imx6q的是board-mx6q-sabresd.c)定義相關的數據即可。驅動的實現非常簡單,但是較適合於實現獨立式按鍵驅動。

gpio-keys是基於input架構實現的一個通用GPIO按鍵驅動。該驅動基於platform_driver架構,實現了驅動和設備分離,符合Linux設備驅動模型的思想。工程中的按鍵驅動我們一般都會基於gpio-keys來寫,所以我們有必要對gpio_keys進行分析。

一.   GPIO-KEY的實現原理

1.      定義GPIO按鍵:

首先定義一個gpio_keys_button的數組, 該類型定義了一個具體的GPIO按鍵信息

arch\arm\mach-mx6\board-mx6q-sabresd.c:

static struct gpio_keys_buttonsabresd_buttons[] = {

GPIO_BUTTON(SABRESD_VOLUME_UP,KEY_VOLUMEUP, 1, "volume-up", 0, 1),

GPIO_BUTTON(SABRESD_VOLUME_DN,KEY_POWER, 1, "volume-down", 1, 1),

 

/*addby aaron 2015.6.29*/

#ifdef FEATURE_YUTONG_ICARD2

GPIO_BUTTON(SABRESD_KEY_MENU,248, 1, "menu-key", 0, 3),

GPIO_BUTTON(SABRESD_KEY_UP,249, 1, "up-key", 0, 3),

GPIO_BUTTON(SABRESD_KEY_DOWN,250, 1, "down-key", 0, 3),

GPIO_BUTTON(SABRESD_KEY_CONFIRM,251, 1, "confirm-key", 0, 3),

GPIO_BUTTON(SABRESD_KEY_ICCARD_DET,247, 1, "iccard-det", 0, 3),

#endif

/*endby aaron*/

};

 

struct gpio_keys_button類型如下, 這是對按鍵的描述:

include\linux\gpio_keys.h:

struct gpio_keys_button {

/*Configuration parameters */

unsignedint code;   /* input event code (KEY_*,SW_*) */

intgpio;

intactive_low;

constchar *desc;

unsignedint type;    /* input event type (EV_KEY,EV_SW, EV_ABS) */

int wakeup;               /* configure the button as awake-up source */

intdebounce_interval;    /* debounce ticksinterval in msecs */

boolcan_disable;

intvalue;           /* axis value for EV_ABS*/

};

 

宏 GPIO_BUTTON()就是初始化每個gpio_key的描述:

arch\arm\mach-mx6\board-mx6q-sabresd.c:

#define GPIO_BUTTON(gpio_num, ev_code,act_low, descr, wake, debounce)      \

{                                                                           \

.gpio                   = gpio_num,                                 \

.type                  = EV_KEY,                            \

.code                  = ev_code,                                    \

.active_low      = act_low,                                     \

.desc                  = "btn " descr,                             \

.wakeup            = wake,                                         \

.debounce_interval= debounce,                                 \

}

 

接着定義一個gpio_keys_platform_data變量:

arch\arm\mach-mx6\board-mx6q-sabresd.c:

staticstruct gpio_keys_platform_data new_sabresd_button_data = {

         .buttons   = new_sabresd_buttons,

         .nbuttons = ARRAY_SIZE(new_sabresd_buttons),

};

 

struct gpio_keys_platform_data的定義也在gpio_keys.h中:

include\linux\gpio_keys.h:

structgpio_keys_platform_data {

         struct gpio_keys_button *buttons;

         int nbuttons;

         unsigned int poll_interval;       /* polling interval in msecs -

                                                  for polling driver only */

         unsigned int rep:1;           /* enable input subsystem auto repeat*/

         int (*enable)(struct device *dev);

         void (*disable)(struct device *dev);

         const char *name;            /* input device name */

};

 

2.      把 1 中定義的new_sabresd_button_data 註冊到系統中去:

我們把gpio_key當成一個platform_device設備, 所以要先定義一個platform_device設備:

arch\arm\mach-mx6\board-mx6q-sabresd.c:

staticstruct platform_device sabresd_button_device = {

         .name                ="gpio-keys", /*名字非常關鍵, 找驅動就靠它來匹配了*/

         .id              =-1,

         .num_resources  = 0,

};

 

然後把我們定義的gpio_key 跟這個platform_device設備綁定在一起:

arch\arm\mach-mx6\board-mx6q-sabresd.c:

platform_device_add_data(&sabresd_button_device,

                    &new_sabresd_button_data,

                             sizeof(new_sabresd_button_data));

 

最後註冊到系統中去:

arch\arm\mach-mx6\board-mx6q-sabresd.c:

platform_device_register(&sabresd_button_device);

 

其中, platform_device_add_data()函數如下:

drivers/base/platform.c:

intplatform_device_add_data(struct platform_device *pdev, const void *data,

                                 size_t size)

{

         void *d = NULL;

 

         if (data) {

                   d = kmemdup(data, size,GFP_KERNEL); /*分配memory,並把data的內容拷貝進去*/

                   if (!d)

                            return -ENOMEM;

         }

 

         kfree(pdev->dev.platform_data);

         pdev->dev.platform_data = d;  /*把gpio_key綁定到這個platform_device上去*/

         return 0;

}

 

3.      匹配驅動:

當用platform_device_register()把 gpio_key的platform_device的添加到系統中去後, 系統會去匹配是否有合適的驅動, 這裏對應的驅動就是:gpio_keys.c, 很顯然這是一個platform_driver的驅動:

drivers/input/keyboard/gpio_keys.c:

static struct platform_drivergpio_keys_device_driver = {

.probe                = gpio_keys_probe,

.remove            = __devexit_p(gpio_keys_remove),

.driver                = {

           .name       = "gpio-keys",   /*發現沒有, 名字跟設備的名字一模一樣*/

           .owner     = THIS_MODULE,

#ifdef CONFIG_PM

           .pm  = &gpio_keys_pm_ops,

#endif

}

};

系統能找到這個驅動, 主要就是因爲他們的名字都是: gpio-keys, 這個很關鍵。 接下來我們就來分析一下這個驅動, 首先找到設備後, 會調用probe函數, 這裏就是gpio_keys_probe();

在gpio_keys_probe()函數中, 會註冊一個input設備, 並創建相應的設備文件。

 

二. GPIO_KEY使用

使用方式比較簡單,和普通的文件操作一樣, 先打開設備文件, 再讀文件獲取鍵值即可:

1.      打開設備文件,

我的設備上gpio_key對應的設備文件是/dev/input/event0, 不同的平臺設備文件可能會有差異, 如果不清楚對應的設備文件, 可以用下面的命令來查看:

 

打開設備代碼如下:

/*1.key device*/

fd_key= open(KEY_DEVICE_FILE, O_RDONLY);

if(fd_key< 0) {

           LOGE("can'topen key device file");

           returnfd_key;

}

 

2.      獲取按鍵值及按鍵類型:

 

         struct input_event key_evt;

 

monitor_key:

         ret = read(fd_key, (unsigned char*)&key_evt, sizeof(struct input_event)); /*阻塞型讀函數*/

         if(ret < 0) {

                   LOGE("read key eventfailed :%d", ret);

         }

         /*filter unknown key*/

         else if(key_evt.code != 248&&     /*KEY_MENU*/ 

                             key_evt.code != 249 &&    /*KEY_UP*/

                             key_evt.code != 250 &&    /*KEY_DOWN*/

                             key_evt.code != 251 &&    /*KEY_CONFIRM*/

                             key_evt.code != 247){     /*ICCARD detect pin*/

                   LOGE("unknown key code:%d", key_evt.code);

                   goto monitor_key;

         }

         else {  /*valid key*/

           /*

            * key_val[0..7] = key code

            * key_val[8] = key value: 0 - released, 1 - pressed.

            */

                   key_val = ((int8_t)key_evt.value<< 8) | ((uint8_t)key_evt.code);

                   //LOGE("get key eventcode:%d, value:%d, type:%d, %d", key_evt.code, key_evt.value,key_evt.type, key_val);

         }

 

         return key_val;

 

實際上就是調用一個阻塞型的讀函數, 所以這個函數儘量放在單獨的一個線程中處理, 鍵值就是在前面板級支持包中定義並註冊的值。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章