zoukankan      html  css  js  c++  java
  • 阻塞式访问和非阻塞式访问, 按键延时去抖。

    1、设备的阻塞方式访问
      应用编程
          sd=socke(...)
          recv(sd, ....)//无数据阻塞等待 有数据立即返回
          
      希望按键设备 也能达到在用户空间阻塞方式访问
     
      内核中为了实现设备的阻塞方式访问,提供了一套机制: 等待队列
          核心数据结构
             wait_queue_head_t
     
      实验步骤:
          1)定义一个等待队列头变量
             wait_queue_head_t  btn_wqh;
          2) 初始化等待队列头变量
             init_waitqueue_head(&btn_wqh);
            
            //等价于步骤1) 2)
            DECLARE_WAIT_QUEUE_HEAD(btn_wqh)   
          3) 驱动程序中,对设备执行读写操作时
             如果设备I/O为就绪 可以调用以下函数,
             实现进程的阻塞
             //该函数会使得调用者进程进入睡眠状态
             wait_event(btn_wqh, condition)  
             //进入的是可中断的睡眠状态
             wait_event_interruptible(btn_wqh, condition)
                condition,为TRUE  直接返回 不睡眠
                          为FALSE,进程进入睡眠状态
                    
          4)当设备I/O就绪时
             唤醒因I/O未就绪而进入睡眠状态的进程
             wake_up(&btn_wqh);
             wake_up_interruptible(&btn_wqh);      
             
       实现原理:
           1)内核中管理进程,会为每个进程建立PCB(进程控制块)
              linux中PCB在内核中对应的数据结构       
              struct task_struct
              {
                 //代表了进程的当前状态
                 volatile long state
                 ...
              }
              linux内核会为每个进程创建一个task_struct变量
              该变量在内核软件中代表着该进程
           2)current  
              始终执行正在CPU中执行的那个进程的数据结构
           3) wait_event_interruptible
              
              __wait_event_interruptible(btn_wqh, ....)
              {
                  //创建了一个新的节点
                  //该节点中保存了当前进程信息(task_struct变量地址)
                  DEFINE_WAIT(__wait);
                  
                  
                  prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE)
                  {
                      //把前边创建的新节点 插入btn_wqh指向的链表中去
                      __add_wait_queue(q, wait);
                      //将当前进程的状态改为TASK_INTERRUPTIBLE(可中断的睡眠状态)
                      set_current_state(state);
                  }    
                  //从处于running的集合中选出一个进程放入CPU中执行
                  //当前任务真的就放弃了CPU 进入睡眠状态
                  schedule();
              }
           4)唤醒
             //btn_wqh指向的链表中有多个节点
             //每个节点中记录了因某类事件而睡眠的进程
             //当该类事件条件满足时
             //内核会唤醒多个节点对应的多个进程
             wake_up_interruptible(&btn_wqh)

    2、延时去抖
       1)timer_list
       
       2) delayed_work


    3、按键的按下和释放都关注
       1)如何使得上升沿和下降沿都触发中断
         requst_irq(..., btn_isr
             IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                    ...)
       2)btn_timer_func
         哪个按键触发的
         
         按下触发还是释放触发 通过读取管脚的电平状态获取
            gpio_get_value(...)
     
      练习:
          ./test
          up 按下 输出 0x10
             释放      0x11
     
    4、设备的非阻塞方式访问           
               
       //默认为阻塞方式访问
       fd = open("/dev/mybuttons", O_RDONLY);
       //实现设备的非阻塞方式访问
       fd = open("/dev/mybuttons", O_RDONLY|O_NONBLOCK);         
               
               
       btn_read(filp, buf, len, offset)
      {
         要实现非阻塞,关键在于要知道open设备时是否
         使用了O_NONBLOCK参数
         
         该参数信息存储在了filp->f_flags
      }    
     
     
      struct file//文件表
      {
          //其中存储了用户空间open设备时的绝大部分参数信息
          unsigned int f_flags;
          //记录对应的操作函数集合地址
          const struct file_operations    *f_op;
          ...
      }    

    #include <stdio.h>
    #include <fcntl.h>
    
    int main(void)
    {
        unsigned char key=0;
        int fd = open("/dev/mybuttons", O_RDONLY);
        if(fd < 0)
        {
            perror("open failed:");
            return -1;
        }
        printf("open successed!
    ");
    
        while(1)
        {
            read(fd, &key, sizeof(key));
            printf("key=%#x
    ", key);
        }
    
        close(fd);
        return 0;
    }
    #include "../../global.h"
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/delay.h>
    #include <linux/uaccess.h>
    #include <linux/interrupt.h>
    #include <mach/platform.h>
    #include <linux/sched.h>
    
    dev_t dev = 0;
    struct cdev btn_cdev;
    struct class *cls = NULL;
    
    /*定义信号量*/
    struct semaphore btn_sem;
    /*按键键值缓冲区*/
    unsigned char key_buf;
    /*记录按键缓冲区中是否有键值
     *0,无键值
     *1, 有键值
     * */
    volatile int ev_press = 0;
    /*定义等待队列头变量*/
    wait_queue_head_t btn_wqh;
    
    static int btn_open(struct inode *inode,
                        struct file *filp)
    {
        int ret = 0;
        //down(&btn_sem);
        ret = down_interruptible(&btn_sem);
        if(ret) //被信号打断
        {
            return -EAGAIN;
        }
        return 0;
    }
    static int btn_release(struct inode *inode,
                           struct file *filp)
    {
        up(&btn_sem);
        return 0;
    }
    static ssize_t btn_read(struct file *filp,
                            char __user *buf, 
                            size_t len,
                            loff_t *off)
    {
        int ret = 0;
        /*
         *按键缓冲区中有键值 直接返回
         *如果无键值 让test进程进入睡眠状态
         * */
        wait_event_interruptible(btn_wqh, ev_press);
    
        //*buf = key_buf;//有风险
        ret = copy_to_user(buf, &key_buf, len);
        if(ret)
        {
            return -EFAULT;
        }
        ev_press = 0;
        return ret;
    }
    struct file_operations btn_fops =
    {
        .owner = THIS_MODULE,
        .open = btn_open,
        .release = btn_release,
        .read = btn_read,
    };
    irqreturn_t btn_isr(int irq, void *dev)
    {
        /*保存按键值*/
        key_buf = 0x10;
        ev_press = 1;//有键值
        /*唤醒因无键值而睡眠的进程*/
        wake_up_interruptible(&btn_wqh);
    
        return IRQ_HANDLED; //中断处理完毕
    }
    int __init btn_drv_init(void)
    {
        if(request_irq(IRQ_GPIO_A_START+28, btn_isr,
                       IRQF_TRIGGER_FALLING,
                       "up", NULL))
        {
            printk("<1>"  "request_irq failed!");
            return -EAGAIN;
        }
        /*申请注册设备号*/
        alloc_chrdev_region(&dev, 0, 1, "buttons");
        /*初始化cdev*/
        cdev_init(&btn_cdev, &btn_fops);
        /*注册cdev*/
        cdev_add(&btn_cdev, dev, 1);
        /*自动创建设备文件*/
        cls = class_create(THIS_MODULE, "buttons");
        device_create(cls, NULL, dev, NULL, "mybuttons");
        /*初始化信号量*/
        /*该设备可以同时被一个进程访问*/
        sema_init(&btn_sem, 1);
        /*初始化等待队列头*/
        init_waitqueue_head(&btn_wqh);
    
        return 0;
    }
    void __exit btn_drv_exit(void)
    {
        /*销毁设备文件*/
        device_destroy(cls, dev);
        class_destroy(cls);
        /*注销cdev*/
        cdev_del(&btn_cdev);
        /*注销设备号*/
        unregister_chrdev_region(dev, 1);
        free_irq(IRQ_GPIO_A_START+28, NULL);
    }
    module_init(btn_drv_init);
    module_exit(btn_drv_exit);
    #include "../../global.h"
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/delay.h>
    #include <linux/uaccess.h>
    #include <linux/interrupt.h>
    #include <mach/platform.h>
    #include <linux/sched.h>
    
    dev_t dev = 0;
    struct cdev btn_cdev;
    struct class *cls = NULL;
    
    /*定义信号量*/
    struct semaphore btn_sem;
    /*按键键值缓冲区*/
    unsigned char key_buf;
    /*记录按键缓冲区中是否有键值
     *0,无键值
     *1, 有键值
     * */
    volatile int ev_press = 0;
    /*定义等待队列头变量*/
    wait_queue_head_t btn_wqh;
    
    struct timer_list btn_timer;
    typedef struct btn_desc
    {
        unsigned char code;// 按键编码
        int irq;
        char *name;
    }btn_desc_t;
    btn_desc_t buttons[]=
    {
        {0x10, IRQ_GPIO_A_START+28, "up"},
        {0x20, IRQ_GPIO_B_START+30, "down"},
        {0x30, IRQ_GPIO_B_START+31, "left"},
        {0x40, IRQ_GPIO_B_START+9,  "right"},
    };
    
    static int btn_open(struct inode *inode,
                        struct file *filp)
    {
        int ret = 0;
        //down(&btn_sem);
        ret = down_interruptible(&btn_sem);
        if(ret) //被信号打断
        {
            return -EAGAIN;
        }
        return 0;
    }
    static int btn_release(struct inode *inode,
                           struct file *filp)
    {
        up(&btn_sem);
        return 0;
    }
    static ssize_t btn_read(struct file *filp,
                            char __user *buf, 
                            size_t len,
                            loff_t *off)
    {
        int ret = 0;
        /*
         *按键缓冲区中有键值 直接返回
         *如果无键值 让test进程进入睡眠状态
         * */
        wait_event_interruptible(btn_wqh, ev_press);
    
        //*buf = key_buf;//有风险
        ret = copy_to_user(buf, &key_buf, len);
        if(ret)
        {
            return -EFAULT;
        }
        ev_press = 0;
        return ret;
    }
    struct file_operations btn_fops =
    {
        .owner = THIS_MODULE,
        .open = btn_open,
        .release = btn_release,
        .read = btn_read,
    };
    
    void btn_timer_func(unsigned long data)
    {
        /*获取按键的描述信息*/
        btn_desc_t *pdata = (btn_desc_t *)data;
        /*保存按键值*/
        key_buf = pdata->code;
        ev_press = 1;//有键值
        /*唤醒因无键值而睡眠的进程*/
        wake_up_interruptible(&btn_wqh);
    
    }
    irqreturn_t btn_isr(int irq, void *dev)
    {
        /*传递按键的描述信息*/
        btn_timer.data = (unsigned long)dev;
        mod_timer(&btn_timer, jiffies+HZ/100);
        return IRQ_HANDLED; //中断处理完毕
    }
    int __init btn_drv_init(void)
    {
        int i = 0;
        int ret = 0;
        /*申请注册设备号*/
        alloc_chrdev_region(&dev, 0, 1, "buttons");
        /*初始化cdev*/
        cdev_init(&btn_cdev, &btn_fops);
        /*注册cdev*/
        cdev_add(&btn_cdev, dev, 1);
        /*自动创建设备文件*/
        cls = class_create(THIS_MODULE, "buttons");
        device_create(cls, NULL, dev, NULL, "mybuttons");
        /*初始化信号量*/
        /*该设备可以同时被一个进程访问*/
        sema_init(&btn_sem, 1);
        /*初始化等待队列头*/
        init_waitqueue_head(&btn_wqh);
        
        for(; i<ARRAY_SIZE(buttons);i++)
        {
            ret = request_irq(buttons[i].irq, btn_isr, 
                              IRQF_TRIGGER_FALLING,
                              buttons[i].name, 
                              &(buttons[i]));
        }
        init_timer(&btn_timer);
        btn_timer.function = btn_timer_func;
        
    
        return 0;
    }
    void __exit btn_drv_exit(void)
    {
        int i = 0;
        for(; i<ARRAY_SIZE(buttons); i++)
        {
            free_irq(buttons[i].irq, buttons+i);
        }
        /*销毁设备文件*/
        device_destroy(cls, dev);
        class_destroy(cls);
        /*注销cdev*/
        cdev_del(&btn_cdev);
        /*注销设备号*/
        unregister_chrdev_region(dev, 1);
    }
    module_init(btn_drv_init);
    module_exit(btn_drv_exit);
    #include "../../global.h"
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/delay.h>
    #include <linux/uaccess.h>
    #include <linux/interrupt.h>
    #include <mach/platform.h>
    #include <linux/sched.h>
    #include <linux/gpio.h>
    
    dev_t dev = 0;
    struct cdev btn_cdev;
    struct class *cls = NULL;
    
    /*定义信号量*/
    struct semaphore btn_sem;
    /*按键键值缓冲区*/
    unsigned char key_buf;
    /*记录按键缓冲区中是否有键值
     *0,无键值
     *1, 有键值
     * */
    volatile int ev_press = 0;
    /*定义等待队列头变量*/
    wait_queue_head_t btn_wqh;
    
    struct timer_list btn_timer;
    typedef struct btn_desc
    {
        unsigned char code;// 按键编码
        int irq;
        char *name;
        int gpio;  //管脚编号
    }btn_desc_t;
    btn_desc_t buttons[]=
    {
        {0x10, IRQ_GPIO_A_START+28, "up",PAD_GPIO_A+28},
        {0x20, IRQ_GPIO_B_START+30, "down",PAD_GPIO_B+30},
        {0x30, IRQ_GPIO_B_START+31, "left",PAD_GPIO_B+31},
        {0x40, IRQ_GPIO_B_START+9,  "right",PAD_GPIO_B+9},
    };
    
    static int btn_open(struct inode *inode,
                        struct file *filp)
    {
        int ret = 0;
        //down(&btn_sem);
        ret = down_interruptible(&btn_sem);
        if(ret) //被信号打断
        {
            return -EAGAIN;
        }
        return 0;
    }
    static int btn_release(struct inode *inode,
                           struct file *filp)
    {
        up(&btn_sem);
        return 0;
    }
    static ssize_t btn_read(struct file *filp,
                            char __user *buf, 
                            size_t len,
                            loff_t *off)
    {
        int ret = 0;
        /*
         *按键缓冲区中有键值 直接返回
         *如果无键值 让test进程进入睡眠状态
         * */
        wait_event_interruptible(btn_wqh, ev_press);
    
        //*buf = key_buf;//有风险
        ret = copy_to_user(buf, &key_buf, len);
        if(ret)
        {
            return -EFAULT;
        }
        ev_press = 0;
        return ret;
    }
    struct file_operations btn_fops =
    {
        .owner = THIS_MODULE,
        .open = btn_open,
        .release = btn_release,
        .read = btn_read,
    };
    
    void btn_timer_func(unsigned long data)
    {
        int stat = 0;
        /*获取按键的描述信息*/
        btn_desc_t *pdata = (btn_desc_t *)data;
        /*判断是按下触发还是释放触发
         *按下 返回0
         *释放 返回1
         * */
        stat = gpio_get_value(pdata->gpio);
        /*保存按键值*/
        key_buf = pdata->code + stat;
        ev_press = 1;//有键值
        /*唤醒因无键值而睡眠的进程*/
        wake_up_interruptible(&btn_wqh);
    
    }
    irqreturn_t btn_isr(int irq, void *dev)
    {
        /*传递按键的描述信息*/
        btn_timer.data = (unsigned long)dev;
        mod_timer(&btn_timer, jiffies+HZ/100);
        return IRQ_HANDLED; //中断处理完毕
    }
    int __init btn_drv_init(void)
    {
        int i = 0;
        int ret = 0;
        /*申请注册设备号*/
        alloc_chrdev_region(&dev, 0, 1, "buttons");
        /*初始化cdev*/
        cdev_init(&btn_cdev, &btn_fops);
        /*注册cdev*/
        cdev_add(&btn_cdev, dev, 1);
        /*自动创建设备文件*/
        cls = class_create(THIS_MODULE, "buttons");
        device_create(cls, NULL, dev, NULL, "mybuttons");
        /*初始化信号量*/
        /*该设备可以同时被一个进程访问*/
        sema_init(&btn_sem, 1);
        /*初始化等待队列头*/
        init_waitqueue_head(&btn_wqh);
        
        for(; i<ARRAY_SIZE(buttons);i++)
        {
            ret = request_irq(buttons[i].irq, btn_isr, 
                IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                              buttons[i].name, 
                              &(buttons[i]));
        }
        init_timer(&btn_timer);
        btn_timer.function = btn_timer_func;
        
    
        return 0;
    }
    void __exit btn_drv_exit(void)
    {
        int i = 0;
        for(; i<ARRAY_SIZE(buttons); i++)
        {
            free_irq(buttons[i].irq, buttons+i);
        }
        /*销毁设备文件*/
        device_destroy(cls, dev);
        class_destroy(cls);
        /*注销cdev*/
        cdev_del(&btn_cdev);
        /*注销设备号*/
        unregister_chrdev_region(dev, 1);
    }
    module_init(btn_drv_init);
    module_exit(btn_drv_exit);
    #include "../../global.h"
    #include <linux/cdev.h>
    #include <linux/fs.h>
    #include <linux/device.h>
    #include <linux/delay.h>
    #include <linux/uaccess.h>
    #include <linux/interrupt.h>
    #include <mach/platform.h>
    #include <linux/sched.h>
    #include <linux/gpio.h>
    
    dev_t dev = 0;
    struct cdev btn_cdev;
    struct class *cls = NULL;
    
    /*定义信号量*/
    struct semaphore btn_sem;
    /*按键键值缓冲区*/
    unsigned char key_buf;
    /*记录按键缓冲区中是否有键值
     *0,无键值
     *1, 有键值
     * */
    volatile int ev_press = 0;
    /*定义等待队列头变量*/
    wait_queue_head_t btn_wqh;
    
    struct timer_list btn_timer;
    typedef struct btn_desc
    {
        unsigned char code;// 按键编码
        int irq;
        char *name;
        int gpio;  //管脚编号
    }btn_desc_t;
    btn_desc_t buttons[]=
    {
        {0x10, IRQ_GPIO_A_START+28, "up",PAD_GPIO_A+28},
        {0x20, IRQ_GPIO_B_START+30, "down",PAD_GPIO_B+30},
        {0x30, IRQ_GPIO_B_START+31, "left",PAD_GPIO_B+31},
        {0x40, IRQ_GPIO_B_START+9,  "right",PAD_GPIO_B+9},
    };
    
    static int btn_open(struct inode *inode,
                        struct file *filp)
    {
        int ret = 0;
        //down(&btn_sem);
        ret = down_interruptible(&btn_sem);
        if(ret) //被信号打断
        {
            return -EAGAIN;
        }
        return 0;
    }
    static int btn_release(struct inode *inode,
                           struct file *filp)
    {
        up(&btn_sem);
        return 0;
    }
    static ssize_t btn_read(struct file *filp,
                            char __user *buf, 
                            size_t len,
                            loff_t *off)
    {
        int ret = 0;
        //if(用户非阻塞方式访问设备 && ev_press==0)
        if((filp->f_flags&O_NONBLOCK) && ev_press==0)
        {
            return -EAGAIN;
        }
        /*
         *按键缓冲区中有键值 直接返回
         *如果无键值 让test进程进入睡眠状态
         * */
        wait_event_interruptible(btn_wqh, ev_press);
    
        //*buf = key_buf;//有风险
        ret = copy_to_user(buf, &key_buf, len);
        if(ret)
        {
            return -EFAULT;
        }
        ev_press = 0;
        return ret;
    }
    struct file_operations btn_fops =
    {
        .owner = THIS_MODULE,
        .open = btn_open,
        .release = btn_release,
        .read = btn_read,
    };
    
    void btn_timer_func(unsigned long data)
    {
        int stat = 0;
        /*获取按键的描述信息*/
        btn_desc_t *pdata = (btn_desc_t *)data;
        /*判断是按下触发还是释放触发
         *按下 返回0
         *释放 返回1
         * */
        stat = gpio_get_value(pdata->gpio);
        /*保存按键值*/
        key_buf = pdata->code + stat;
        ev_press = 1;//有键值
        /*唤醒因无键值而睡眠的进程*/
        wake_up_interruptible(&btn_wqh);
    
    }
    irqreturn_t btn_isr(int irq, void *dev)
    {
        /*传递按键的描述信息*/
        btn_timer.data = (unsigned long)dev;
        mod_timer(&btn_timer, jiffies+HZ/100);
        return IRQ_HANDLED; //中断处理完毕
    }
    int __init btn_drv_init(void)
    {
        int i = 0;
        int ret = 0;
        /*申请注册设备号*/
        alloc_chrdev_region(&dev, 0, 1, "buttons");
        /*初始化cdev*/
        cdev_init(&btn_cdev, &btn_fops);
        /*注册cdev*/
        cdev_add(&btn_cdev, dev, 1);
        /*自动创建设备文件*/
        cls = class_create(THIS_MODULE, "buttons");
        device_create(cls, NULL, dev, NULL, "mybuttons");
        /*初始化信号量*/
        /*该设备可以同时被一个进程访问*/
        sema_init(&btn_sem, 1);
        /*初始化等待队列头*/
        init_waitqueue_head(&btn_wqh);
        
        for(; i<ARRAY_SIZE(buttons);i++)
        {
            ret = request_irq(buttons[i].irq, btn_isr, 
                IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
                              buttons[i].name, 
                              &(buttons[i]));
        }
        init_timer(&btn_timer);
        btn_timer.function = btn_timer_func;
        
    
        return 0;
    }
    void __exit btn_drv_exit(void)
    {
        int i = 0;
        for(; i<ARRAY_SIZE(buttons); i++)
        {
            free_irq(buttons[i].irq, buttons+i);
        }
        /*销毁设备文件*/
        device_destroy(cls, dev);
        class_destroy(cls);
        /*注销cdev*/
        cdev_del(&btn_cdev);
        /*注销设备号*/
        unregister_chrdev_region(dev, 1);
    }
    module_init(btn_drv_init);
    module_exit(btn_drv_exit);

  • 相关阅读:
    dedecms文章标题是在哪个数据库表?要批量替换关键词
    dedecms首页调用的简介一直修改不了是自动文章摘要在作怪
    如何进行微信营销?玩转微信公众平台营销理论手册
    用了那么久居然没发现firefox快捷键有如此多
    保护隐私:清除cookie、禁用cookie确保安全【分享给身边的朋友吧】
    如何更改firefox默认搜索引擎?一步搞定!
    微信红包中使用的技术:AA收款+随机算法
    马年添加一下马蹄印记统计(网站统计)
    今天上完就放假了,马年见
    三种dedecms调用相关文章的方法
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11884407.html
Copyright © 2011-2022 走看看