中断驱动

  • 一、查询方式
    • 1、驱动程序
    • 2、应用程序
    • 3、结果测试
  • 二、中断方式
    • 1、中断内部函数实现方式(理论框架)
      • 1.1. 异常处理结构
        • (1)设置异常向量表
        • (2)异常服务函数调用
      • 1.2. 中断处理体系结构
        • (1)中断处理体系结构的初始化: s3c24xx_init_irq
          • (i)底层硬件函数的结构体:chip
          • (ii)入口函数:handle_irq
            • (①)desc->chip->ack(irq)
            • (②)handle_IRQ_event()
        • (2)用户注册中断处理函数:request_irq()
          • (i)action
          • (ii)setup_irq
            • (①)action
            • (②)set_type
            • (③)enable/start
        • (3)用户释放中断处理函数:free_irq
        • (4)中断处理函数:asm_do_irq()
          • (i)irq_desc
          • (ii)desc_handle_irq
    • 2、中断操作实现方式(驱动部分)
      • (1)定义引脚描述结构体
      • (2)用户注册中断
      • (3)用户释放中断
      • (4)中断服务函数
      • (5)加入休眠
      • 总(驱动代码
    • 3、中断操作实现方式(应用程序)
  • 三、poll机制

CPU对于按键的查询方式有以下三种:

  1. main函数中进行while大循环
  2. 中断方式
  3. poll机制

一、查询方式

在之前LED驱动基础上进行修改,通过裸机的学习可以知道只是将IO口属性修改为输入。

1、驱动程序

则以文件类的read()函数对输入的按键值进行读取。进入文件类的定义源代码可以查找到以下关于read()的原函数定义代码:

ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

所以根据要求将read()函数编写为以下形式,其中实现了通过查找寄存器DATVAL来判断是哪个按键按下执行。

ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{// buf is level of four button pin unsigned char button_val[4];int datval;if(size != sizeof(button_val))return -EINVAL;    datval = *gpfdat;button_val[0] = (datval & (1<<0)) ? 1 : 0;button_val[1] = (datval & (1<<2)) ? 1 : 0;datval = *gpgdat;button_val[2] = (datval & (1<<3)) ? 1 : 0;button_val[3] = (datval & (1<<11)) ? 1 : 0;copy_to_user(buf, button_val, sizeof(button_val));return 4;
}

则通过上一篇的LED驱动程序,再添加读取函数read()作为按键读取函数,并且以open()函数为按键的初始化函数。则驱动函数如下:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/compiler.h>
#include <linux/err.h>#define DEV_NAME "button_drv"  // device namestatic struct class *buttondrv_class;
static struct class_device    *buttondrv_class_dev;volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpgcon = NULL;
volatile unsigned long *gpgdat = NULL;static int button_drv_open(struct inode *inode, struct file *file)
{*gpfcon &= ~((3<<8) | (3<<10) | (3<<12));   // input// *gpfcon |=  ((1<<8) | (1<<10) | (1<<12));     *gpgcon &= ~((3<<8) | (3<<10) | (3<<12));   // input// *gpgcon |=  ((1<<8) | (1<<10) | (1<<12));return 0;
}ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{// buf is level of four button pin unsigned char button_val[4];int datval;if(size != sizeof(button_val))return -EINVAL;    datval = *gpfdat;button_val[0] = (datval & (1<<0)) ? 1 : 0;button_val[1] = (datval & (1<<2)) ? 1 : 0;datval = *gpgdat;button_val[2] = (datval & (1<<3)) ? 1 : 0;button_val[3] = (datval & (1<<11)) ? 1 : 0;copy_to_user(buf, button_val, sizeof(button_val));return 4;
}static struct file_operations button_drv_fops = {.owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open   =   button_drv_open,     .read   =   button_drv_read,
};
int major;
static int button_drv_init(void)
{major = register_chrdev(0, DEV_NAME, &button_drv_fops); // 注册, 告诉内核// register chrdev. to major (major, "drv name", file operations);buttondrv_class = class_create(THIS_MODULE, "buttondrv");if(IS_ERR(buttondrv_class)){printk( "ERROR creat key class");}buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "button"); /* /dev/xyz */if(buttondrv_class_dev == NULL){printk("ERROR creat dev");}gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //remap button GPFCPNgpfdat = gpfcon + 1;                                        //remap button GPFDATgpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); //remap button GPGCPNgpgdat = gpgcon + 1;                                        //remap button GPGDATreturn 0;
}static void button_drv_exit(void)
{unregister_chrdev(major, DEV_NAME); // 卸载class_device_unregister(buttondrv_class_dev);class_destroy(buttondrv_class);iounmap(gpfcon);           // unmap virtual address
}module_init(button_drv_init); //When install drv, system will find init function
module_exit(button_drv_exit);   // Uinstall drvMODULE_LICENSE("GPL");

2、应用程序

想必都看烂了,闭着眼睛都能写,所以就直接上程序吧

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>// button_drv int main(int argc, char **argv)
{int fd;int count = 0;unsigned char button_val[4];fd = open("/dev/button", O_RDWR);if (fd < 0){printf("can't open!\n");}if(argc != 1){printf("error parterner\n");printf("%s\n",argv[0]);return 0;}while(1){read(fd, button_val, sizeof(button_val));if(!button_val[0] || !button_val[1] ||!button_val[2] ||!button_val[3]){count++;printf("count: %d key state: %d %d %d %d\n",count, button_val[0], button_val[1],button_val[2], button_val[3]);}}return 0;
}

3、结果测试

运行应用程序,在按下对应的按键按下之后会直接出现很多查询出的值。
缺点:由于不断的查询方式读按键值。占用CPU资源非常多,通过top命令可以看到CPU的资源在该应用程序消耗绝大部分。
改进方式:中断方式查询按键值

二、中断方式

1、中断内部函数实现方式(理论框架)

比起裸机中的中断服务,linux内核中中断的实现方式是通过函数进行封装的。所以在使用的时候只需要调用接口函数即可,在这里对其内部函数进行分析。

1.1. 异常处理结构

中断驱动和之前裸跑中断的实现方式道理是一样的。复习下中断的步骤,一共三个操作:

  1. 按键按下
  2. 出现异常,跳到异常向量入口
  3. 完成中断操作,其中保存现场、执行中断函数(asm_do_irq)、恢复现场

(1)设置异常向量表

由于在驱动中使用的地址为虚拟地址,所以在程序初始化的时候应该把中断向量表在虚拟地址中建立起来,内核中利用trap_init()函数对其进行构建,对于ARM架构异常向量表的虚拟地址可以为:0x00000000或者0xffff0000,在Linux中一般使用0xffff0000。
所以该函数的作用就是将异常向量表的内容拷贝至0xffff0000处,这位子如果不确定我们可以通过查看内核中的 .config 文件,其中CONFIG_VECTORS_BASE = 0xffff0000,表示了异常向量表的地址。
在内核中的异常向量表实现的方式和裸机中是一模一样的,通过跳转指令实现。

(2)异常服务函数调用

这里以中断服务函数为例子

  1. trap_init() 设置异常向量表,发生中断时跳至vector_irq (汇编中实现,和裸机中相同)
  2. vector_irq 中保存现场
  3. vector_irq 中调用中断总入口函数asm_do_irq()(C函数,中断服务函数)
  4. vector_irq 恢复现场
    则异常处理结构可以由下图表示:

1.2. 中断处理体系结构

断处理体系结构:

(1)中断处理体系结构的初始化: s3c24xx_init_irq

内核中断处理体系结构的搭建任务主要落在init_IRQ()函数,他直接由srart_kernel()函数来调用,定义于arch/arm/kernel/irq.c。定义如下:

void __init init_IRQ(void)
{int irq; //中断号for(irq = 0; irq < NR_IRQS; irq++)irq_desc[irq].status|= IRQ_NOREQUEST | IRQ_NOPROBE; //通过把每一个中断号对应结构体成员“status”状态字段设置成未请求和未探测状态init_arch_irq();
}

其中,最后一步init_arch_irq实际上是一个函数指针,最初由setup_arch()函数来设置这个指针

void __init setup_arch(char **cmdline_p)
{...init_arch_irq= mdesc->init_irq; //将void型指针init_arch_irq强制指向struct machine_desc结构体的init_irq成员...
}

接着一级一级递进,mdesc->init_irq的初始化在如下操作中定义:

MACHINE_START(S3C2440, "TQ2440").phys_io = S3C2410_PA_UART,.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) &0xfffc,.boot_params    = S3C2410_SDRAM_PA + 0x100,.init_irq = s3c24xx_init_irq, //在这.map_io      = tq2440_map_io,.init_machine     = tq2440_machine_init, .timer         = &s3c24xx_timer,
MACHINE_END

