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\n”);
  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:

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!!!\n");
     return -1;}
 while(1)
 {
     ret=read(fd,&val,1);    //读取一个值,(当在等待队列时,本进程就会进入休眠状态)
     if(ret<0)
     {
     printf("read err!\n");
     continue;
     }
  printf("key_val=0X%x\r\n",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)
{
         even_press=0;
        /*将中断 进入等待队列(休眠状态)*/
         wait_event_interruptible(button_wait, even_press);            

        /*有按键按下,退出等待队列,上传key_val 给用户层*/
         if(copy_to_user(buf,&key_val,sizeof(key_val)))
         return EFAULT;

         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");
时间: 2024-12-23 01:52:37

7.自己写中断方式按键驱动程序的相关文章

内核中断及按键驱动程序

寒假Linux学习笔记 2015年1月25日 晚 20:00 一.内核中断处理 进程上下文:应用程序主动调用内核驱动的程序的跳转 中断上下文:中断由硬件产生的,与应用程序无关 ? ? 1.注册中断 Int request_irq(unsigned int irq, ????????????//中断号 void (*handler)(int ,void *, struct pt_regs*),????//中断处理函数 unsigned long flags,????????????//与中断处理管

在Linux下的中断方式读取按键驱动程序

// 在Linux下的中断方式读取按键驱动程序 //包含外部中断 休眠 加入poll机制 // 采用异步通知的方式 // 驱动程序发 ---> app接收 (通过kill_fasync()发送) // 为了使设备支持异步通知机制,驱动程序中涉及以下3项工作: // 1. 支持F_SETOWN命令,能在这个控制命令处理中设置filp->f_owner为对应进程ID. // 不过此项工作已由内核完成,设备驱动无须处理. // 2. 支持F_SETFL命令的处理,每当FASYNC标志改变时,驱动程序

IMX257实现GPIO-IRQ中断按键驱动程序

IMX257实现GPIO-IRQ中断按键驱动程序 2015-02-18 李海沿 ????昨天我们已经实现了中断查询的方式实现GPIO按键驱动程序,但是,有一个缺点就是,当我们把应用程序放在后台执行时,即便没有按键,应用程序while循环中的read函数也不断的在运行,严重的导致了CPU资源的浪费. ????本文中,我们在前面按键查询驱动程序的基础上来修改. ????大概介绍一下设计思路吧: ????和前面的差不多,当我们加载驱动时,首先在init函数中,对GPIO功能进行模式设置,都设置为GPI

RTX——第19章 SVC 中断方式调用用户函数(后期补历程)

本章节为大家讲解如何采用 SVC 中断方式调用用户函数. 当用户将 RTX 任务设置为工作在非特权级模式时,任务中是不允许访问特权级寄存器的,这个时候使用 SVC 中断,此问题就迎刃而解了. SVC 功能介绍SVC 用于产生系统函数的调用请求.例如,操作系统通常不让用户程序直接访问硬件,而是通过提供一些系统服务函数,让用户程序使用 SVC 发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件.因此,当用户程序想要控制特定的硬件时,它就要产生一个 SVC 异常,然后操作系统提供的SVC

基于输入子系统的按键驱动程序

输入子系统框图: 基于输入子系统的按键驱动程序步骤: 1.分配input_dev结构体 2.设置这个结构体 3.注册 4.硬件相关操作(有数据产生时调用 input_event来上报). 1.分配input_dev结构体 首先要定义这个结构体:static struct input_dev *buttons_dev; 然后在init函数中进行以下操作:buttons_dev = input_allocate_device(); 2.设置这个结构体 使用set_bit来设置这一个位,能设置以下参数

linux驱动之中断方式获取键值

linux驱动之中断方式获取键值 ------------------------------------------------------------------------------------------------------------------------------------------------------ 回想在单片机下的中断处理 分辨是哪个中断 调用处理函数 清中断 --------------------------------------------------

按键驱动程序(异步通知)

此驱动程序之前的按键驱动程序(中断方式)上加以优化.用到异步通知.对于内核来讲,既然用户想得到的是按键后的状态,那么自然不必时时都要read状态.当它检测到中断发生变主动通知用户,用户再来读.这样,用户空间.内核就可以着手干点其它的事情,而不必忙等按键按下或释放.那么就先从应用程序上面看. 怎么设置相关联到"异步通知"呢?flag = fcntl(fd, F_GETFL);fcntl(fd, F_SETFL, flag|FASYNC);这样的两句就可以将文件描述符fd与异步通知关联,到

输入子系统------键盘按键驱动程序

由上一节的输入子系统的框架分析可知,其分三层:设备驱动层,核心层,事件驱动层 我们在为某种设备的编写驱动层,只需要关心设备驱动层,即如何驱动设备并获得硬件数据(如按下的按键数据),然后调用核心层提供的接口,核心层就会自动把数据提交给事件处理层.在输入子系统中,事件驱动是标准的,适用于所有输入类的.我们的设备可以利用一个已经存在的,合适的输入事件驱动,通过输入核心,和用户应用程序接口. 一.编写设备驱动层的流程 1.分配一个input--dev结构体 2.设置input_dev的成员 3.注册in

20150216 IMX257实现GPIO-查询按键驱动程序

20150216IMX257实现GPIO-查询按键驱动程序 2015-02-16 李海沿 前面我们介绍了简单的通用字符设备驱动程序,接下来,我们在它的基础上来实现GPIO的查询按键功能. 先附上驱动程序代码 1 /****************************** 2 linux key_query 3 *****************************/ 4 #include <linux/module.h> 5 #include <linux/init.h>