• 蜕变成蝶~Linux设备驱动之按键设备驱动


     

     在上述的驱动系列博客中,我们已经了解了关于阻塞和非阻塞、异步通知、轮询、内存和I/O口访问、并发控制等知识,按键设备驱动相对来说是比较简单的,本章内容可以加深我们对字符设备驱动架构、阻塞与非阻塞、中断定时器等相关知识的理解。在嵌入式的系统中,按键的硬件原理简单,就是通过一个上拉电阻将处理器的外部中断引脚拉高,电阻的另一端接按钮并接地就可以实现。

    1.按键的确认流程如下

    2 按键驱动中的有关数据结构

    2.1 按键设备结构体以及定时器

    #define MAX KEY BUF 16 // 键缓冲区大小                                   
    typedef unsigned char KEY RET; 
    
    //设备结构体: 
    typedef struct 
    {                               
        unsigned int keyStatus[KEY NUM]; //4个 键的 键状态 
        KEY RET buf[MAX KEY BUF]; // 键缓冲区 
        unsigned int head, tail; // 键缓冲区头和尾 
        wait queue head t wq; //等待队列 
        struct cdev cdev;      //cdev 结构体   
    } KEY DEV; 
    
    static struct timer list key timer[KEY NUM];//4个 键去抖定时器 
    

    2.2 按键硬件资源、键值信息结构体

     static struct key info 
          { 
                   
            int irq no;      //中断号 
                              
            unsigned int gpio port; //GPIO端口 
                     
            int key no;     //键值 
              
          } key info tab [4] = 
          { 
             /* 键所使用的CPU 资源*/ 
            {  IRQ EINT10, GPIO G2, 1 
            } 
           , 
            { 
            
             IRQ EINT13, GPIO G5, 2 
            } 
           , 
            { 
                
             IRQ EINT14, GPIO G6, 3 
            } 
           , 
            { 
                        
             IRQ EINT15, GPIO G7, 4 
            } 
           , 
         }; 
    

    2.3 按键设备驱动文件操作结构体

    static struct file operations s3c2410 key fops = 
         { 
                     
           owner: THIS MODULE, 
                        
           open: s3c2410 key open,  //启动设备 
                         
           release: s3c2410 key release,  //关闭设备 
                        
           read: s3c2410 key read,  //读取 键的键值 
         }; 
    

    3 按键设备的模块加载和卸载函数

    3.1 加载函数

    static int    init s3c2410 key init (void) 
          { 
            ...//申请设备号,添加cdev 
         
                     
            request irqs(); //注册中断函数 
            keydev .head = keydev .tail = 0; //初始化结构体 
                                
            for (i = 0; i < KEY NUM; i++) 
                                               
              keydev.keyStatus[i] = KEYSTATUS UP; 
              
            init waitqueue head (&(keydev .wq)); //等待队列 
          
           //初始化定时器,实现软件的去抖动 
                              
           for (i = 0; i < KEY NUM; i++) 
                
             setup timer (&key timer[i], key timer handler, i); 
           //把 键的序号作为传入定时器处理函数的参数 
         }
    

    3.2 卸载函数

    static void     exit s3c2410 key exit (void) 
         { 
                
           free irqs(); //注销中断 
           ...//释放设备号,删除cdev 
         } 
    

    3.3 中断申请函数

    /*申请系统中断,中断方式为下降沿触发*/ 
                               
          static int request irqs(void) 
          { 
                        
            struct key info *k; 
            int i; 
                                  
            for (i= 0; i < sizeof(key info tab) / sizeof(key info tab [1]); i++) 
            { 
                    
              k = key info tab + i; 
              
              set external irq (k->irq no, EXT LOWLEVEL, GPIO PULLUP DIS); 
                          //设置低电平触发 
                          
                  if   (request irq (k->irq no,  &buttons irq,  SA INTERRUPT, 
        
    DEVICE NAME, 
               i))  //申请中断,将 键序号作为参数传入中断服务程序 
             { 
               return  - 1; 
             } 
           } 
           return 0; 
         }
    

    3.4 中断释放函数

    /*释放中断*/ 
                            
          static void free irqs(void) 
          { 
                     
            struct key info *k; 
            int i; 
                                
            for (i= 0; i < sizeof(key info tab) / sizeof(key info tab [1]); i++) 
            { 
                     
              k = key info tab + i; 
                
              free irq (k->irq no, buttons irq); //释放中断 
           } 
         } 
    

    4 按键设备驱动中断和定时器处理程序

      在按键按下之后,将发生中断,在中断处理程序中,应该先关闭中断进去查询模式,延时以消抖如下中断处理过程只有顶半部,没有底半部。

    4.1 中断处理程序

    static void s3c2410 eint key (int irq, void *dev id, struct pt regs 
    *reg) 
         { 
                      
           int key = dev id; 
                
           disable irq (key info tab [key].irq no); //关中断,转入查询 式 
          
                                            
           keydev.keyStatus[key] = KEYSTATUS DOWNX;//状态为按下 
               _                     
           key timer [key].expires == jiffies + KEY TIMER DELAY1;//延迟 
      
           add timer (&key timer[key]); //启动定时器 
         } 
    

    4.2 定时器处理流程

      按键按下时,该按键将记录字啊缓冲区,同时定时器启动延时,每次记录新的键值时,等待队列被唤醒,其代码如下。

    //按键设备驱动的定时器处理函数
    static void key timer handler (unsigned long data) 
          { 
            int key = data; 
            
            if (ISKEY DOWN (key)) 
            { 
                       
              if (keydev.keyStatus[key] == KEYSTATUS DOWNX) 
              //从中断进入 
              { 
                             
                keydev .keyStatus[key] = KEYSTATUS DOWN; 
                 
               key timer[key].expires == jiffies + KEY TIMER DELAY; //延迟 
               keyEvent ();  //记录键值,唤醒等待队列 
                
               add timer(&key timer [key]); 
             } 
             else 
             { 
                 
               key timer[key].expires == jiffies + KEY TIMER DELAY; //延迟 
                 
               add timer(&key timer [key]); 
             } 
           } 
           else       //键已抬起 
           { 
                                                 
             keydev.keyStatus[key] = KEYSTATUS UP; 
                
             enable irq (key info tab [key].irq no); 
           } 
    

    5 打开和释放函数

      这里主要是设置keydev.head和keydev.tail还有按键事件函数指针keyEvent的值,按键设备驱动的打开、释放函数如下:

    static int s3c2410 key open (struct inode *inode, struct file *filp) 
          { 
            keydev .head = keydev .tail = 0; //清空 键动作缓冲区 
                                                               
            keyEvent = keyEvent raw; //函数指针指向 键处理函数keyEvent raw 
            return 0; 
          } 
         
                             
           static int s3c2410 key release (struct inode *inode, struct file *filp) 
          { 
                              
           keyEvent = keyEvent dummy; //函数指针指向空函数 
           return 0;
       }
    

    6 读函数

      读函数主要是提供对按键设备结构体缓冲区的读并复制到用户空间,当keydev.head != keydev.tail时,说明缓冲区有数据,使用copy_to_user()函数拷贝到用户空间,反之根据用户空间是阻塞还是非阻塞读分为以下两种情况:

    • 非阻塞读:没有按键缓存,直接返回- EAGAIN;
    • 阻塞读:在keydev.wq等待队列上睡眠,直到有按键记录 到缓冲区后被唤醒。
    //按键设备驱动的读函数 
    
          static ssize t s3c2410 key read (struct file *filp,char *buf,ssize t 
    count, 
                
            loff t*ppos) 
          { 
            retry: if (keydev.head != keydev .tail) 
            //当前循环队列中有数据 
            { 
                  
              key ret = keyRead (); //读取按键 
                   
              copy to user(..); //把数据从内核空间传送到用户空间 
            } 
           else 
           { 
                       
             if (filp->f flags &O NONBLOCK) 
             //若用户采用非阻塞方式读取 
             { 
               return  - EAGAIN; 
             } 
                          
             interruptible sleep on (&(keydev .wq)); 
               //用户采用阻塞方式读取,调用该函数使进程睡眠 
             goto retry; 
           } 
           return 0; 
         } 
    

      

      版权所有,转载请注明转载地址:http://www.cnblogs.com/lihuidashen/p/4498025.html

  • 相关阅读:
    小条打印机输出简析
    Visual Studio离线安装
    删除“Open in Windows Terminal”右键菜单
    《新媒体营销精华:精准定位+爆款打造+匠心运营+内容变现》笔者的新书,欢迎各位粉丝上京东购买
    vue3.0 的 Composition API 的一种使用方法
    对比传统的Xilinx AMP方案和OPENAMP方案-xapp1078和ug1186【转】
    OpenAMP简介【转】
    git 用法【笔记】
    针对非对称多处理系统实现更简单的软件开发【转】
    内核探测工具systemtap简介【转】
  • 原文地址:https://www.cnblogs.com/lihuidashen/p/4498025.html
Copyright © 2020-2023  润新知