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");