• 7.自己写中断方式按键驱动程序(详解)


    request_irq()和free_irq()分析完毕后,接下来开始编写上升沿中断的按键驱动

    如下图,需要设置4个按键的EINT0, EINT2, EINT11, EINT19的模式为双边沿,且设置按键引脚为中断引脚

     

    这里我们只需要使用request_irq函数就行了, 在request_irq函数里会初始chip->set_type(设置引脚和中断模式)

    1.首先添加头文件

    #include <linux/irq.h>         //要用到IRQ_EINT0和IRQT_RISING这些变量

    2.在second_drv_open函数中,申请4个中断:

    /* IRQ_EINT0:     中断号, 定义在 asm/arch/irqs.h,被linux/irq.h调用
    buttons_irq :     中断服务函数,
    IRQT_ BOTHEDGE:   双边沿中断, 定义在 asm/irq.h,被linux/irq.h调用
    “S1”:             保存文件到/proc/interrupt/S1,
    1:                dev_id,中断函数的参数, 被用来释放中断服务函数,中断时并会传入中断服务函数
    */
    
    request_irq(IRQ_EINT0, buttons_irq,IRQT_BOTHEDGE, “S1”, 1);  
    request_irq(IRQ_EINT2, buttons_irq,IRQT_ BOTHEDGE, “S2”, 1);
    request_irq(IRQ_EINT11, buttons_irq,IRQT_ BOTHEDGE, “S3”, 1);
    request_irq(IRQ_EINT19, buttons_irq,IRQT_ BOTHEDGE, “S4”, 1);

    3.在file_oprations结构体中添加.release成员函数,用来释放中断

    static struct file_operations second_drv_fops={
         .owner = THIS_MODULE,
         .open = second_drv_open,
         .read = second_drv_read,
         .release=second_drv_class,        //里面添加free_irq函数,来释放中断服务函数
    };

    然后写.release成员函数,释放中断:

    int  second_drv_class(struct inode *inode, struct file  *file)
    {
      free_irq(IRQ_EINT0,1);
      free_irq(IRQ_EINT2,1);
      free_irq(IRQ_EINT11,1);
      free_irq(IRQ_EINT19,1);
    
      return 0;
    
    }

    4.写action->handler中断服务函数,在第2小节里request_irq函数的中断服务函数是buttons_irq

    static irqreturn_t  buttons_irq (int irq, void *dev_id)     //irq:中断号, void *:表示支持所有类型
    {
      printk(“irq=%d
    ”);
      return IRQ_HANDLED;
    }

    5.make后,然后放在开发板里insmod,并挂载好了buttons设备节点,如下图:

     

    6.通过exec 5</dev/buttons   将/dev/buttons 设备节点挂载到-sh进程下描述符5:

    如下图,使用ps查看-sh进程为801,然后ls -l /proc/801/fd 找到描述符5指向/dev/buttons

    如下图,并申请中断,当有按键按下时,就进入中断服务函数buttons_irq()打印数据:

     

    6.通过exec 5<&- 将描述符5卸载

    会进入.release成员second_drv_class()函数释放中断,

    然后cat /proc/interrupts会发现申请的中断已经注销掉了,在-sh进程fd文件里也没有文件描述符5

    7.改进中断按键驱动程序

    使用等待队列,让read函数没有中断时,进入休眠状态,降低CPU.

    使用dev_id来获取不同按键的状态,是上升沿还是下降沿触发?

    7.1接下来要用到以下几个函数:

    s3c2410_gpio_getpin(unsigned int pin);     //获取引脚高低电平

    pin: 引脚名称,例如:S3C2410_GPA0,定义在<asm/arch/regs-gpio.h>

    队列3个函数(声明队列,唤醒队列,等待队列):

    static DECLARE_WAIT_QUEUE_HEAD(qname);      

    声明一个新的等待队列类型的中断

    qname:就是中断名字,被用来后面的唤醒中断和等待中断

    wake_up_interruptible(*qname);   

    唤醒一个中断,会将这个中断重新添加到runqueue队列(将中断置为TASK_RUNNING状态)

    qname:指向声明的等待队列类型中断名字

    wait_event_interruptible(qname, condition);  

    等待事件中断函数,用来将中断放回等待队列,

    前提是condition要为0,然后将这个中断从runqueue队列中删除(将中断置为TASK_INTERRUPTIBLE状态),然后会在函数里一直for(; ;)判断condition为真才退出

    注意:此时的中断属于僵尸进程(既不在等待队列,也不在运行队列),当需要这个进程时,需要使用wake_up_interruptible(*qname)来唤醒中断

    qname: (wait queue):为声明的等待队列的中断名字

    condition:状态,等于0时就是中断进入休眠, 1:退出休眠

    7.2 驱动程序步骤

    (1)定义引脚描述结构体数组,每个结构体都保存按键引脚和初始状态,然后在中断服务函数中通过s3c2410_gpio_getpin()来获取按键是松开还是按下(因为中断是双边沿触发),并保存在key_val里(它会在.read函数发送给用户层)

     /*
      *引脚描述结构体
      */
     struct pin_desc{
       unsigned int  pin;
       unsigned int  pin_status;
    };
     /*
      *key初始状态(没有按下): 0x01,0x02,0x03,0x04
      *key状态(按下):                       0x81,0x82,0x83,0x84
      */
    struct pin_desc  pins_desc[4]={
            {S3C2410_GPF0,0x01 }, 
            {S3C2410_GPF2, 0x02 },
            {S3C2410_GPG3, 0x03 },
            {S3C2410_GPG11,0x04},} ;

     (2)声明等待队列类型的中断button_wait:

    static DECLARE_WAIT_QUEUE_HEAD(button_ wait);        //声明等待队列类型的中断

    (3)定义全局变量even _press,用于中断事件标志:

    static volatile int even _press = 0;

    (4)在.read函数里,将even _press置0放入等待事件中断函数中,判断even _press为真,才发送数据:

    even_press = 0;                                
    
    wait_event_interruptible(button_ wait, even _press);   //当even _press为真,表示有按键按下,退出等待队列  
    
    copy_to_user(buf, &key_val, 1);       //even _press为真,有数据了,发送给用户层      

    (5)在中断服务函数里,发生中断时, 就将even _press置1,并唤醒中断button_wait(.read函数里就会发送数据给用户层):

    even _press = 0;  
    wake_up_interruptible(&button_wait);         //唤醒中断

    7.3 更改测试程序second_interrupt_text.c

    最终修改如下:

    #include <sys/types.h>    //调用sys目录下types.h文件
    #include <sys/stat.h>      //stat.h获取文件属性
    #include <fcntl.h>
    #include <stdio.h>
    #include <string.h>
    /*secondtext            while一直获取按键信息   */
    int main(int argc,char **argv)
    {
     int fd,ret;
     unsigned int val=0;               
     fd=open("/dev/buttons",O_RDWR);       
     if(fd<0)
         {printf("can't open!!!
    ");
         return -1;}
    while(1) { ret=read(fd,&val,1); //读取一个值,(当在等待队列时,本进程就会进入休眠状态) if(ret<0) { printf("read err! "); continue; } printf("key_val=0X%x ",val); } return 0; }

    8.运行结果

    insmod second_interrupt.ko           //挂载驱动设备

    ./second_interrupt_text &             //后台运行测试程序 

    创建了4个中断,如下图:

     

     

    当没有按键按下时,这个进程就处于静止状态staitc,如下图所示:

     

    在等待队列(休眠状态)下,该进程占用了CPU0%资源,如下图所示:

     

    当有按键按下时,便打印数据,如下图所示:

    下节继续改进按键程序—使用poll机制

    本节驱动代码如下:

    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/fs.h>
    #include <linux/init.h>
    #include <linux/delay.h>
    #include <linux/irq.h>  
    #include <asm/irq.h>
    #include <asm/arch/regs-gpio.h>
    #include <asm/hardware.h>
    #include <asm/uaccess.h>
    #include <asm/io.h>
    
    
    static struct class *seconddrv_class;                 
    static struct class_device   *seconddrv_class_devs; 
    
     
    /*    声明等待队列类型中断 button_wait      */
    static DECLARE_WAIT_QUEUE_HEAD(button_wait);
    
     
     /*
      * 定义中断事件标志
      * 0:进入等待队列        1:退出等待队列
      */
    static int even_press=0;                          
    
     /*
      * 定义全局变量key_val,保存key状态
      */
    static int key_val=0;                          
    
     /*
      *引脚描述结构体
      */
     struct pin_desc{
       unsigned int  pin;
       unsigned int  pin_status;
    };
    
     
     /*
      *key初始状态(没有按下): 0x01,0x02,0x03,0x04
      *key状态(按下):   0x81,0x82,0x83,0x84
      */
    struct pin_desc  pins_desc[4]={
                       {S3C2410_GPF0,0x01 },
                       {S3C2410_GPF2, 0x02 },
                       {S3C2410_GPG3, 0x03 },
                       {S3C2410_GPG11,0x04},} ;
    
    
    int  second_drv_class(struct inode *inode, struct file  *file)  //卸载中断
    {
      free_irq(IRQ_EINT0,&pins_desc[0]);
      free_irq(IRQ_EINT2,&pins_desc[1]);
      free_irq(IRQ_EINT11,&pins_desc[2]);
      free_irq(IRQ_EINT19,&pins_desc[3]);
    return 0; } /* 确定是上升沿还是下降沿 */ static irqreturn_t buttons_irq (int irq, void *dev_id) //中断服务函数 { struct pin_desc *pindesc=(struct pin_desc *)dev_id; //获取引脚描述结构体 unsigned int pin_val=0; pin_val=s3c2410_gpio_getpin(pindesc->pin); if(pin_val) { /*没有按下 (下降沿),清除0x80*/ key_val=pindesc->pin_status&0xef; } else { /*按下(上升沿),加上0x80*/ key_val=pindesc->pin_status|0x80; } even_press=1; //退出等待队列 wake_up_interruptible(&button_wait); //唤醒 中断 return IRQ_HANDLED; } static int second_drv_open(struct inode *inode, struct file *file) { request_irq(IRQ_EINT0,buttons_irq,IRQT_BOTHEDGE,"S1",&pins_desc[0]);
    request_irq(IRQ_EINT2, buttons_irq,IRQT_BOTHEDGE,
    "S2", &pins_desc[1]); request_irq(IRQ_EINT11, buttons_irq,IRQT_BOTHEDGE, "S3", &pins_desc[2]); request_irq(IRQ_EINT19, buttons_irq,IRQT_BOTHEDGE, "S4", &pins_desc[3]); return 0; } static int second_drv_read(struct file *file, const char __user *buf, size_t count, loff_t * ppos) {/*将中断 进入等待队列(休眠状态)*/ wait_event_interruptible(button_wait, even_press); /*有按键按下,退出等待队列,上传key_val 给用户层*/ if(copy_to_user(buf,&key_val,sizeof(key_val))) return EFAULT;
         even_press=0; //数据发完后,立马设为休眠状态,避免误操作
    return 0; } static struct file_operations second_drv_fops={ .owner = THIS_MODULE, .open = second_drv_open, .read = second_drv_read, .release=second_drv_class, //里面添加free_irq函数,来释放中断服务函数 }; volatile int second_major; static int second_drv_init(void) { second_major=register_chrdev(0,"second_drv",&second_drv_fops); //创建驱动 seconddrv_class=class_create(THIS_MODULE,"second_dev"); //创建类名 seconddrv_class_devs=class_device_create(seconddrv_class, NULL, MKDEV(second_major,0), NULL,"buttons"); return 0; } static int second_drv_exit(void) { unregister_chrdev(second_major,"second_drv"); //卸载驱动 class_device_unregister(seconddrv_class_devs); //卸载类设备 class_destroy(seconddrv_class); //卸载类 return 0; } module_init(second_drv_init); module_exit(second_drv_exit); MODULE_LICENSE("GPL v2");

     接下来学习:

    8.中断按键驱动程序之poll机制(详解)

  • 相关阅读:
    Ch’s gift HDU
    String HDU
    Rikka with String HDU
    DNA repair HDU
    Ring HDU
    A Secret HDU
    看详细的tomcat报错信息
    linux tomcat服务器优化配置
    linux常用命令
    关于Context []startup failed due to previous errors有效解决方式
  • 原文地址:https://www.cnblogs.com/lifexy/p/7506718.html
Copyright © 2020-2023  润新知