分享

linux_davinci按键驱动

 yikongzi 2014-12-24
linux_davinci按键驱动
http://blog./uid-27717694-id-3658419.html

简单的按键驱动,貌似是只有一个键的操作,定义了3种键状态:按下、未按键、按键释放;

整体思路:驱动设计按键事件本身定义为中断,按键事件用队列处理,貌似没有用到消息机制;
中断方面:使用到标准中断模式:顶半部 + 底半部处理;
临界保护:貌似因为一个键操作,没有过分的保护,只有开关中断的操作;

------------------------------
  1. 一、概述
  2. 该按键驱动原理虽简单,但是在处理中却运用到了Linux驱动中中断的一些关键技术,比如“顶半部”和“底半部”使用,等待队列的设置。
  3. 这里“顶半部”即中断处理函数运行时间很短,基本就做了两件事:1、关中断;2、调用定时器。具体代码如下:

  4. 二、需要的结构定义
  5. #define STATUS_NOKEY 0     //无按键状态,按键抬起
  6. #define STATUS_DOWNX 1    //有按键状态,但不确定
  7. #define STATUS_DOWN 2      //等释放状态,确定按下

  8. #define BUF_HEAD (keydev.buf[keydev.head])
  9. #define BUF_TAIL (keydev.buf[keydev.tail])

  10. #define INCBUF(x,mod) ((++(x)) & ((mod)-1))

  11. #define MAX_BUTTON_BUF    16      //按键缓冲区大小
  12. #define BUTTON_MAJOR    232         // major device NO./* 主设备号 */
  13. #define DEVICE_NAME    "Key-IN1"   /*定义设备驱动的名字,或设备节点名称*/
  14. #define RTU_KEY_IN1    GPIO_TO_PIN(6, 10)

  15. struct button_irq_desc {
  16.     int irq;
  17.     unsigned long flags;
  18.     char *name;
  19. };

  20. /* 用来指定按键所用的外部中断引脚及中断触发方式, 名字 */
  21. static struct button_irq_desc button_irqs[] = {
  22.     {gpio_to_irq(RTU_KEY_IN1),IRQF_TRIGGER_FALLING, "KEY_IN1"},            /* K1 */
  23. };

  24. //按键结构体
  25. typedef struct key_dev{
  26.     unsigned int keyStatus;                                    //按键状态    
  27.     unsigned char buf[MAX_BUTTON_BUF];        //按键缓冲区    
  28.     unsigned int head,tail;                                      //按键缓冲区头和尾    
  29.     wait_queue_head_t wq;                                   //等待队列    
  30.     struct timer_list key_timer;                               //按键去抖定时器    
  31.     struct cdev cdev;                                              //cdev结构体
  32. } KEY_DEV;

  33. static KEY_DEV keydev;
  34. static struct class *buttons_class;

  35. 三、具体实现
  36. static unsigned char get_key(int irqno)
  37. {
  38.     return (gpio_get_value(RTU_KEY_IN1) >> 10);//返回的数值是寄存器IN_DATA67的数值,取
  39.                                                                                  第10位的值,即GPIO6_10的数值
  40. }

  41. static void (*keyEvent)(unsigned long key);

  42. static void keyEvent_raw(unsigned long key)
  43. {
  44.     BUF_HEAD=key;                                     //添加到buffer头
  45.     keydev.head=INCBUF(keydev.head,MAX_BUTTON_BUF);
  46.     wake_up_interruptible(&keydev.wq);       //唤醒等待队列
  47. }

  48. static irqreturn_t buttons_interrupt(int irq,void *dev_id)
  49. {
  50. //    disable_irqs();                                                   //关闭中断,转入查询状态
  51.     keydev.keyStatus=STATUS_DOWNX;               //转为不确定状态
  52.     keydev.key_timer.data=get_key(irq)|(irq<<8);    //低8位存放键值,高位存放中断号
  53.     keydev.key_timer.expires=jiffies+HZ/50;            //延迟20ms
  54.     add_timer(&keydev.key_timer);                          //启动定时器
  55.     return IRQ_HANDLED;
  56. }

  57. static int buttons_open(struct inode *inode,struct file *file)
  58. {
  59.     int i;
  60.     int ret=0;
  61.     
  62.     for (i = 0; i <sizeof(button_irqs)/sizeof(button_irqs[0]); i++)
  63.     {    // 注册中断处理函数,设置GP6[10]IO口为中断下降沿有效的触发方式
  64.         ret = request_irq(gpio_to_irq(RTU_KEY_IN1), buttons_interrupt, button_irqs[i].flags, button_irqs[i].name, (void *)&button_irqs[i]);
  65.         if(ret)
  66.         {
  67.             break;
  68.         }
  69.     }
  70.     
  71.     if(ret)
  72.     {//中断申请失败处理,释放已经注册的中断
  73.         i--;        
  74.         for(; i>= 0; i--)
  75.             free_irq(gpio_to_irq(RTU_KEY_IN1), (void *)&button_irqs[i]);        
  76.         return -EBUSY;
  77.     }
  78.     
  79.     keydev.head=keydev.tail=0;//清空按键动作缓冲区
  80.     keyEvent=keyEvent_raw; //函数指针指向按键处理函数
  81.     return 0;
  82. }


  83. static void key_timer_handler(unsigned long key)
  84. {
  85.     if( get_key(key) == (key & 0xff) )
  86.     {//仍处于按下状态
  87.         if(keydev.keyStatus==STATUS_DOWNX){//从中断进入
  88.             keydev.keyStatus=STATUS_DOWN;
  89.             keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
  90.             keyEvent(key & 0xff);//记录键值,唤醒等待队列
  91.             add_timer(&keydev.key_timer);
  92.         }
  93.         else{//keyStatus=STATUS_DOWN
  94.             keydev.key_timer.expires=jiffies+HZ/5;//延迟200毫秒
  95.             add_timer(&keydev.key_timer);
  96.         }
  97.     }
  98.     else{//键已抬起
  99.         keydev.keyStatus=STATUS_NOKEY;
  100. //        enable_irqs();//使能irq
  101.     }
  102. }

  103. static void keyEvent_dummy(unsigned long key) {}

  104. static int buttons_close(struct inode *inode,struct file *filp)
  105. {
  106.     keyEvent=keyEvent_dummy;//函数指针指向空函数
  107.     return 0;
  108. }

  109. static unsigned char keyRead(void)
  110. {
  111.     unsigned char key_ret;
  112.     key_ret=BUF_TAIL;
  113.     keydev.tail=INCBUF(keydev.tail,MAX_BUTTON_BUF);
  114.     return key_ret;
  115. }

  116. static ssize_t buttons_read(struct file *filp,char *buffer,size_t count,loff_t *ppos)
  117. {
  118.     static unsigned char key_ret;
  119.     unsigned long flag;
  120. retry:
  121.     if(keydev.head!=keydev.tail){//当前循环队列中有数据
  122.         local_irq_save(flag); //进入临界区,关闭中断
  123.         key_ret=keyRead();//读取按键
  124.         local_irq_restore(flag); //退出临界区
  125.         copy_to_user(buffer,(char *)&key_ret,1);
  126.         return 1;
  127.     }
  128.     else {
  129.         if(filp->f_flags & O_NONBLOCK) //若用户采用非阻塞方式读取
  130.             return -EAGAIN;
  131.         interruptible_sleep_on(&keydev.wq);//采用阻塞方式读取
  132.         goto retry;
  133.     }
  134.     return 0;
  135. }


  136. static struct file_operations buttons_fops =
  137. {
  138.     .owner = THIS_MODULE,
  139.     .open = buttons_open, /*open()*/
  140.     .release = buttons_close, /*release()*/
  141.     .read = buttons_read, /*read()*/
  142. };

  143. static void buttons_setup_cdev(void)/* 初始化并注册cdev */
  144. {
  145.     int err,devno = MKDEV(BUTTON_MAJOR,0);
  146.     cdev_init(&keydev.cdev,&buttons_fops);
  147.     keydev.cdev.owner = THIS_MODULE;
  148.     keydev.cdev.ops = &buttons_fops;
  149.     err = cdev_add(&keydev.cdev, devno, 1);
  150.     if (err)
  151.         printk(KERN_NOTICE "Error %d adding utukey", err);
  152. }
  153. static struct gpio gpios_6_array[] =
  154. {
  155.     { GPIO_TO_PIN(6, 10), GPIOF_IN,"RTU_KEY_IN1"},
  156. };

  157. static int __init buttons_init(void)
  158. {
  159.     int result;
  160.     dev_t devno = MKDEV(BUTTON_MAJOR,0);//用主次设备号生成设备号
  161.     
  162.     //申请GPIO,并设置为输入
  163.     int ret = gpio_request_array(gpios_6_array, ARRAY_SIZE(gpios_6_array));
  164.     if (ret < 0)
  165.     {
  166.         printk(KERN_ALERT "Cannot open GPIO 6_array\n");
  167.         gpio_free_array(gpios_6_array, ARRAY_SIZE(gpios_6_array));
  168.     }
  169.     
  170.     /* 申请设备号 */
  171.     if (BUTTON_MAJOR)
  172.         result = register_chrdev_region(devno, 1, DEVICE_NAME);
  173.     else//动态申请设备号
  174.     {
  175.         result = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME);
  176.         int button_major = MAJOR(devno);
  177.         printk(KERN_INFO "Todo: mknod /dev/%s c %d 0\n", DEVICE_NAME, button_major);
  178.     }
  179.     if (result < 0)
  180.         return result;
  181.     
  182.     buttons_setup_cdev();
  183.     keydev.head=keydev.tail=0;//初始化按键缓冲区
  184.     keydev.keyStatus=STATUS_NOKEY;//初始化按键状态
  185.     init_waitqueue_head(&keydev.wq);//初始化等待队列
  186.     init_timer(&keydev.key_timer);//初始化定时器,实现软件去抖
  187.     keydev.key_timer.function=key_timer_handler;
  188.     
  189.     //生成sysfs文件系统所需的class和属性文件
  190.     buttons_class = class_create(THIS_MODULE,DEVICE_NAME);
  191.     if(IS_ERR(buttons_class))
  192.     {
  193.         printk(KERN_ALERT"err:fail in buttons_class!\n");
  194.         return -1;
  195.     }
  196.     device_create(buttons_class,NULL,MKDEV(BUTTON_MAJOR,0),NULL,DEVICE_NAME);
  197.     printk(KERN_WARNING"buttons Module initialed!\n");
  198. }

  199. static void __exit buttons_exit(void)
  200. {
  201.     cdev_del(&keydev.cdev); // 注销cdev
  202.     unregister_chrdev_region(MKDEV(BUTTON_MAJOR, 0), 1); // 释放设备号
  203.     device_destroy(buttons_class,MKDEV(BUTTON_MAJOR,0));
  204.     class_destroy(buttons_class);
  205. }

  206. module_init(buttons_init);
  207. module_exit(buttons_exit);

  208. MODULE_AUTHOR("WBL");
  209. MODULE_DESCRIPTION("Davinci BUTTON Driver");
  210. MODULE_LICENSE("GPL");


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多