初入android驱动开发之字符设备(四-中断)

大鹏一日同风起,扶摇直上九万里。这篇文章主要讲述初入android驱动开发之字符设备(四-中断)相关的知识,希望能为你提供帮助。
上一篇讲到android驱动开发中,应用是怎样去操作底层硬件的整个流程,实现了按键控制led的亮灭。当然,这是一个非常easy的实例,只是略微演变一下,就能够得到广泛的应用。
如开发扫描头,应用透过监听上报的按键的键值 ,监听到,则调用扫描头的模块。成功,则点亮LED灯,并把扫描头解码后的条码信息。通过广播的形式发出。又扯到其他地方,这里主要说说中断。
1. 中断的一些概念
中断,是什么?
中断。能够看成是cpu对特殊事件的一种处理的机制,这类特殊事件一般指紧急事件或者说异常事件。非常easy的一个样例,你拿你手机正在看视频,来了一个电话。你接完电话,还是停在视频。本来你的cpu正在运行看视频这一系列的指令处理。但当接收到电话,会产生一个中断,cpu依据优先级推断。优先级高于当前则停止当前工作。并保存,然后运行中断的处理函数,其中断这一系列的事件处理完成以后。再运行保存在暂停队列中的工作。这是一个外部中断的样例。
那么中断,是指 CPU 在运行程序的过程中,出现了某些突发事件时 CPU 必须暂停运行当前的程序,转去处理突发事件,处理完成后 CPU 又返回原程序被中断的位置并继续运行。依据中断的来源,中断可分为内部中断和外部中断,内部中断的中断源来自 CPU内部(软件中断指令、溢出、除法错误等,比如,操作系统从用户态切换到内核态需借助 CPU 内部的软件中断),外部中断的中断源来自 CPU 外部,由外设提出请求。

中断,实现它的机制?
中断。当外设发出一个中断信号,cpu则依据中断信号,来进行分析处理,依据中断信号所对于的地址。去调用中断处理函数。所以。中断处理函数。是值 该中断产生后,cpu应该去紧急运行的事件。
那么,这里主要解说一下中断处理函数的机制。

s5pv210是arm架构的芯片,当中断的资源很的丰富,这里有32个外部中断和其余的gpio中断。一般。实际开发中,中断主要由外设发出。所以,这里我们基本都是用的外部中断。採用外部中断的 CPU 通常为不同的中断分配不同的中断号,当检測到某中断号的中断到来后,就自己主动跳转到与该中断号相应的地址运行。
不同中断号的中断有不同的入口地址。
中断处理机制,,Linux 将中断处理程序分解为两个半部:顶半部(top half)和底半部(bottom half)。
在这两者重要的差别,顶半部,不可被中断,而底半部,能够被新的中断打开。那么,这两者之前的差别,就认为了它们各自独特的特性。顶半部,不可被打断,所以注定它的运行时间要很很的高速,所以一般它仅仅是简单的读取寄存器的中断状态并清楚中断标志,然后就把底半部处理程序挂究竟半部运行队列中。而这样,中断处理的大部分工作就落究竟半部了。由于可被打断,相对来说,时间就比較充足。运行一些耗时的任务。
底半部的三种方式:软中断、tasklet、工作队列。
这里有个博文链接。主要将三种机制以及之间的差异。http://blog.chinaunix.net/uid-20768928-id-5077401.html
中断,当中关键的一些函数?
int request_threaded_irq(unsigned int irq, irq_handler_t handler,
irq_handler_t thread_fn, unsigned long irqflags,
const char *devname, void *dev_id)
irq:中断号,这里由gpio_to_irq()方法得到。
handler:发生中断时首先要运行的硬中断处理函数,这个函数能够通过返回 IRQ_WAKE_THREADED唤醒中断线程,也可返回IRQ_HANDLE不运行中断线程
thread_fn : 中断线程,类似 于中断下半部,若传參为null,则和request_irq()一样
qflags:中断标志。备注:IRQF_SHARED 共享中断时,dev_id不能为空。由于释放irq时要区分哪个共享中断。
devname:中断名
dev_id: 传给中断处理函数的參数。

2.简单的实例:

