当前位置: 代码迷 >> 驱动开发 >> 初进android驱动开发之字符设备(四-中断)
  详细解决方案

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

热度:485   发布时间:2016-04-28 09:58:54.0
初入android驱动开发之字符设备(四-中断)

上一篇讲到android驱动开发中,应用是如何去操作底层硬件的整个流程,实现了按键控制led的亮灭。当然,这是一个很简单的实例,不过稍微演变一下,就可以得到广泛的应用。如开发扫描头,应用透过监听上报的按键的键值,监听到,则调用扫描头的模块,成功,则点亮LED灯,并把扫描头解码后的条码信息,通过广播的形式发出。又扯到其它地方,这里主要说说中断。

1. 中断的一些概念

中断,是什么?

中断,可以看成是cpu对特殊事件的一种处理的机制,这类特殊事件一般指紧急事件或者说异常事件。很简单的一个例子,你拿你手机正在看视频,来了一个电话,你接完电话,还是停在视频。本来你的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)

这个是等待队列的机制,当有中断的时候,唤醒,把事件加入工作队列中,处理完事件后,继续休眠,直到下次中断。

3.关于一些调试方法:

一般编写中断的程序,最主要的是要看,gpio口的中断号是否申请成功。这里主要根据打印语句进行调试了。

若驱动程序不报错误了,则可进入android系统下,cat proc/interrupts   ,可查看到你申请成功的中断。

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案