OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6251|回复: 5

input子系统的实现历程分享

[复制链接]

3

主题

8

帖子

0

精华

新手上路

积分
35
金钱
35
注册时间
2020-10-27
在线时间
9 小时
发表于 2020-11-6 13:58:42 | 显示全部楼层 |阅读模式
基于原子教程的input子系统的学习历程
修改部分为:由一个按键改为两个按键,涉及gpio的操作、定时器的初始化和注册等都有变动
学习所得:通过input子系统的学习,把按键、定时器、设备树又进行了一次复习,又有了不一样的理解,代码中我有加粗的部分,供大家参考。具体代码如下:
设备树部分如下:
  1.   mytest_key {
  2.         compatible = "mykey_gpio";
  3.         pinctrl-names = "default";
  4.         pinctrl-0 = <&myGPIO_key0
  5.                                 &myGPIO_key1>;
  6.         gpios = <&gpio5 1 GPIO_ACTIVE_LOW
  7.                          &gpio4 14 GPIO_ACTIVE_LOW>;
  8.     };
复制代码



驱动部分:
  1. #include <linux/types.h>
  2. #include <linux/kernel.h>
  3. #include <linux/delay.h>
  4. #include <linux/ide.h>
  5. #include <linux/init.h>
  6. #include <linux/module.h>
  7. #include <linux/errno.h>
  8. #include <linux/gpio.h>
  9. #include <linux/cdev.h>
  10. #include <linux/device.h>
  11. #include <linux/of.h>
  12. #include <linux/of_address.h>
  13. #include <linux/of_gpio.h>
  14. #include <linux/input.h>
  15. #include <linux/semaphore.h>
  16. #include <linux/timer.h>
  17. #include <linux/of_irq.h>
  18. #include <linux/irq.h>
  19. #include <asm/mach/map.h>
  20. #include <asm/uaccess.h>
  21. #include <asm/io.h>


  22. #define KEYINPUT_NAME                "keyinput"        /* 名字                 */
  23. #define KEY0VALUE                        0X01                /* KEY0按键值         */
  24. #define INVAKEY                                0XFF                /* 无效的按键值 */
  25. #define KEY_NUM                                2                        /* 按键数量         */


  26. struct irq_keydesc{
  27.         unsigned                char        value;        /*键值*/
  28.         int gpio;
  29.         int flags;
  30.         struct gpio_desc *gpio_key;       
  31.         int irqnum;                                                                /* 中断号     */
  32.         irqreturn_t (*handler)(int, void *);        /* 中断服务函数 */
  33. };



  34. struct input_key_test{
  35.         dev_t devid;                                /* 设备号          */
  36.         struct cdev cdev;                        /* cdev         */
  37.         struct class *class;                /* 类                 */
  38.         struct device *device;                /* 设备          */
  39.         struct device_node        *nd;         /* 设备节点 */
  40.         struct irq_keydesc *irqkeydesc;        /* 按键描述数组 */
  41.         struct timer_list timer;        /* 定义一个定时器*/
  42.         unsigned char curkeynum;                                /* 当前的按键号 */
  43.         struct input_dev *inputdev;                                /* input结构体 */
  44. };
  45. static        struct input_key_test        keyinputdev;

  46. static irqreturn_t        key_handle0(int irq, void *dev_id)
  47. {
  48.         struct input_key_test *dev = (struct input_key_test *)dev_id;
  49. //        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

  50.         dev->timer.data = (unsigned long)dev_id;
  51.         dev->curkeynum =0;
  52.         mod_timer(&dev->timer , jiffies + HZ/5);        /*延时去抖*/
  53.         return IRQ_HANDLED;
  54. }
  55. static irqreturn_t        key_handle1(int irq, void *dev_id)
  56. {
  57.         struct input_key_test *dev = (struct input_key_test *)dev_id;
  58. //        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);

  59.         dev->timer.data = (unsigned long)dev_id;
  60.         dev->curkeynum =1;
  61.         mod_timer(&dev->timer , jiffies + HZ/5);        /*延时去抖*/
  62.         return IRQ_HANDLED;
  63. }
  64. static void        timer_handle0(unsigned long arg)
  65. {
  66.         int        num;
  67.         int        key;
  68.         struct input_key_test *dev = (struct input_key_test *)arg;
  69.         struct irq_keydesc *keydesc;
  70. //        printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
  71. //        printk("key num = %d\n",dev->curkeynum);
  72.         num = dev->curkeynum;
  73.         keydesc = &dev->irqkeydesc[num];
  74.         key = gpiod_get_value(keydesc->gpio_key );
  75. //        printk("key is  %d\n",key);
  76.         if(key == 0){                                                 /* 按下按键 */
  77.                 /* 上报按键值 */
  78.                 //input_event(dev->inputdev, EV_KEY, keydesc->value, 1);
  79.                 input_report_key(dev->inputdev, keydesc->value, 1);/* 最后一个参数表示按下还是松开,1为按下,0为松开 */
  80.                 input_sync(dev->inputdev);
  81.         } else {                                                                         /* 按键松开 */
  82.                 //input_event(dev->inputdev, EV_KEY, keydesc->value, 0);
  83.                 input_report_key(dev->inputdev, keydesc->value, 0);
  84.                 input_sync(dev->inputdev);
  85.         }       
  86. }

  87. static        int        keyio_init(void)
  88. {
  89.         int i;
  90.         int err;
  91.         int count;
  92.         enum of_gpio_flags flag;
  93.         /*获取设备树信息*/
  94.         keyinputdev.nd = of_find_node_by_path("/mytest_key");        /*获取设备节点*/
  95.         if(keyinputdev.nd == NULL)
  96.         {
  97.                 printk("key node not find!\r\n");
  98.                 return -1;
  99.         }
  100.         printk("keyinputdev.nd name is %s\n",keyinputdev.nd->name);
  101. <div><b>        count = of_gpio_count(keyinputdev.nd);//此处注意点:of_gpio_count在匹配设备树时,是匹配的gpios ,由于前期用的是gpiod_get这个函数,对应的设备树中需要写成“key-gpios”,所以我在这个地方被自己给坑了很久</b></div><div><b>//另外在写驱动的时候,对函数的返回值最好做判断,就比如对count我之前没有做判断(设备树中还是key-gpios),导致count的返回值是 -2,而我又没有对count的返回判断(即,没有下面的if(count<0)这个判断),加载模块就挂掉,内核反馈的错误</b></div><div><b>//代码也看不懂,只能重启,修改代码,测试,挂掉,重启......循环下去</b></div>        <b></b><b></b>
  102.         if(count < 0)
  103.         {
  104.                 printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
  105.                 return -1;
  106.         }
  107.         /*
  108.                 内存分配
  109.                 请求大小:gpio个数 * 一个结构体的大小
  110.                 返回:申请内存的指针,正好给结构体指针
  111.         */
  112.         keyinputdev.irqkeydesc = kmalloc(count * sizeof(struct irq_keydesc), GFP_KERNEL);
  113.        
  114.         keyinputdev.irqkeydesc[0].handler = key_handle0;
  115.         keyinputdev.irqkeydesc[1].handler = key_handle1;
  116.         for(i=0 ;i<count;i++)
  117.         {
  118.                 keyinputdev.irqkeydesc[i].gpio         = of_get_gpio_flags(keyinputdev.nd, i, &flag);                                        /*获取gpio*/
  119.                 keyinputdev.irqkeydesc[i].gpio_key = gpio_to_desc(keyinputdev.irqkeydesc[i].gpio);        /*由gpio转换为gpio_desc*/
  120.                 keyinputdev.irqkeydesc[i].flags         = flag & OF_GPIO_ACTIVE_LOW;
  121.                 keyinputdev.irqkeydesc[i].irqnum        = gpio_to_irq(keyinputdev.irqkeydesc[i].gpio);       
  122.                 //keyinputdev.irqkeydesc[i].handler        = key_handle0;
  123.                 /*注册中断*/
  124.                 err = request_irq(keyinputdev.irqkeydesc[i].irqnum, keyinputdev.irqkeydesc[i].handler,IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "key_irq_test0", &keyinputdev);
  125.                 if(err < 0)
  126.                 {
  127.                         printk("irq %d request failed!\r\n", keyinputdev.irqkeydesc[i].irqnum);
  128.                         return -EFAULT;
  129.                 }




  130.         }
  131.         keyinputdev.irqkeydesc[0].value =KEY_0;
  132.         keyinputdev.irqkeydesc[1].value =KEY_1;
  133.        
  134.                 /*设置定时器*/
  135.                
  136.                 setup_timer(&keyinputdev.timer , timer_handle0, (unsigned long)&keyinputdev);
  137.                 keyinputdev.timer.expires = ~0 ;
  138.                 add_timer(&keyinputdev.timer);
  139. #if 0

  140.             init_timer(&keyinputdev.timer);    //初始化定时器
  141.             keyinputdev.timer.function = timer_handle0;    //设置定时器触发时函数
  142.                 keyinputdev.timer.expires = ~0 ;
  143.         //        add_timer(&keyinputdev.timer);
  144. #endif       

  145.         /* 申请input_dev */
  146.         keyinputdev.inputdev = input_allocate_device();

  147.         keyinputdev.inputdev->name                 = KEYINPUT_NAME;
  148.         keyinputdev.inputdev->evbit[0]        = BIT_MASK(EV_KEY);
  149.         for(i=0 ;i<count;i++)
  150.         {
  151.                 input_set_capability(keyinputdev.inputdev, EV_KEY, keyinputdev.irqkeydesc[i].value);        /*设置keybit*/
  152.         }


  153.         /*注册input_dev*/
  154.         err = input_register_device(keyinputdev.inputdev);
  155.         return 0;
  156.        
  157. }

  158. static int __init keyinput_init(void)
  159. {
  160.         keyio_init();
  161.         return 0;
  162. }

  163. /*
  164. * @description        : 驱动出口函数
  165. * [url=home.php?mod=space&uid=271674]@param[/url]                 : 无
  166. * @return                 : 无
  167. */
  168. static void __exit keyinput_exit(void)
  169. {
  170.         int count;
  171.         unsigned int i = 0;
  172.         printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
  173.         /*获取设备树信息*/
  174.         keyinputdev.nd = of_find_node_by_path("/mytest_key");        /*获取设备节点*/
  175.         count = of_gpio_count(keyinputdev.nd);
  176.        
  177.         if(count < 0)
  178.         {
  179.                 printk("%s %s line %d, there isn't any gpio available\n", __FILE__, __FUNCTION__, __LINE__);
  180.                
  181.         }
  182.         /* 删除定时器 */
  183. #if 0
  184.         for(i=0 ;i<count;i++)
  185.         {
  186.                 del_timer(&keyinputdev.irqkeydesc[i].timer );        /* 删除定时器 */
  187.         }       
  188. #endif
  189.         del_timer(&keyinputdev.timer );        /* 删除定时器 */
  190.         /* 释放中断 */
  191.         for (i = 0; i < count; i++) {
  192.                 free_irq(keyinputdev.irqkeydesc[i].irqnum, &keyinputdev);
  193.         }
  194.         /* 释放input_dev */
  195.         input_unregister_device(keyinputdev.inputdev);
  196.         input_free_device(keyinputdev.inputdev);
  197. }

  198. module_init(keyinput_init);
  199. module_exit(keyinput_exit);
  200. MODULE_LICENSE("GPL");


