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;

 

实际上就是调用一个阻塞型的读函数, 所以这个函数尽量放在单独的一个线程中处理, 键值就是在前面板级支持包中定义并注册的值。

 

你可能感兴趣的:(linux,driver)