Linux驱动开发_内核中断注册与工作队列

举报
DS小龙哥 发表于 2022/04/28 23:39:23 2022/04/28
【摘要】 完成编写LED灯驱动,在应用层控制LED灯、编写按键驱动,在应用层打印出按键值、在应用层实现按键控制LED灯。

任务1: 完成上一次的作业

作业:

1.​ 编写LED灯驱动,在应用层控制LED灯。

2.​ 编写按键驱动,在应用层打印出按键值。

3.​ 在应用层实现按键控制LED灯。(程序里需要安装两个驱动)



任务2: 内核中断处理(按键为例)

​ 裸机的中断(STM32为例):

1.​ 中断线

2.​ 中断号(设置优先级、使能)

3.​ 中断服务函数

4.​ 建立中断向量表(是中断入口函数地址)


 Linux操作系统下中断:

1.​ 编写中断服务函数

2.​ 注册中断 request_irq

3.​ 注销中断 free_irq

4.​ 获取中断号(通过GPIO口编号进行获取)

5.​ 产生中断的属性: 边沿触发。

6.​ 中断向量表(在Linux内核不需要写,它是在UBOOT阶段建立的)


Linux系统下中断处理

1.​ 内核中断服务函数里不能出现会导致系统休眠的函数。比如: mdelay

2.​ 内核中断处理分为上文和下文(真正做事情的地方)。

3.​ 内核中断下文一般使用工作队列或者内核的微线程实现。

4.​ 内核中断上文一般就调度工作队列或者内核微线程。

注明:工作队列和内核微线程里可以使用休眠函数。


查看内核所有中断的信息: [root@tiny4412 ]#cat /proc/interrupts


任务3: 工作队列

工作队列是将任务推后执行的一种机制,什么时候执行? CPU空闲的时候执行。

工作: 就是一个结构体,结构体里保存了工作对应的函数(函数指针)。

队列: 是一个链表形式,每一个链表的节点就是一个工作结构体。

这个队列由内核的一个线程轮询执行,当内核执行了工作之后,该工作结构体就会从链表节点里删除掉。

注意事项: 如果之前加入的工作已经在工作队列里,还没有被执行,这个时候不能再重复添加。

在Linux内核里有默认的工作队列: 共享工作队列。 也可以自己去创建自己的队列。

练习:

1.​ 将上课代码全部写一遍: 作业、中断、工作队列

2.​ 试着使用内核定时器给按键进行消抖。

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/gpio.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>

static struct work_struct key_work; /*工作结构体*/
static struct KEY_INFO *key_p=NULL;

/*定义结构体,保存一个按键的所有信息*/
static struct KEY_INFO
{
	int gpio;	/*GPIO口编号*/
	int flag;	/*触发边沿标志*/
	int key; 	/*按键值*/
	char *name;/*中断的名称*/
	int irq; 	/*中断号*/
};

/*初始化按键信息*/
static struct KEY_INFO key_buff[]=
{
	{EXYNOS4_GPX3(2),IRQ_TYPE_EDGE_BOTH,0x1,"tiny4412_key1"},
	{EXYNOS4_GPX3(3),IRQ_TYPE_EDGE_BOTH,0x2,"tiny4412_key2"},
	{EXYNOS4_GPX3(4),IRQ_TYPE_EDGE_BOTH,0x3,"tiny4412_key3"},
	{EXYNOS4_GPX3(5),IRQ_TYPE_EDGE_BOTH,0x4,"tiny4412_key4"},
};

/*工作处理函数*/
static void tiny4412_work_func(struct work_struct *work)
{
	static int key_state=0;
	if(key_p==NULL)return;
	msleep(50); /*消抖50ms*/
	if(!gpio_get_value(key_p->gpio))
	{
		key_state=1; /*表示按键已经按下*/
		printk("按键值:0x%x\n",key_p->key);
	}
	else
	{
		if(key_state)
		{
			key_state=0;/*表示按键已经松开*/
			printk("按键值:0x%x\n",key_p->key|0x80);	
		}
	}
}

/*按键的中断服务函数*/
static irqreturn_t tiny4412_keyirq_handler(int irq, void *dev)
{
	key_p=(struct KEY_INFO *)dev;
	/*调度工作队列(系统共享工作队列)*/
	schedule_work(&key_work);
	return IRQ_HANDLED;
}

static int __init tiny4412_interrupt_dev_init(void) 
{
	int i;
	/*初始化工作*/
	INIT_WORK(&key_work,tiny4412_work_func);
	
	for(i=0;i<sizeof(key_buff)/sizeof(key_buff[0]);i++)
	{
		/*获取中断编号*/
		key_buff[i].irq=gpio_to_irq(key_buff[i].gpio);
		/*注册中断*/
		request_irq(key_buff[i].irq,tiny4412_keyirq_handler,key_buff[i].flag,key_buff[i].name,&key_buff[i]);
		printk("%s的中断号:%d\n",key_buff[i].name,key_buff[i].irq);
	}
	printk("中断:驱动安装成功!\n");
	return 0;
}

static void __exit tiny4412_interrupt_dev_exit(void) 
{
	int i;
	for(i=0;i<sizeof(key_buff)/sizeof(key_buff[0]);i++)
	{
		/*释放中断号*/
		free_irq(key_buff[i].irq,&key_buff[i]);
	}
	
	/*销毁工作队列*/
	destroy_workqueue(&key_work);
	printk("中断:驱动卸载成功!\n");
}

module_init(tiny4412_interrupt_dev_init);
module_exit(tiny4412_interrupt_dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tiny4412 wbyq");
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。