首页 > 其他分享 >关于正点原子input子系统,驱动中按键中断只检测了上升或下降沿却可以实现连按(EV_REP)的原因

关于正点原子input子系统,驱动中按键中断只检测了上升或下降沿却可以实现连按(EV_REP)的原因

时间:2024-08-31 20:47:06浏览次数:12  
标签:include keyinput REP dev irqkey KEY input EV

问题

在学习到Linux内核input子系统时,产生了一个疑惑。可以看到,我们改造按键中断驱动程序(请见keyinputdriver.c(内核驱动代码)),通过检测按键的上升沿和下降沿,在中断处理函数(上半部内)通过mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20))函数启动定时器。在定时器处理函数中上报和同步按键事件(EV_KEY和EV_REP)。那么问题来了,驱动中,按键中断是上升沿和下降沿触发,当连按的时候,中断只触发了一次,为什么会一直上报的呢?

keyinputdriver.c(内核驱动代码)点击查看代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/irq.h>
#include <linux/of_irq.h>
#include <linux/types.h>
#include <linux/atomic.h>
#include <linux/string.h>
#include <linux/interrupt.h>
#include <asm/mach/map.h> 
#include <asm/uaccess.h> 
#include <asm/io.h>
#include <linux/input.h>

#define KEYINPUT_CNT        1
#define KEYINPUT_NAME       "keyinput"
#define KEY_NUM             1
#define KEY0_VALUE          0X01
#define INVAKEY             0XFF


struct irq_keydesc
{
    int gpio;                                   /* io编号 */
    int irqnum;                                 /* 中断号 */
    unsigned char value;                        /* 键值 */
    char name[10];                              /* 名字 */
    irqreturn_t (*handler)(int, void*);         /* 中断处理函数 */

};


struct keyinput_dev
{
    struct device_node *nd;
    struct irq_keydesc irqkey[KEY_NUM];
    struct timer_list timer;
    
    struct input_dev *inputdev;
};
struct keyinput_dev keyinput;


/* 按键处理函数 */
static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct keyinput_dev *dev = dev_id;

    dev->timer.data = (volatile unsigned long)dev;
    /* 开始定时 */
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(20));
    
    return IRQ_HANDLED;
}

/* 定时器处理函数 */
static void timer_func(unsigned long arg)
{
    int value = 0;
    struct keyinput_dev *dev = (struct keyinput_dev*)arg;
    
    value = gpio_get_value(dev->irqkey[0].gpio);
    if (0 == value) /* 按下 */
    {
        // printk("KEY0 press!\r\n");
        input_event(dev->inputdev, EV_KEY, KEY_0, 1);
         
    }
    else if (1 == value) /* 释放 */
    {
        // printk("KEY0 release!\r\n");
        input_event(dev->inputdev, EV_KEY, KEY_0, 0);
        
    }
    input_sync(dev->inputdev);
}

/* 按键初始化 */
static int keyio_init(struct keyinput_dev *dev)
{
    int i = 0;
    int ret = 0;
    /* 按键初始化 */
    dev->nd = of_find_node_by_path("/key");
    if (NULL == dev->nd)
    {
        ret = -EINVAL;
        goto fail_findnode;
    }
    
    for (i = 0; i < KEY_NUM; i++)
    {
        dev->irqkey[i].gpio = of_get_named_gpio(dev->nd, "key-gpios", i);
    }

    for (i = 0; i < KEY_NUM; i++)
    {
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "KEY%d", i);
        gpio_request(dev->irqkey[i].gpio, "key-gpios");
        gpio_direction_input(dev->irqkey[i].gpio);
        /* 按键中断初始化 */
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
#if 0
        dev->irqkey[i].irqnum = irq_of_parse_and_map(dev->nd, i);
#endif // 0         
    }
    
    /* 按键中断初始化 */
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0_VALUE;
    for (i = 0; i < KEY_NUM; i++)
    {
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, 
                    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 
                    dev->irqkey[i].name, 
                    &keyinput);
        if (ret)
        {
            printk("irq %d request failed!\r\n", dev->irqkey[i].irqnum);
            goto fail_irq;
        }
    }

    /* 定时器初始化 */
    init_timer(&dev->timer);
    dev->timer.function = timer_func;



