嵌入式linux驱动中断编程笔记

目录

 

中断申请函数:

中断释放函数:

休眠等待宏:

休眠唤醒宏

实例代码:

程序难点分析:


 

 

中断申请函数:

int request_irq(unsigned int irq, irq_handler_t handler,
        unsigned long irqflags, const char *devname, void *dev_id)

 /*
 *    @irq: 中断号
 *    @handler: 中断服务函数
 *    @irqflags: 触发方式
 *    @devname: 中断的设备名称:自己随便定义
 *    @dev_id:    到时候传递给中断服务函数(handler)的一个指针
 */

中断释放函数:

void free_irq(unsigned int irq, void *dev_id)

 /*
 *    @irq:         中断号
 *    @dev_id:    和上面的相同
 */

休眠等待宏:

#define wait_event_interruptible(wq, condition)    

@wq  :  在驱动函数中自己定义的全局变量 (button_waitq)          static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

@condition  :  状态标志,为0表示没有发生事件,进程休眠等待,为1表示事件发生执行休眠的进程,自己定义:static volatile int ev_press = 0;  

注意:编写驱动的时候把标志置1 或者清零

休眠唤醒宏

#define wake_up_interruptible(x)

@x  同上面的wq  button_waitq    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

实例代码:

#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/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/interrupt.h> 



//编写任务:
//1、中断的申请和注销
//2、中断服务函数的编写 判断按键时放下还是松开
//3、返回键值
//3、中断的唤醒和睡眠




#define ledmajor 250 

static struct class *button_cls ;
static struct device *button_dev;

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 char key_val;
};


struct pin_desc pins_desc[4] = {
	{S3C2410_GPF0, 0x01},
	{S3C2410_GPF2, 0x02},
	{S3C2410_GPG3, 0x03},
	{S3C2410_GPG11, 0x04},
};

static irqreturn_t buttons_irq(int irq, void *dev_id)
{
	struct pin_desc *pindesc = (struct pin_desc*)dev_id;
	unsigned int pinval;

	pinval = s3c2410_gpio_getpin(pindesc->pin);

	if(pinval)
	{
		key_val = 0x80|pindesc->key_val;   //松开按键
	}
	else
	{	
		key_val = pindesc->key_val;   //按下按键
	}

//	printk("key_val = %d",key_val);
	
	ev_press = 1;    //表示有中断发生 标志为1

	wake_up_interruptible(&button_waitq);    //唤醒休眠的进程

	return IRQ_RETVAL(IRQ_HANDLED);
	

}

ssize_t led_read (struct file *filp, char __user *buf , size_t count, loff_t *fops)
{

	int ret;

	printk("read entry\n");

	wait_event_interruptible(button_waitq, ev_press);  //等待中断事件发生			


    ret = copy_to_user(buf, &key_val, count);
	if(ret > 0)
	{
		printk("read filed\n");
		return -EFAULT;
	}
    
	ev_press = 0;  //执行完毕 清楚事件标志

	printk("read leave\n");

	return 0 ;

}

ssize_t led_write (struct file *filp, const char __user * buf, size_t count, loff_t *fops)
{

	int value ;
	int ret ;

	ret = copy_from_user(&value, buf, count);
	if(ret > 0)
	{
		printk("write filed!\n");
		return -EFAULT;
	}

	
	return 0;

}

int led_open (struct inode *inod, struct file *filp)
{

	request_irq(IRQ_EINT0,  buttons_irq, IRQT_BOTHEDGE, "k0", &pins_desc[0]);
	request_irq(IRQ_EINT2,  buttons_irq, IRQT_BOTHEDGE, "k1", &pins_desc[1]);
	request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "k2", &pins_desc[2]);
	request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "k3", &pins_desc[3]);  //资源的申请 出发方式:双边沿触发

	 printk("open ok\n");
	 
	return 0;

}

int led_close(struct inode *inod, struct file *filp)
{
//	printk("close ok\n");

	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]);  //释放中断资源

	printk("close ok\n");

	return 0;
}


const struct file_operations button_fops = {
	.owner   =  THIS_MODULE, 
	.open = led_open ,
	.read = led_read ,
	.write = led_write ,
	.release = led_close ,

};

static int __init button_drv_init(void)
{
	int ret;
	
	ret = register_chrdev(ledmajor , "button_test" , &button_fops) ;  //注册设备号

	if (ret == 0)
		{
			printk("register ok!\n" );

	}
	else
		{

		printk("register filed!\n");
		return -1;
	}

	button_cls = class_create( THIS_MODULE , "button_class");  
	button_dev = device_create(button_cls , NULL , MKDEV(250 , 0), "button_device") ; //创建设备节点
	
	return 0;
	
}

static void __exit button_drv_exit(void)
{

	
	device_destroy(button_cls,  MKDEV(250 , 0));    //注意三者的顺序

	class_destroy(button_cls);

	unregister_chrdev(ledmajor , "button_test") ;    //注销设备号

}


module_init(button_drv_init);
module_exit(button_drv_exit);
MODULE_LICENSE("GPL");


程序难点分析:

首先,应用程序read 按键值,执行wait_event_interruptible 查询标志ev_press,没有事件程序开始休眠,

当有按键按下,执行  buttons_irq     ,

把标志ev_press置1,表明有事件发生,

执行wake_up_interruptible(&button_waitq),唤醒进程

然后回到read刚开始的断点处,查询标志ev_press有事件发生,事件被唤醒,

退出wait_event_interruptible,

继续往下执行,在read函数最后把标志位ev_press置0

发布了135 篇原创文章 · 获赞 112 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/shenlong1356/article/details/88424296