在这里将mdesc->init_irq 赋值了s3c24xx_init_irq函数,所以我们的init_arch_irq()函数即是s3c24xx_init_irq()

在递进一步来分析s3c24xx_init_irq()
在这个函数里主要初始化中断处理体系结构中的handle_irq和chip*

void _init s3c24xx_init_irq()
{...set_irq_chip(irqno, &s3c_irqext_chip);set_irq_handler(irq, handle_edge_irq);...
}

PS: 这里调用的函数不一定是handle_edge_irq,但是大体结构相同

(i)底层硬件函数的结构体:chip

在这里chip = s3c24xx系列底层访问函数。在handle_irq中调用。
在例子里使用的chip结构体定义如下:

static struct irq_chip s3c_irqext_chip = {.name     = "s3c-ext",.mask     = s3c_irqext_mask,   //屏蔽中断源.unmask   = s3c_irqext_unmask, //开启接收中断源.ack      = s3c_irqext_ack,    //响应中断:通常是清除当前中断使得可以接收下一个中断.set_type = s3c_irqext_type,   //触发方式.set_wake = s3c_irqext_wake    //唤醒相关
};
(ii)入口函数:handle_irq

handle_irq的设置是通过set_handle_irq实现的,即有handle_irq = handle_edge_irq,并且通过分析源码可以得到handle_edge_irq由以下两部分组成:

