现在的位置: 首页 > 综合 > 正文

linux_davinci按键驱动

2018年02月07日 ⁄ 综合 ⁄ 共 5177字 ⁄ 字号 评论关闭

     linux_davinci按键驱动 2013-05-07 09:59:31
    分类: LINUX
    
    一、概述
    该按键驱动原理虽简单,但是在处理中却运用到了Linux驱动中中断的一些关键技术,比如“顶半部”和“底半部”使用,等待队列的设置。
    这里“顶半部”即中断处理函数运行时间很短,基本就做了两件事:1、关中断;2、调用定时器。具体代码如下:
    
    二、需要的结构定义
    #define STATUS_NOKEY 0 //无按键状态,按键抬起
    #define STATUS_DOWNX 1 //有按键状态,但不确定
    #define STATUS_DOWN 2 //等释放状态,确定按下
    
    #define BUF_HEAD (keydev.buf[keydev.head])
    #define BUF_TAIL (keydev.buf[keydev.tail])
    #define INCBUF(x,mod) ((++(x)) & ((mod)-1))
    #define MAX_BUTTON_BUF    16 //按键缓冲区大小
    #define BUTTON_MAJOR    232         // major device NO./* 主设备号 */
    #define DEVICE_NAME    "Key-IN1" /*定义设备驱动的名字,或设备节点名称*/
    #define RTU_KEY_IN1    GPIO_TO_PIN(6, 10)
    
    struct button_irq_desc {
        int irq;
        unsigned long flags;
        char *name;
    };
    
    /* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
    static struct button_irq_desc button_irqs[] = {
        {gpio_to_irq(RTU_KEY_IN1),IRQF_TRIGGER_FALLING, "KEY_IN1"}, /* K1 */
    };
    
    //按键结构体
    typedef struct key_dev{
        unsigned int keyStatus; //按键状态    
        unsigned char buf[MAX_BUTTON_BUF];//按键缓冲区    
        unsigned int head,tail;//按键缓冲区头和尾    
        wait_queue_head_t wq;//等待队列    
        struct timer_list key_timer;//按键去抖定时器    
        struct cdev cdev;//cdev结构体
    } KEY_DEV;
    
    static KEY_DEV keydev;
    static struct class *buttons_class;
    
    三、具体实现
    static unsigned char get_key(int irqno)
    { 
        return (gpio_get_value(RTU_KEY_IN1) >> 10);//返回的数值是寄存器IN_DATA67的数值,取第10位的值,即GPIO6_10的数值
    }
    
    static void (*keyEvent)(unsigned long key);
    
    static void keyEvent_raw(unsigned long key)
    {
        BUF_HEAD=key;//添加到buffer头
        keydev.head=INCBUF(keydev.head,MAX_BUTTON_BUF);
        wake_up_interruptible(&keydev.wq);//唤醒等待队列
    }
    
    static irqreturn_t buttons_interrupt(int irq,void *dev_id)
    {
    //    disable_irqs();//关闭中断,转入查询状态
        keydev.keyStatus=STATUS_DOWNX;//转为不确定状态
        keydev.key_timer.data=get_key(irq)|(irq<<8);//低8位存放键值,高位存放中断号
        keydev.key_timer.expires=jiffies+HZ/50;//延迟20ms
        add_timer(&keydev.key_timer);//启动定时器
        return IRQ_HANDLED;
    }
    
    static int buttons_open(struct inode *inode,struct file *file)
    {
        int i;
        int ret=0;
        
        for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
        {    // 注册中断处理函数,设置GP6[10]IO口为中断下降沿有效的触发方式 
            ret = request_irq(gpio_to_irq(RTU_KEY_IN1), buttons_interrupt, button_irqs[i].flags, button_irqs[i].name, (void *)&button_irqs[i]);
            if(ret)
            {
                break;
            }
        }
        
        if(ret)
        {//中断申请失败处理,释放已经注册的中断
            i--;        
            for(; i>= 0; i--)
                free_irq(gpio_to_irq(RTU_KEY_IN1), (void *)&button_irqs[i]);        
            return -EBUSY;
        }
        
        keydev.head=keydev.tail=0;//清空按键动作缓冲区
        keyEvent=keyEvent_raw; //函数指针指向按键处理函数
        return 0;
    }
    
    
    static void key_timer_handler(unsigned long key)
    {
        if( get_key(key) == (key & 0xff) )
        {//仍处于按下状态
            if(keydev.keyStatus==STATUS_DOWNX){//从中断进入
                keydev.keyStatus=STATUS_DOWN;
                keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
                keyEvent(key & 0xff);//记录键值,唤醒等待队列
                add_timer(&keydev.key_timer);
            }
            else{//keyStatus=STATUS_DOWN
                keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
                add_timer(&keydev.key_timer);
            }
        }
        else{//键已抬起
            keydev.keyStatus=STATUS_NOKEY;
    //        enable_irqs();//使能irq
        }
    }
    
    static void keyEvent_dummy(unsigned long key) {}
    
    static int buttons_close(struct inode *inode,struct file *filp)
    {
        keyEvent=keyEvent_dummy;//函数指针指向空函数
        return 0;
    }
    
    static unsigned char keyRead(void)
    {
        unsigned char key_ret;
        key_ret=BUF_TAIL;
        keydev.tail=INCBUF(keydev.tail,MAX_BUTTON_BUF);
        return key_ret;
    }
    
    static ssize_t buttons_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
    {
        static unsigned char key_ret;
        unsigned long flag; 
    retry:
        if(keydev.head!=keydev.tail){//当前循环队列中有数据
            local_irq_save(flag); //进入临界区,关闭中断 
            key_ret=keyRead();//读取按键
            local_irq_restore(flag); //退出临界区 
            copy_to_user(buffer,(char *)&key_ret,1);
            return 1;
        }
        else {
            if(filp->f_flags & O_NONBLOCK) //若用户采用非阻塞方式读取
                return -EAGAIN;
            interruptible_sleep_on(&keydev.wq);//采用阻塞方式读取
            goto retry;
        }
        return 0;
    }
    
    
    static struct file_operations buttons_fops =
    {
        .owner = THIS_MODULE,
        .open = buttons_open, /*open()*/
        .release = buttons_close, /*release()*/
        .read = buttons_read, /*read()*/
    }; 
    
    static void buttons_setup_cdev(void)/* 初始化并注册cdev */
    {
        int err,devno = MKDEV(BUTTON_MAJOR,0);
        cdev_init(&keydev.cdev,&buttons_fops);
        keydev.cdev.owner = THIS_MODULE;
        keydev.cdev.ops = &buttons_fops;
        err = cdev_add(&keydev.cdev, devno, 1);
        if (err)
            printk(KERN_NOTICE "Error %d adding utukey", err);
    }
    static struct gpio gpios_6_array[] =
    {
        { GPIO_TO_PIN(6, 10), GPIOF_IN,"RTU_KEY_IN1"},
    };
    
    static int __init buttons_init(void)
    {
        int result;
        dev_t devno = MKDEV(BUTTON_MAJOR,0);//用主次设备号生成设备号
        
        //申请GPIO,并设置为输入
        int ret = gpio_request_array(gpios_6_array, ARRAY_SIZE(gpios_6_array));
        if (ret < 0)
        {
            printk(KERN_ALERT "Cannot open GPIO 6_array\n");
            gpio_free_array(gpios_6_array, ARRAY_SIZE(gpios_6_array)); 
        }
        
        /* 申请设备号 */
        if (BUTTON_MAJOR)
            result = register_chrdev_region(devno, 1, DEVICE_NAME);
        else//动态申请设备号
        {
            result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
            int button_major = MAJOR(devno);
            printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major);
        } 
        if (result < 0)
            return result;
        
        buttons_setup_cdev();
        keydev.head=keydev.tail=0;//初始化按键缓冲区
        keydev.keyStatus=STATUS_NOKEY;//初始化按键状态
        init_waitqueue_head(&keydev.wq);//初始化等待队列
        init_timer(&keydev.key_timer);//初始化定时器,实现软件去抖
        keydev.key_timer.function=key_timer_handler;
        
        //生成sysfs文件系统所需的class和属性文件
        buttons_class = class_create(THIS_MODULE,DEVICE_NAME);
        if(IS_ERR(buttons_class))
        {
            printk(KERN_ALERT"err:fail in buttons_class!\n");
            return -1;
        }
        device_create(buttons_class,NULL,MKDEV(BUTTON_MAJOR,0),NULL,DEVICE_NAME);
        printk(KERN_WARNING"buttons Module initialed!\n");
    }
    
    static void __exit buttons_exit(void)
    {
        cdev_del(&keydev.cdev); // 注销cdev
        unregister_chrdev_region(MKDEV(BUTTON_MAJOR, 0), 1); // 释放设备号
        device_destroy(buttons_class,MKDEV(BUTTON_MAJOR,0));
        class_destroy(buttons_class);
    }
    
    module_init(buttons_init);
    module_exit(buttons_exit);
    
    MODULE_AUTHOR("WBL"); 
    MODULE_DESCRIPTION("Davinci BUTTON Driver"); 
    MODULE_LICENSE("GPL");

               

抱歉!评论已关闭.