中断-----按键中断驱动程序

本文介绍Linux系统中断处理机制及其实现方法,包括中断注册、处理函数编写,并详细解析了一个具体的按键中断驱动程序实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中断处理

1:外设的处理速度一般慢于CPU2CPU不能一直等待外部事件,所以设备必须有一种方法来通知CPU它的工作进度,这个方法就是中断,外设与CPU信息交互的机制,提高CPU利用率。处理之外还有查询,但是查询会一直占有CPU资源,导致CPU低利用率,好处是实现简单。

Linux系统中为设备实现中断处理程序,需要做两件事,1:向内核注册中断,让Linux内核知道中断的存在。2:实现中断处理函数。

一:中断的注册:int request_irq(unsigned int irq,  irq_handler_t handler,  unsigned long flags, const char *devname, void *dev_id) 返回0表示成功,让内核找到中断函数调用。

参数:irq:中断号, irq_handler_t handler:中断处理函数,flags:  中断标志

Devname:设备名,中断处理程序属于的设备,dev_id:共享中断时使用

flag中断标志:1IRQF_DISABLED 如果设置这个标志,表示是一个“快中断”处理程序,如果没有设置,那么是一个“慢中断”处理程序。

2IRQF_SHARED该位表示中断可在设备间共享。

快速/慢速中断:

快速中断处理程序是不允许发生中断嵌套的,慢速中断允许嵌套。举例:在CPU运行过程中突然某个点产生串口中断,进入串口中断处理程序,处理到某个时间点后产生并口中断,这时会暂停串口中断处理函数处理,转向处理并口中断,执行完后,继续处理串口中断,结束后退回CPU,这就是嵌套情况,在串口执行过程中嵌入并口中断。如果中断嵌套发生就是慢速中断,如果是快速中断情况,在产生并口中断时,丢掉并口中断。默认慢速中断。

共享中断:早期中断源不同的情况,讲不同中断设备挂到同一个中断线上,有相同中断号,通过request_irq函数注册,flags指定IRQF_SHAREDdev_id必须是唯一的,不能使用中断的处理程序disable_irq(unsigned int  irq)

二:中断处理函数:中断处理程序是在中断上下文中运行。流程

void  short_sh_interrupt (int irq,  void *dev_id,  struct pt_regs  *reg) {

//判断该设备是否产生中断,避免共享中断产生的误调用其他设备

value = inb(short_base);

if  (!(value  &  0x80 ))  return ;

//清除中断位,中断位表明该设备是否发生中断

Outb(value & 0x7f  short_base) ;

//中断处理,通常是接受数据

。。。。。。。。。

//唤醒等待数据的进程,可能有进程因发生中断而阻塞

wake_up_interruptible (&short_queue) ;

}

    三:释放中断 void free_irq (unsigned  int  irq,void *dev_id)  

按键中断驱动程序

S3c2440GPIO_G0GPIO_G3GPIO_G5GPIO_G6GPIO_G7GPIO_G11作为输入口,读取按键状态,这6I/O口分别使用外部中断EINT8EINT11, EINT13EINT14, EINT15EINT19。当按键松开时,I/O处于高电平,当按键按下时,I/O处于低电平, 

 

#define DEVICE_NAME  "buttons"
struct button_irq_desc {
	int irq;
	int pin;
	int pin_setting;
	int number;
	char *name;
};