fail_irq:
    for (i = 0; i < KEY_NUM; i++)
    {
        gpio_free(dev->irqkey[i].gpio);
    }
fail_findnode:
    return ret;
}

static int __init keyinput_init(void)
{
    int ret = 0;

    /* 1、初始化io key */
    ret = keyio_init(&keyinput);
    if (ret < 0)
    {
        goto fail_keyinit;
    }

    /* 2、注册input_dev*/
    keyinput.inputdev = input_allocate_device();
    if (NULL == keyinput.inputdev)
    {
        ret = -EINVAL;
        goto fail_keyinit;
    }

    keyinput.inputdev->name = KEYINPUT_NAME;
    __set_bit(EV_KEY, keyinput.inputdev->evbit);    //设置按键事件
    __set_bit(EV_REP, keyinput.inputdev->evbit);    //设置重复事件
    __set_bit(KEY_0, keyinput.inputdev->keybit);    //设置key0

    ret = input_register_device(keyinput.inputdev);
    if (ret)
    {
        goto fail_input_register;
    }
    
    return 0;
fail_input_register:
    input_free_device(keyinput.inputdev);
fail_keyinit:
    return ret;
}


static void __exit keyinput_exit(void)
{
    int i = 0;
    /* 1、删除定时器 */
    del_timer_sync(&keyinput.timer);

    /* 2、释放中断 */
    for (i = 0; i < KEY_NUM; i++)
    {
        free_irq(keyinput.irqkey[i].irqnum, &keyinput);
    }
    /* 3、释放GPIO */
    for (i = 0; i < KEY_NUM; i++)
    {
        gpio_free(keyinput.irqkey[i].gpio);
    }

    /* 4、注销input_dev */
    input_unregister_device(keyinput.inputdev);
    input_free_device(keyinput.inputdev);
}


module_init(keyinput_init);
module_exit(keyinput_exit);

MODULE_AUTHOR("tyler");
MODULE_LICENSE("GPL");
keyinputAPP.c(测试APP代码)点击查看代码
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <poll.h> 
#include <sys/select.h> 
#include <sys/time.h> 
#include <signal.h> 
#include <fcntl.h>
#include <linux/input.h>
/*
 * ./keyinputAPP <filename> <0/1>
 * ./keyinputAPP /dev/input/event2  
 * 
 */


static struct input_event inputevent;

int main(int argc, char* argv[])
{
    int fd, err;
    char * filename;

    if (argc != 2)
    {
        printf("args num error!!\r\n");
        return -1;
    }
    
    filename = argv[1];

    fd = open(filename, O_RDWR);
    if (fd < 0)
    {
        printf("open error\r\n");
        return -1;
    }
    while (1)
    {
        err = read(fd, &inputevent, sizeof(inputevent));
        if (err > 0) /* 数据读取成功 */
        {
            switch (inputevent.type)
            {
            case EV_KEY:
                if (inputevent.code < BTN_MISC)
                {
                    printf("key %d %s\r\n", inputevent.code, inputevent.value ? "press":"release");
                }
                else
                {
                    printf("button %d %s\r\n", inputevent.code, inputevent.value ? "press":"release");
                }
                
                printf("EV_KEY事件\r\n");
                break;
            case EV_SYN:
                
                break;
            case EV_REL:
                break;
            case EV_ABS:
                break;
            };

        }
        else
        {
            printf("读取失败!\r\n");
        }
        
    }
    

    close(fd);
    return 0;
}


input输入系统实现按键重复的方式

简单来说,在input的子系统中,实现按键重复的方式是启动了一个内核定时器,通过保存按键值和上一次的值进行比较来检测按键是否按下,如果没用检测到释放按键,那么就会不断地利用mod_timer来启动定时器,进行按键重复上报。当释放了按键,那么就不会重复启动定时器,即停止了按键重复事件。

参考资料:https://blog.51cto.com/assassinwu/1080111 (input输入系统中是如何实现按键重复 )