handle_edge_irq()
{...desc->chip->ack(irq);...handle_IRQ_event();...
}
(①)desc->chip->ack(irq)

作用:清中断

(②)handle_IRQ_event()

作用:中断处理
方式:取出action链表中的成员,然后指向action中的handler中断服务函数

(2)用户注册中断处理函数:request_irq()

这一步主要设置:action链表
以下是用户注册中断处理函数的源码:

int request_irq(unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
{struct irqaction *action;int retval;#ifdef CONFIG_LOCKDEP/** Lockdep wants atomic interrupt handlers:*/irqflags |= IRQF_DISABLED;
#endif/** Sanity-check: shared interrupts must pass in a real dev-ID,* otherwise we'll have trouble later trying to figure out* which interrupt is which (messes up the interrupt freeing* logic etc).*/if ((irqflags & IRQF_SHARED) && !dev_id)return -EINVAL;if (irq >= NR_IRQS)return -EINVAL;if (irq_desc[irq].status & IRQ_NOREQUEST)return -EINVAL;if (!handler)return -EINVAL;action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);if (!action)return -ENOMEM;action->handler = handler;action->flags = irqflags;cpus_clear(action->mask);action->name = devname;action->next = NULL;action->dev_id = dev_id;select_smp_affinity(irq);#ifdef CONFIG_DEBUG_SHIRQif (irqflags & IRQF_SHARED) {/** It's a shared IRQ -- the driver ought to be prepared for it* to happen immediately, so let's make sure....* We do this before actually registering it, to make sure that* a 'real' IRQ doesn't run in parallel with our fake*/if (irqflags & IRQF_DISABLED) {unsigned long flags;local_irq_save(flags);handler(irq, dev_id);local_irq_restore(flags);} elsehandler(irq, dev_id);}
#endifretval = setup_irq(irq, action);if (retval)kfree(action);return retval;
}

传入参数分析:

  1. irq:注册中断函数对应的中断号
  2. handler:中断服务函数
  3. irqflags:中断触发方式(第一个字节代表电平触发方式,第二个字节代表声明)
  4. devname:自定义的中断名
  5. dev_id:传入中断服务函数的参数,可以为任意形式(大多数是一个结构体指针)
(i)action

在这个数据结构里面保存着中断初始化中的所有的成员

(ii)setup_irq

这个函数有三个作用:

(①)action

把在request_irq中设置的irq_action数据结构,添加至链表action中。
如果该中断号对应的action链表里面有值,则判断以下条件:

  1. 是否irq_falgs声明为可共享的(IRQF_SHARE)
  2. 是否有相同的irq_flags电平触发方式

如果满足该条件,则将新建的数据结构irq_action链入至action中

(②)set_type

把引脚设置为中断引脚,由传入的irqflags来设置

(③)enable/start

这个时候初始就注册完毕,中断使能了。除非将irq_desc[irq].status = IRQ_NOAUTOEN(表示注册时不使能中断)。

(3)用户释放中断处理函数:free_irq

其分为两部对中断进行卸载

  1. 使用irq判断是在哪个irq_desc
  2. 使用dev_id判断是哪个action结构,并删除该action结构
  3. 如果删除后链表空,则屏蔽该中断
    内核源码定义如下:
void free_irq(unsigned int irq, void *dev_id)
{struct irq_desc *desc;struct irqaction **p;unsigned long flags;irqreturn_t (*handler)(int, void *) = NULL;WARN_ON(in_interrupt());if (irq >= NR_IRQS)return;desc = irq_desc + irq;spin_lock_irqsave(&desc->lock, flags);p = &desc->action;for (;;) {struct irqaction *action = *p;if (action) {struct irqaction **pp = p;p = &action->next;if (action->dev_id != dev_id)continue;/* Found it - now remove it from the list of entries */*pp = action->next;/* Currently used only by UML, might disappear one day.*/
#ifdef CONFIG_IRQ_RELEASE_METHODif (desc->chip->release)desc->chip->release(irq, dev_id);    //释放对应的action
#endifif (!desc->action) {desc->status |= IRQ_DISABLED;if (desc->chip->shutdown)desc->chip->shutdown(irq);elsedesc->chip->disable(irq);      //屏蔽中断}spin_unlock_irqrestore(&desc->lock, flags);unregister_handler_proc(irq, action);/* Make sure it's not being used on another CPU */synchronize_irq(irq);if (action->flags & IRQF_SHARED)handler = action->handler;kfree(action);return;}printk(KERN_ERR "Trying to free already-free IRQ %d\n", irq);spin_unlock_irqrestore(&desc->lock, flags);return;}
#ifdef CONFIG_DEBUG_SHIRQif (handler) {/** It's a shared IRQ -- the driver ought to be prepared for it* to happen even now it's being freed, so let's make sure....* We do this after actually deregistering it, to make sure that* a 'real' IRQ doesn't run in parallel with our fake*/handler(irq, dev_id);}
#endif
}

综上,request_irq()所做的事情有:
5. 将irq_desc[irq]中的action链表已近链入了用户注册中断函数中的irq_action
6. 设置中断方式
7. 使能中断

(4)中断处理函数:asm_do_irq()

中断处理体系结构就是asm_do_irq()中中断服务的实现方式。在裸机中的中断总入口函数的处理步骤为:

  1. 分辨是哪个中断

  2. 调用处理函数

  3. 清中断

在中断总入口函数asm_do_irq()进行中断服务的步骤为

  1. 根据中断号调用irq_desc数组项中的handle_irq

  2. handle_irq会用chip的成员中的函数来设置硬件,比如清除中断、禁止中断、重新使能中断等

  3. handle_irq逐个调用action链表中的中断服务函数

以下是中断总入口函数的源码:

asmlinkage void __exception asm_do_IRQ(unsigned int irq, struct pt_regs *regs)
{struct pt_regs *old_regs = set_irq_regs(regs);struct irq_desc *desc = irq_desc + irq;/** Some hardware gives randomly wrong interrupts.  Rather* than crashing, do something sensible.*/if (irq >= NR_IRQS)desc = &bad_irq_desc;irq_enter();desc_handle_irq(irq, desc);/* AT91 specific workaround */irq_finish(irq);irq_exit();set_irq_regs(old_regs);
}

接着对应着中断服务的步骤对其代码进行分析

(i)irq_desc

这是一个中断描述的数组irq_desc[irq],他以中断号为下标。即实现分辨是哪个中断。
*desc = irq_desc + irq;代表着对应的中断的结构体数组项地址赋值给desc。
这个结构体的成员如下所示

struct irq_desc {irq_flow_handler_t  handle_irq;struct irq_chip     *chip;struct msi_desc     *msi_desc;void            *handler_data;void            *chip_data;struct irqaction    *action;    /* IRQ action list */unsigned int        status;     /* IRQ status */unsigned int        depth;      /* nested irq disables */unsigned int        wake_depth; /* nested wake enables */unsigned int        irq_count;  /* For detecting broken IRQs */unsigned int        irqs_unhandled;spinlock_t      lock;
#ifdef CONFIG_SMPcpumask_t       affinity;unsigned int        cpu;
#endif
#if defined(CONFIG_GENERIC_PENDING_IRQ) || defined(CONFIG_IRQBALANCE)cpumask_t       pending_mask;
#endif
#ifdef CONFIG_PROC_FSstruct proc_dir_entry   *dir;
#endifconst char      *name;
}

在执行中断服务函数的时候,irq_desc在初始化的时候就已经被设置好,所以能够直接调用。其中包括了主要的成员为:

  1. chip:
  2. handle_irq:
  3. action:
(ii)desc_handle_irq

其中源码如下所示:

static inline void desc_handle_irq(unsigned int irq, struct irq_desc *desc)
{desc->handle_irq(irq, desc);
}

可以看出这是利用之前设置的desc来调用中断号对应的中断处理函数。其中desc->handle_irq()在之前初始化中体现,其中调用的是注册函数中的中断服务函数。最终实现中断服务函数的调用。

2、中断操作实现方式(驱动部分)

(1)定义引脚描述结构体

为了完成我们实现的功能,引脚结构体需要有的成员为:

  1. 查询按键是否按下对应的寄存器(gpfdat、gpgdat的虚拟地址)
  2. 按键对应的引脚
  3. 按键对应的号码
    即设置引脚描述结构体数组为:
struct pin_desc{volatile unsigned long *key_Register;int key_pin;int key_num;
};struct pin_desc pins_desc[4] = {{0x01, 0,  1},{0x02, 2,  2},{0x03, 3,  3},{0x04, 11, 4},
};

其中寄存器为随便定义一个,具体操作在open函数中定义。

(2)用户注册中断

实现注册中断操作在open()函数中完成。在open()函数neural主要完成两个任务:

  1. 引脚描述结构体数组初始化
  2. 注册中断
    由register_irq()函数的定义:
int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)

open()函数实现代码为:

static int button_drv_open(struct inode *inode, struct file *file)
{pins_desc[0].key_Register = gpfdat;pins_desc[1].key_Register = gpfdat;pins_desc[2].key_Register = gpgdat;pins_desc[3].key_Register = gpgdat;printk("%d\n",&pins_desc[0]);request_irq(IRQ_EINT0,  button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);request_irq(IRQ_EINT2,  button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);return 0;
}

其中button_irq为中断服务函数

(3)用户释放中断

有注册就有释放,首先在file_operations声明需要使用释放函数。
通过查找定义可以知道release的定义如下:

int (*release) (struct inode *, struct file *); 

故我们在函数EINTkey_close()中释放中断,其代码如下:

static int EINTkey_close(struct inode *inode, struct file *file)
{// FREE IRQ 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;
}

所以file_operation如下所示为:

struct file_operations button_drv_fops = {.owner   =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open    =   button_drv_open,     .read    =   button_drv_read,.release =   EINTkey_close,
};

(4)中断服务函数

思想:通过注册中断中传入的结构体来判断那哪一个引脚应该动作

static irqreturn_t button_irq(int irq, void *dev_id)
{struct pin_desc *pindesc = (struct pin_desc *)dev_id;key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1;if(key_val){//    printk("%d", pindesc -> key_num);}printk("%d  ",key_val);printk("%d\n",pindesc->key_num);return IRQ_HANDLED;
}

(5)加入休眠

由于咱们使用中断的初衷是为了减少对CPU的使用,照原来的在主函数中大while循环肯定是不行的,所以加入休眠程序。
使用方法:

  1. DECLARE_WAIT_QUEUE_HEAD(name)
    -----生成一个等待队列头wait_queue_head_t,名字为name
  2. wait_event_interruptible ( queue , condition )
    -----当 condition ( 一个布尔表达式 ) 为真,立即返回
    -----否则让进程进入TASK_INTERRUPTIBLE 模式,并挂在 queue 参数所指定的等待队列上,进入休眠
  3. wake_up_interruptible ( wait_queue_t *q )
    -----从等待队列 q 中唤醒状态为 TASK_INTERRUPTIBLE 的进程
    所以思路为在read()中对主程序进行休眠操作,并在中断程序中唤醒,如下流程图表示

    所以改写的button_irq和read函数如下所示:
static irqreturn_t button_irq(int irq, void *dev_id)
{struct pin_desc *pindesc = (struct pin_desc *)dev_id;key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1;if(key_val){//    printk("%d", pindesc -> key_num);}printk("%d  ",key_val);printk("%d\n",pindesc->key_num);ev_press = 1;                  /* 表示中断发生了 */wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */return IRQ_HANDLED;
}
ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{if(size != -1)return -EINVAL;    /* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */copy_to_user(buf, &key_val, 1);ev_press = 0;return 1;
}

总(驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/compiler.h>
#include <linux/err.h>
#include <linux/irqreturn.h>
#include <linux/irq.h>
#include <asm/arch-s3c2410/regs-gpio.h>
#define DEV_NAME "button_drv"  // device namestatic struct class *buttondrv_class;
static struct class_device    *buttondrv_class_dev;static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
/* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
static volatile int ev_press = 0;/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val;volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;
volatile unsigned long *gpgcon = NULL;
volatile unsigned long *gpgdat = NULL;struct pin_desc{volatile unsigned long *key_Register;int key_pin;int key_num;
};struct pin_desc pins_desc[4] = {{0x01, 0,  1},{0x02, 2,  2},{0x03, 3,  3},{0x04, 11, 4},
};static irqreturn_t button_irq(int irq, void *dev_id)
{struct pin_desc *pindesc = (struct pin_desc *)dev_id;key_val = ((*(pindesc -> key_Register)) & (1<<(pindesc -> key_pin))) ? 0 : 1;if(key_val){//    printk("%d", pindesc -> key_num);}printk("%d  ",key_val);printk("%d\n",pindesc->key_num);ev_press = 1;                  /* 表示中断发生了 */wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */return IRQ_HANDLED;
}static int button_drv_open(struct inode *inode, struct file *file)
{pins_desc[0].key_Register = gpfdat;pins_desc[1].key_Register = gpfdat;pins_desc[2].key_Register = gpgdat;pins_desc[3].key_Register = gpgdat;printk("%d\n",&pins_desc[0]);request_irq(IRQ_EINT0,  button_irq, IRQT_BOTHEDGE, "S2", &pins_desc[0]);request_irq(IRQ_EINT2,  button_irq, IRQT_BOTHEDGE, "S3", &pins_desc[1]);request_irq(IRQ_EINT11, button_irq, IRQT_BOTHEDGE, "S4", &pins_desc[2]);request_irq(IRQ_EINT19, button_irq, IRQT_BOTHEDGE, "S5", &pins_desc[3]);return 0;
}
ssize_t button_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{if(size == -1)return -EINVAL;    /* 如果没有按键动作, 休眠 */wait_event_interruptible(button_waitq, ev_press);/* 如果有按键动作, 返回键值 */copy_to_user(buf, &key_val, 1);ev_press = 0;return 1;
}static int EINTkey_close(struct inode *inode, struct file *file)
{// FREE IRQ 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 struct file_operations button_drv_fops = {.owner   =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open    =   button_drv_open,     .read    =   button_drv_read,.release =   EINTkey_close,
};
int major;
static int button_drv_init(void)
{major = register_chrdev(0, DEV_NAME, &button_drv_fops); // 注册, 告诉内核// register chrdev. to major (major, "drv name", file operations);buttondrv_class = class_create(THIS_MODULE, "buttondrv");if(IS_ERR(buttondrv_class)){printk( "ERROR creat key class");}buttondrv_class_dev = class_device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/xyz */if(buttondrv_class_dev == NULL){printk("ERROR creat dev");}gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16); //remap button GPFCPNgpfdat = gpfcon + 1;                                        //remap button GPFDATgpgcon = (volatile unsigned long *)ioremap(0x56000060, 16); //remap button GPGCPNgpgdat = gpgcon + 1;                                        //remap button GPGDATreturn 0;
}static void button_drv_exit(void)
{unregister_chrdev(major, DEV_NAME); // 卸载class_device_unregister(buttondrv_class_dev);class_destroy(buttondrv_class);iounmap(gpgcon);iounmap(gpfcon);           // unmap virtual address
}module_init(button_drv_init); //When install drv, system will find init function
module_exit(button_drv_exit);   // Uinstall drvMODULE_LICENSE("GPL");

3、中断操作实现方式(应用程序)

按照休眠的思路只要while read()函数就行了,在基础上在加上一个输出按键值操作,其代码如下:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>// button_drv int main(int argc, char **argv){int fd;int count = 0;unsigned char button_val[4];fd = open("/dev/button", O_RDWR);if (fd < 0)  printf("can't open!\n");if(argc != 1)    {printf("error parterner\n");printf("%s\n",argv[0]);return 0;}while(1)read(fd, button_val, sizeof(button_val));  return 0;
}

三、poll机制

本篇太长了所以接着下一节博文内容,连接如下:
S3C2440 开发板实战(9):poll机制

S3C2440 开发板实战(8):中断驱动相关推荐

  1. 【Verilog】基于Nexys4DDR开发板实现数字钟

    功能: 基于Nexys4DDR开发板实现的数字钟,六位数码管显示时分秒,可切换24时制/12时制,有整点报时功能(led灯闪烁). Verilog代码: `timescale 1ns / 1ps//数 ...

  2. 基于PYNQ-Z2开发板实现矩阵乘法加速详细流程

    基于PYNQ-Z2开发板实现矩阵乘法加速 主要内容 1.在Vivado HLS中生成矩阵乘法加速的IP核. 2.在Vivado中完成Block Design. 3.在Jupyter Notebook上 ...

  3. 移植根文件系统到linux内核 s3c2440,u-boot-2011.06在基于s3c2440开发板的移植之引导内核与加载根文件系统...

    三.根文件系统的制作 我们利用busybox来制作根文件系统 1.在下列网站下载busybox-1.15.0.tar.bz2 在当前目录下解压busybox tar -jxvf busybox-1.1 ...

  4. 利用粤嵌LinuxGEC6818开发板实现电子相册

    实验目的 利用粤嵌LinuxGEC6818开发板实现电子相册,要求如下: 实验操作必须在Linux操作系统下完成 源代码模块化设计 实现水平或者垂直滑动切换图片 实验步骤 因为操作需要在Linux下运 ...

  5. 【微信小程序控制硬件16 】 安信可 ESP32-S 开发板实现移植腾讯物联开发平台蓝牙 llsync 协议,实现一键蓝牙快速配网+远程控制。(附带源码)

    文章目录 一.前言 二.源码目录说明 三.编译指导 四.常见问题 五.开源微信物联网控制一览表 另外,不要把我的博客作为学习标准,我的只是笔记,难有疏忽之处,如果有,请指出来,也欢迎留言哈! 微信物联 ...

  6. Mixly(米思齐)的安装以及基于Arduino开发板实现电容触摸控制灯

    Mixly(米思齐)的安装以及基于Arduino开发板实现电容触摸控制灯 1.Mixly下载 http://mixly.org/bnu-maker/mixly-arduino-win Mixly软件安 ...

  7. GEC6818开发板JPG图像显示,科大讯飞离线语音识别包Linux_aitalk_exp1227_1398d7c6运行demo程序,开发板实现录音

    GEC6818开发板JPG图像显示 | 开发板实现录音 一.GEC6818开发板JPG图像显示 1.jpg图片特性 2.如何解压缩jpg图片 1.对jpegsrc.v8c.tar.gz进行arm移植 ...

  8. CC2540 CC2541 蓝牙4.0BLE开发板实现 空中飞鼠、体感游戏手柄【多图】

    蓝牙4.0BLE开发板实现空中飞鼠.体感游戏手柄 近年来开始流行空中飞鼠与体感游戏手柄,正好我们的开发板上设计有插入mpu6050六轴传感器的适配接口,我们中秋节期间兴趣使然,实现了这么一个方案.   ...

  9. 基于STM32开发板实现传感数据采集-DHT11温湿度采集

    基于STM32开发板实现传感数据采集-DHT11温湿度采集 一.项目简介 本次项目是基于STM32开发板实现传感数据采集-DHT11温湿度采集.采用ARM结构中最为代表的Cortex-M4系列的芯片, ...

最新文章

  1. 技术 | Bengio终结Theano不是偶然,其性能早在Keras支持的四大框架中垫底
  2. LeetCode 1470. 重新排列数组
  3. 那些被大数据时代抛弃的人
  4. java线程池执行器_Java线程池ThreadPoolExecutor的使用
  5. 蓝色版去水印小程序源码+接口
  6. SLAM GMapping(5)运动模型
  7. React与ES6(一)开篇介绍
  8. 【WPF】设置DataGrid表头内容居中显示
  9. oracle存储过程id递增,oracle存储过程——按id更新相关信息
  10. Vova and Train (codeforces 1066A)
  11. Linux 终端快捷键
  12. 消费者人群画像-信用智能评分(金融风控模型经典案例)
  13. ubuntu更改文件夹权限
  14. 软件测试行业中ta表示什么意思,软件测试架构师(TA)的职位特点
  15. 获取手机唯一标识插件_H5能获取到手机设备ID或者手机浏览器唯一识别码吗
  16. 软硬件学习记录5—— Verilog语言中的数据类型
  17. 几种方法判断平面点在三角形内
  18. PHP之两个日期之间相差天数
  19. salesforce lightning 入门(一)
  20. 运筹说 第85期 | 只有初中学历的数学家

热门文章

  1. 用vs2017写php,windows下开发php7.2拓展,vs2017
  2. 与传统招聘方式相比,小程序招聘都有哪些优势?
  3. python 键盘记录_记录键盘敲击次数 python实现
  4. 用一个uchar 类型表示八个通道的状态
  5. VMWare虚拟机无法开启,显示模块“Disk”启动失败
  6. MySQL安全登录策略
  7. android usb wifi驱动下载,android 平台USB wifi驱动移植及使用
  8. 论文笔记1 | 使用CTC对湍流工业火焰进行瞬时三维重建
  9. 【xlrd读取Excel日期】使用xlrd读取Excel日期格式单元格的方法
  10. 关于Sun公司的SCJP认证