复制代码
应用层和原子的一样,就不上传了。


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

3

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
82
金钱
82
注册时间
2019-7-17
在线时间
38 小时
发表于 2020-11-6 23:23:19 | 显示全部楼层
一个按键改为两个按键
需要修改设备树吗 ?
回复 支持 反对

使用道具 举报

3

主题

8

帖子

0

精华

新手上路

积分
35
金钱
35
注册时间
2020-10-27
在线时间
9 小时
 楼主| 发表于 2020-11-9 08:34:57 | 显示全部楼层
ColorSight 发表于 2020-11-6 23:23
一个按键改为两个按键
需要修改设备树吗 ?

需要的,设备树需要有两个按键。
其实我的理解,设备树就是给资源的,要有两个按键的资源,你才能在驱动中调配这些资源
回复 支持 反对

使用道具 举报

7

主题

76

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
244
金钱
244
注册时间
2016-4-11
在线时间
54 小时
发表于 2021-5-20 10:23:26 | 显示全部楼层
两个按键,的驱动部分,如何进行同时上报,和避免按键冲突
回复 支持 反对

使用道具 举报

86

主题

982

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
1846
金钱
1846
注册时间
2013-4-15
在线时间
163 小时
发表于 2023-9-25 09:42:30 | 显示全部楼层
楼主,问个问题,我看你的按键对应的IO初始化为 上升沿/下降沿 触发的,而且在定时器中断服务函数里面上报的是EV_KEY事件,请问,为啥在当按键按下不放的时候,为啥在应用程序里面可以一直检测到按键按下?定时器服务函数里面你也没有上报 “重复”事件
合肥-文盲
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-25 01:58

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表