#if !defined  (CONFIG_QQ2440_BUTTONS)
static struct button_irq_desc buttons_irqs[] = {
	{IRQ_EINT8,s3c2410_GPG0,s3c2410_GPG0_EINT8,0,"KEY0"},
	{IRQ_EINT11,s3c2410_GPG3,s3c2410_GPG3_EINT11,1,"KEY1"},
	{IRQ_EINT13,s3c2410_GPG5,s3c2410_GPG5_EINT13,2,"KEY2"},
	{IRQ_EINT15,s3c2410_GPG7,s3c2410_GPG7_EINT15,3,"KEY3"},
	{IRQ_EINT14,s3c2410_GPG6,s3c2410_GPG6_EINT14,4,"KEY4"},
	{IRQ_EINT19,s3c2410_GPG11,s3c2410_GPG11_EINT19,5,"KEY5"},
};
#else
static struct button_irq_desc button_irqs[] = {
{IRQ_EINT8,s3c2410_GPG0,s3c2410_GPG0_EINT8,0,"KEY0"},
	{IRQ_EINT11,s3c2410_GPG3,s3c2410_GPG3_EINT11,1,"KEY1"},
	{IRQ_EINT13,s3c2410_GPG5,s3c2410_GPG5_EINT13,2,"KEY2"},
	{IRQ_EINT15,s3c2410_GPG7,s3c2410_GPG7_EINT15,3,"KEY3"},
	{IRQ_EINT14,s3c2410_GPG6,s3c2410_GPG6_EINT14,4,"KEY4"},
	{IRQ_EINT19,s3c2410_GPG11,s3c2410_GPG11_EINT19,5,"KEY5"},
};
#endif
static volatile char key_values[] = {'0','0','0','0','0','0'};
static DECLARE_WAIT_QUEUE_HEAD (button_waitq);
static volatile int env_press = 0;
static int s3c24xx_button_read (struct file *filp, char __user *buff, size_t count, loff_t *offp)
{//从按键中读数据。
	unsigned long err;
	if(!ev_press) {	
		//ev_press为0,无数据可读,驱动程序默认按照阻塞方式处理
		if(filp->flag & O_NONBLOCK)	
			//O_NONBLOCK标志用户指定不阻塞,如果有O_NONBLOCK,返回
			return -EAGAIN;
		else 
			wait_event_interrupt(button_waitq, ev_press);
			//如果没设置O_NONBLOCK,程序阻塞在等待队列button_waitq
	}
	//有数据往下走
	ev_press = 0;
	err = copy_to_user(buf, (const void *)key_values, min(sizeof(key_value), count));
	//通过按键的键值key_values判断哪个键,读走键值
	return err? = -EFAULT:min(sizeof(key_values),count);
}

static unsigned int s3c24xx_buttons_poll (struct file *file, struct poll_table_struct *wait) 
{
	unsigned int mask = 0;
	poll_wait(file, &button_waitq, wait);//用poll_wait指明使用等待队列button_waitq
	if(ev_press)   //ev_press为1时按键按下,有数据可读
		mask |= POLLIN | POLLRDNORM; //按键可读掩码
	return mask;
}
static irqretum_t buttons_interrupt (int irq, void *dev_id)
{
	struct button_irq_desc *button_irqs = (struct button_irq_desc*)dev_id;
	int down;
	down = !s3c2410_gpio_getpin(button_irq_pin);
	//获取gpio数据寄存器的值,按下时低电平取反down=1
	
	if(down != (key_values[button_irq->number] & 1)) {	//当按下时 1 != 0, 继续 
		key_values[button_irq->number] = '0' + down;	

		ev_press = 1;
		wake_up_interruptable(&button_waitq);
		//不是ev_press改变就唤醒,改变是唤醒条件,真正唤醒的是wake_up_interruptable
	}
	return IRQ_RETVAL(IRQ_HANDLED);
}
static int s3c24xx_buttons_open (struct inode *inode, struct file *file)
{
	int i;
	int err;

	for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if(button_irq[i] < 0) {
			continue;
		}//按键按下高->低电平
		err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH,
			//中断双沿触发,按键按下弹起都会触发,按键按下和弹起都有数据读。
			button_irq[i].name, (void*)&button_irqs[i]);	//为6个按键分别中断处理程序
		if(err)
			break;
	}
	if(err){
		i--;
		for(; i>=0; i--){
			if(button_irqs[i].irq < 0) {
				continue;
			}
			disable_irq(button_irq[i].irq);
			free_irq(button_irq[i].irq, (void *)&button_irq[i]);
		}
		return -EBUSY;
	}
	ev_press = 1;
	return 0;
}
static int s3c24xx_buttons_close (struct inode *inode, struct file *file)
{
	int i;
	int err;

	for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
		if(button_irq[i] < 0) {
			continue;
		}
	free_irq(button_irq[i].irq, (void *)&button_irq[i]);
	}
	return 0;
}
static int s3c24xx_buttons_op[(struct inode *inode, struct file *file)
{
	int i;
	for()
}
static struct file_operations dev_fops = {
	.owner = THIS_MODULE,
	.open = s3c24xx_button_open,
	.release = s3c24xx_button_close,
	.read = s3c24xx_button_read,
	.poll = s3c24xx_button_poll,	//进行多路监控
}
static struct miscdevice misc = {
	.minor = MISC_DYNAMIC_MINOR;
	.name = DEVICE_NAME;
	.fops = &dev_fops;  //字符设备需要file_operation
}
static int __init dev_init (void)
{
	int ret;
	ret = misc_register(&misc);   //主设备号为10,一类字符设备
	return ret;
}
static void __exit dev_exit(void)
{
	misc_deregister(&misc);
}
module_init(dev_init);
module_exit(dev_exit);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值