• linux输入子系统下按键驱动编写


    学习目的:

    • 熟悉输入子系统下按键驱动程序编写

    在分析输入子系统框架时,我们已经知道内核对不同类型的输入设备已经抽象出了不同的handler进行处理,device层实现纯硬件的操作,我们可以根据所实现驱动的功能对device层进行设计,主要是内容是当检测有效输入发送,调用input_event函数向handler层上报结果即可。device部分驱动程序设计主要分为以下几个步骤:

    ① 初始化硬件,注册input_dev设备

    ② 有效按键按下,向handler上报按键值

    现在我们开始使用内核的输入子系统框架,将自己编写驱动程序融入其中,编写更通用的按键驱动程序。我们编写的驱动程序实现将开发板上的4个按键分别映射成键盘上不同键值,代表键盘上l、s、shift、enter值。

    1、驱动入口函数

    static int button_drv_init(void)
    {
        unsigned int i = 0;
        
        init_timer(&button_timer);-------------------------->①
        button_timer.expires = 0;
        button_timer.function = button_timer_handler;
        add_timer(&button_timer);
    
        /* 分配一个input_dev结构体 */
        input = input_allocate_device();-------------------->②
        
        /* 产生哪一类事件 */
        set_bit(EV_KEY, input->evbit);
        
        /* 产生该类事件的那些事件 */
        set_bit(KEY_L, input->keybit);
        set_bit(KEY_S, input->keybit);
        set_bit(KEY_ENTER, input->keybit);
        set_bit(KEY_LEFTSHIFT, input->keybit);
        
        gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);------------->③
        gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
        gpfdat = gpfcon + 1;
        gpgdat = gpgcon + 1;
        
        *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
        *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
        
        /* 注册中断处理函数 */
        for(i = 0; i < BUTTON_NUMS; i++)---------------------->④
            request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]);
        
        /* 注册input_dev结构体 */
        input_register_device(input);------------------------->⑤
        
        return 0;
    }

    ① 初始化定时器,用于按键消抖获取稳定按键值使用

    ② 分配一个input_dev结构体,设置当前device能产生按键类事件,以及产生该类事件的那些按键值

    ③ 硬件相关操作:芯片相关gpio寄存器地址映射,设置连接按键的GPIO引脚为输入模式

    ④ 注册中断服务程序,设置中断触发方式以及中断发送时的中断服务函数

    ⑤ 注册input_dev结构体,找到内核中所有匹配的handler,并建立dev和handler直接连接

    2、中断服务程序

    static irqreturn_t button_irq_handle(int irq, void *dev_id)
    {    
        pdesc = (struct button_desc *)dev_id;
        
        mod_timer(&button_timer, jiffies+HZ/100);
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }

    按键按下时,由于按键内部机械弹片抖动引起GIPO口电平跳变进入中断服务程序,此时读取按键值为非有效值。为按键消抖,设置消抖时间,消抖时间到时在定时器的超时处理函数中获取有效按键值

    3、定时器超时处理程序

    void button_timer_handler(unsigned long data)
    {
        unsigned char pin_val;
        unsigned int  key_sts;
        
        if(pdesc != NULL)
        {
            pin_val = gpio_get_value(pdesc->pin);---------------------------->①
            if(pin_val == 1)
            {
                /* 1-按下 */
                key_sts = 1;
            }
            else
            {
                /* 0-松开 */
                key_sts = 0;
            }
            input_event(input, EV_KEY, pdesc->key_val, key_sts);------------>②
            input_sync(input);---------------------------------------------->③
        }
    }

    ① 读取触发中断的按键的GPIO的状态,确定按键是按下还是松开

    ② 调用input_event上报事件,input_event最终会调用handler中的event函数,event函数中将有效输入传送到handler中的相应存储区域,并唤醒因无数据而读取进入休眠的应用程序

    ③ 当提交输入设备产生的输入事件之后,需要调用此函数来通知输入子系统,以处理设备产生的完整事件

    4、驱动程序测试

    加载驱动程序,/dev目录下多出event1设备节点,该节点是device和内核中的handler匹配成功,调用handler中的connect函数input_class类下创建的设备。udev机制会使用这些信息在dev目录中创建相关节点

    执行exec 0</dev/tty1命令,重定向shell程序的标准输入来自于/dev/tty1,这样按键产生的ls命令就能在中断显示了

     完整驱动程序

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/irq.h>
    #include <asm/uaccess.h>
    #include <asm/irq.h>
    #include <asm/io.h>
    #include <plat/gpio-fns.h>
    #include <mach/gpio-nrs.h>
    #include <linux/interrupt.h>
    #include <linux/wait.h>
    #include <linux/sched.h>
    #include <linux/device.h>
    #include <linux/gpio.h>
    #include <linux/poll.h>
    #include <linux/input.h>
    
    #define BUTTON_NUMS    4
    #define IRQT_BOTHEDGE IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
    
    struct button_desc
    {
        int pin;
        int irq_type;
        unsigned long flags;
        char *name;
        int key_val;
    };
    
    static volatile unsigned long *gpfcon = NULL;
    static volatile unsigned long *gpgcon = NULL;
    static volatile unsigned long *gpfdat = NULL;
    static volatile unsigned long *gpgdat = NULL;
    
    
    
    static struct timer_list button_timer;
    
    static struct input_dev *input;
    
    
    struct button_desc *pdesc = NULL;
    
    static struct button_desc btn_desc[BUTTON_NUMS] = {
        {S3C2410_GPF(0),  IRQ_EINT0,  IRQT_BOTHEDGE, "S2", KEY_L},
        {S3C2410_GPF(2),  IRQ_EINT2,  IRQT_BOTHEDGE, "S3", KEY_S},
        {S3C2410_GPG(3),  IRQ_EINT11, IRQT_BOTHEDGE, "S4", KEY_ENTER},
        {S3C2410_GPG(11), IRQ_EINT19, IRQT_BOTHEDGE, "S5", KEY_LEFTSHIFT},
    };
    
    
    void button_timer_handler(unsigned long data)
    {
        unsigned char pin_val;
        unsigned int  key_sts;
        
        if(pdesc != NULL)
        {
            pin_val = gpio_get_value(pdesc->pin);    
            if(pin_val == 1)
            {
                /* 1-按下 */
                key_sts = 1;
            }
            else
            {
                /* 0-松开 */
                key_sts = 0;
            }
            input_event(input, EV_KEY, pdesc->key_val, key_sts);
            input_sync(input);
        }
    }
    
    static irqreturn_t button_irq_handle(int irq, void *dev_id)
    {    
        pdesc = (struct button_desc *)dev_id;
        
        mod_timer(&button_timer, jiffies+HZ/100);
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }
        
    static int button_drv_init(void)
    {
        unsigned int i = 0;
        
        init_timer(&button_timer);
        button_timer.expires = 0;
        button_timer.function = button_timer_handler;
        add_timer(&button_timer);
    
        /* 分配一个input_dev结构体 */
        input = input_allocate_device();
        
        /* 产生哪一类事件 */
        set_bit(EV_KEY, input->evbit);
        
        /* 产生该类事件的那些事件 */
        set_bit(KEY_L, input->keybit);
        set_bit(KEY_S, input->keybit);
        set_bit(KEY_ENTER, input->keybit);
        set_bit(KEY_LEFTSHIFT, input->keybit);
        
        gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
        gpgcon = (volatile unsigned long *)ioremap(0x56000060, 16);
        gpfdat = gpfcon + 1;
        gpgdat = gpgcon + 1;
        
        *gpfcon &= ~((0x3<<(0*2)) | (0x3<<(2*2)));
        *gpgcon &= ~((0x3<<(3*2)) | (0x3<<(11*2)));
        
        /* 注册中断处理函数 */
        for(i = 0; i < BUTTON_NUMS; i++)
            request_irq(btn_desc[i].irq_type, button_irq_handle, btn_desc[i].flags, btn_desc[i].name, &btn_desc[i]);
        
        /* 注册input_dev结构体 */
        input_register_device(input);
        
        return 0;
    }
    
    static void button_drv_exit(void)
    {
        unsigned int i = 0;
        
        del_timer(&button_timer);
        
        for(i = 0; i < BUTTON_NUMS; i++)
            free_irq(btn_desc[i].irq_type, &btn_desc[i]);
        
        input_unregister_device(input);
        input_free_device(input);
        
        iounmap(gpfcon);
        iounmap(gpgcon);
    }
    
    module_init(button_drv_init);
    module_exit(button_drv_exit);
    
    MODULE_LICENSE("GPL");
    input_dev
  • 相关阅读:
    学习完vue指令 做的一个学生信息录入系统
    vue常用指令总结
    vue介绍以及相关概念理解大全
    class类
    javascript闭包详解
    前端必看网站(一直更新中)
    微信小程序wx.getUserInfo获取用户信息
    微信小程序自定义组件注意事项
    微信小程序自定义选项卡
    uni-app开发注意事项及常用样式
  • 原文地址:https://www.cnblogs.com/053179hu/p/13799390.html
Copyright © 2020-2023  润新知