#include < linux/module.h> #include < linux/kernel.h> #include < linux/init.h> #include < linux/irq.h> #include < linux/input.h> #include < linux/platform_device.h> #include < linux/miscdevice.h> #include < mach/gpio.h> #include < linux/io.h> #include < mach/hardware.h> #include < linux/delay.h> #include < asm/irq.h> #include < asm/uaccess.h> #include < linux/interrupt.h> #include < linux/gpio.h> #include < linux/wait.h> #include < linux/sched.h> #include < plat/gpio-core.h> #include < plat/gpio-cfg.h> #include < plat/gpio-cfg-helpers.h> static struct class *buttondrv_class; static struct device *buttondrv_class_dev; int major; volatile unsigned long *GPCCON; volatile unsigned long *GPCDAT; //static DECLARE_WAIT_QUEUE_HEAD(button_waitq); static unsigned char key_val; static volatile int ev_press = 0; struct pin_desc{ unsigned int pin; unsigned int key_val; }; struct pin_desc pins_desc[2] = { {S5PV210_GPH3(7), 0x01}, }; static irqreturn_t buttons_irq(int irq, void *dev_id) { printk(" > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > buttons_irq\n" ); struct pin_desc *pindesc = (struct pin_desc *)dev_id; unsigned int pinval; pinval = gpio_get_value(pindesc-> pin); printk(" irq > > > > > > > > > > > > > > > > > > > > > > > > > > > > pinval =%d \n" ,pinval); if (pinval) { key_val = 0x80 | pindesc-> key_val; printk(" 1111 > > > > > > > > > > > > > > > > > > > > > > > > > > > key_val =%d \n" ,key_val); } else { key_val = pindesc-> key_val; printk(" 0000 > > > > > > > > > > > > > > > > > > > > > > > > > > > key_val =%d \n" ,key_val); } ev_press = 1; //wake_up_interruptible(& button_waitq); return IRQ_RETVAL(IRQ_HANDLED); }static int button_drv_open(struct inode *inode, struct file *file) { printk(" > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > button_drv_open\n" ); int ret=-1; s3c_gpio_setpull(S5PV210_GPH3(7), S3C_GPIO_PULL_NONE); ret = request_threaded_irq(gpio_to_irq(S5PV210_GPH3(7)), NULL, buttons_irq, IRQF_TRIGGER_RISING, " s2" , & pins_desc[0]); printk(" ret=%d irq=%d > > > > > > > > > > > > > > > > > > > > > > > > > \n " ,ret,gpio_to_irq(S5PV210_GPH3(7))); return 0; }int button_drv_close(struct inode *inode, struct file *file) { free_irq(gpio_to_irq(S5PV210_GPH3(7)), & pins_desc[0]); return 0; }static int button_drv_read(struct file *filp, char __user *buf, size_t count, loff_t *offp) { printk(" > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > button_drv_read\n" ); if (count != 1) return -EINVAL; printk(" read > > > > > > > > > > > > > > > > > > > > > > > > > > > key_val =%d \n" ,key_val); // wait_event_interruptible(button_waitq, ev_press); copy_to_user(buf, & key_val, 1); key_val=0; ev_press = 0; return 1; }static struct file_operations button_drv_fops = { .owner=THIS_MODULE, .open=button_drv_open, .read = button_drv_read, .release =button_drv_close, }; static int button_drv_init(void){ printk(" > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > button_drv_init\n" ); GPCCON = (volatile unsigned long *)ioremap(0xE0200C60, 8); GPCDAT= GPCCON + 1; if (!GPCCON) { return -EIO; } major = register_chrdev(0, " button_drv" , & button_drv_fops); buttondrv_class = class_create(THIS_MODULE, " buttondrv" ); buttondrv_class_dev = device_create(buttondrv_class, NULL, MKDEV(major, 0), NULL, " button" ); return 0; }static void button_drv_exit(void){ printk(" > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > button_drv_exit\n" ); unregister_chrdev(major, " button_drv" ); device_unregister(buttondrv_class_dev); class_destroy(buttondrv_class); iounmap(GPCCON); }module_init(button_drv_init); module_exit(button_drv_exit); MODULE_LICENSE(" GPL" );

关于代码一些简单的说明:
static DECLARE_WAIT_QUEUE_HEAD(button_waitq)
wake_up_interruptible(& button_waitq)
wait_event_interruptible(button_waitq, ev_press)
【初入android驱动开发之字符设备(四-中断)】这个是等待队列的机制,当有中断的时候。唤醒。把事件增加工作队列中,处理完事件后。继续休眠,直到下次中断。
3.关于一些调试方法:
一般在编写中断的程序,最基本的是要看,gpio口的中断号是否申请成功。这里主要依据打印语句进行调试了。
若驱动程序不报错误了,则可进入android系统下,cat proc/interrupts   ,可查看到你申请成功的中断。









    推荐阅读