标签:include,keyinput,REP,dev,irqkey,KEY,input,EV
From: https://www.cnblogs.com/tylerw/p/18390749

相关文章

  • YOLOv8改进 | 模块缝合 | C2f融合卷积重参数化OREPA【CVPR2022】
    秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转......
  • C语言(vs2022、Vc++6.0、DevC++)连接MySql
    本文c++(OraOla编写)与Java(Wideskyzz编写)由于csdn的排版太垃圾了,所以可以直接看资料上传资料也麻烦,所以可直接访问我的giteeC语言连接MySql:C语言(vs2022、Vc++6.0、DevC++)连接MySqlhttps://gitee.com/gyhjim/c-language-connection---my-sql一定要自己实践当你发现与我的......
  • Elsevier 期刊 Expert SystemsWith Applications 投稿经验
    准备材料1.AuthorAgreement:带全部作者的电子签名,证明全部作者对该论文的知情2.CoverLetter:介绍该文章的工作内容,不是简单的把摘要复制过来3.ORCIDInformation:写上全部作者姓名以及对应的orcid4.Highlights:3-5点即可,这里需要注意字数5.CreditAuthorStatement:说明每一......
  • OpenVoiceV2:零样本跨语言语音克隆技术,支持多种风格控制
    1openvoicev2介绍语音克隆技术近年来取得了显著进展,但现有方法通常存在着局限性,例如无法灵活控制语音风格、需要大量多语言数据进行训练、生成速度慢等等。为了克服这些挑战,MyShell.ai团队推出了全新的语音克隆技术OpenVoiceV2,它能够在无需额外训练的情况下,仅凭少量参考音频......
  • 【RK3588】关于 devfreq 和 cpufreq 的记录
    前言本文主要介绍了/sys/class/devfreq和/sys/devices/system/cpu/cpufreq目录,以及如何手动管理和监控设备频率和CPU频率。同时提供了简单的Python脚本,用于打印设备和CPU的频率信息。环境信息:硬件:FriendlyNanoPi-R6S固件:rk3588-usb-debian-bullseye-minimal-6......
  • Versal 自适应 SoC,XCVM2502-1MSEVSVC2197、XCVM2502-1LSEVSVC2197、XCVM2502-1LLIVSVI
    Prime 系列概述VersalPrime系列是一款高度集成、多核、异构计算平台,适用于数据中心网络、存储和有线通信等多种应用。它通过在优化了连接性的设备中实现低延迟的内联加速,为这些应用提供突破性的性能。 应用:存储加速数据中心网络加速5GxHaul无源光网络通......
  • 使用devpi-server搭建pypi本地缓存服务器
    使用缓存机制可以显著减少对外部源的请求量,从而提高下载速度,并降低被源站封禁的风险。下面详细解释如何在本地服务器上设置和使用pip缓存机制。缓存机制的基本原理缓存机制的原理是在本地服务器上保存已经下载过的Python包,当其他服务器请求同样的包时,本地服务器可以直接提供,......
  • FineBI与FineReport的区别
    在企业信息化的浪潮中,数据分析和报表工具的需求日益增加。FineBI与FineReport是帆软公司旗下两款常见的数据分析和报表工具,它们各自有着不同的特点和应用场景。本文将从功能、适用场景和用户体验等方面分析FineBI与FineReport的区别。一、功能对比FineBI数据分析:FineBI......
  • Linux驱动学习之input子系统
    简介input子系统就是管理输入的子系统,和pinctrl、gpio子系统一样,都是Linux内核针对某一类设备而创建的框架。按键、鼠标、键盘、触摸屏等都属于输入设备,linux内核为此专门做了一个叫做input子系统的框架来处理输入事件。输入设备本质上还是字符设备,只是在此基础上套上了......
  • .NET 开源报表神器 Seal-Report
    前言Seal-Report是一款.NET开源报表工具,拥有1.4KStar。它提供了一个完整的框架,使用C#编写,最新的版本采用的是.NET8.0。它能够高效地从各种数据库或NoSQL数据源生成日常报表,并支持执行复杂的报表任务。其简单易用的安装过程和直观的设计界面,我们能够在几分钟内创建并......