bet·365官方网站-365bet体育在线中文网-365bet娱乐

Linux中断基础概念

Linux中断基础概念

Linux中断基础概念

中断上下文

Linux内核的中断回调可以有两部分,即上下文。当中断比较简单时,可以只有上文。

一般中断上文是指由中断产生的回调函数直接执行的部分;中断下文在上文中启用调度,再由内核调度。

中断上文:处理尽可能少的任务,特点是响应速度快

中断下文:处理耗时任务,可以被新的中断打断

中断嵌套

Linux中断现在不能嵌套,之前可以

中断相关的函数及命令

获取中断号

如果是有设备树的内核,一般通过节点的interrupt-parent和interrupt属性来描述中断

对GPIO来说,GPIO的节点可以作为中断控制器,一般由BSP厂家编写

//从设备树的设备节点中获取中断号

unsigned int irq_of_parse_and_map(struct device_node *dev, int index);

//参数:dev设备节点,index索引(节点中interrupts属性可能包含多条中断信息,通过index确认)

//返回值:中断号

//如果是GPIO的话,可以不从设备树中获取

int gpio_to_irq(unsigned int gpio);

//参数:gpio的编号

//返回值:gpio对应的中断号

申请中断

申请中断的函数

int request_irq(unsigned int irq,

irq_handler_t handler,

unsigned long flags,

const char *name,

void *dev);

//参数:

//irq:要申请中断的中断号

//handler:中断处理函数

//flags:中断标志

//name:中断名字,可在/proc/interrupts文件中看到对应的名字

//dev:flags为IRQF_SHARED时,dev用来区分不同的中断。一般将dev设置为设备结构体,传递给irq_handler_t的第二个参数

//返回值:0申请成功,其他负值申请失败;如果返回-EBUSY标识已经被申请

中断标志(申请中断函数的flags参数)定义在 include/linux/interrupt.h中

常见的中断标志:

标志

功能

IRQF_SHARED

多个设备共享一个中断线,申请中断函数的dev参数是区分它们的唯一标志

IRQF_ONESHOT

单次中断,中断执行一次就结束

IRQF_TRIGGER_NONE

无触发

IRQF_TRIGGER_RISING

上升沿触发

IRQF_TRIGGER_FALLING

下降沿触发

IRQF_TRIGGER_HIGH

高电平触发

IRQF_TRIGGER_LOW

低电平触发

中断处理函数

使用request_irq申请中断的时候需要中断处理函数irq_handler_t来做参数,

这里的irq_handler_t函数可以理解为中断上文的回调函数,发生中断时内核会调用处理函数。

irqretuen_t (*irq_handler_t)(int, void*)

//参数:int型中断号,void*型需要和中断申请函数的dev参数保持一致

//返回值:irqretuen_t类型

enum irqreturn {

IRQ_NONE = (0<<0), //不处理

IRQ_HANDLED = (1<<0), //正常处理

IRQ_WAKE_THREAD = (1<<1), //使用中断下文处理

};

typedef enum irqreturn irqreturn_t;

//irqretuen_t是一个枚举类型,一般中断处理函数会返回第二个数,格式如下

//return ITQ_RETVAL(IRQ_HANDLED)

释放中断

void free_irq(unsigned int irq, void *dev);

//参数:irq要释放的中断号,dev如果设置为IRQ_SHARED的话,此参数来区分具体中断

查看中断是否存在

#查看中断是否存在

cat /proc/interrupts

#查看中断执行次数

cat /proc/irq/xx/spurious

#xx指中断号

中断实验-以GPIO按键中断为例

实验的gpio中断内容很少,所以只使用中断上文

使用讯为的itop-4412开发板,home键,按键按下时进入中断执行打印

设备树修改

home键的gpio是gpx1_1,将之前home键的描述注释掉,添加自己的节点

home_inter {

compatible = "taxue,home_key";

status = "okay";

inter_gpio = <&gpx1 1 GPIO_ACTIVE_LOW>;

};

驱动部分的代码步骤

以平台总线driver的驱动作为框架,在probe函数里执行以下步骤

从设备树获取描述GPIO的节点

通过节点中的gpio参数,获取gpio编号

申请gpio

设置gpio方向

获取gpio对应的中断号

申请中断(中断处理函数)

编写中断处理函数

platform_driver的of_match_table参数要和设备树节点中的compatible参数一致,保证可以匹配

在remove函数里,释放中断、释放GPIO

代码

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

//用于和设备匹配

#define DEVICE_NAME "taxue,home_key"

int home_gpio_idx; //home键的gpio编号

int home_inter_idx; //home键gpio对应的中断号

static irqreturn_t home_interrupt(int irq, void *dev_id) {

printk("%s(%d)\n", __FUNCTION__, __LINE__); //__LINE__,代码所在的行数

printk("HOME KEY HIGH TO LOW!\n");

return IRQ_HANDLED; //返回值,表示正常执行

}

int drProbe(struct platform_device *dev){

int ret;

struct device_node *inter_node;

//根据设备树路径获取节点

inter_node = of_find_node_by_path("/home_inter");

if(inter_node == NULL){

printk("find node failed\n");

return -1;

}

//根据节点的inter_gpio属性,获取gpio编号

home_gpio_idx = of_get_named_gpio( inter_node, "inter_gpio", 0);

//申请gpio

ret = gpio_request(home_gpio_idx,"home");

if( ret != 0){

printk("gpio request failed\n");

return -1;

}

//将gpio设置为输入

gpio_direction_input(home_gpio_idx);

//获取中断号

home_inter_idx = gpio_to_irq(home_gpio_idx);

//申请中断,下降沿触发

ret = request_irq(home_inter_idx, home_interrupt, IRQF_TRIGGER_FALLING, "home_key", dev);

if(ret < 0){

printk("request interrupt failed, IRQ=%d,ret=%d\n", home_inter_idx, ret);

return -1;

}

return 0;

}

int drRemove(struct platform_device *dev){

free_irq(home_inter_idx, dev); //释放中断

gpio_free(home_gpio_idx); //释放gpio

printk("driver remove\n");

return 0;

}

static const struct of_device_id of_interr_match[] = {

{.compatible = DEVICE_NAME},

{},

};

static struct platform_driver pdrv = {

.probe = drProbe,

.remove = drRemove,

.driver = {

.name = DEVICE_NAME,

.owner = THIS_MODULE,

.of_match_table = of_interr_match,

}

};

//模块入口

static int driver_init_interr(void){

int ret=0;

ret = platform_driver_register(&pdrv); //注册平台driver

if(ret < 0){

printk("platform driver regist failed\n");

return -1;

}

return 0;

}

//模块出口

static void driver_exit_interr(void){

platform_driver_unregister(&pdrv); //卸载平台driver

printk("platform driver exit!\n");

}

module_init(driver_init_interr);

module_exit(driver_exit_interr);

MODULE_LICENSE("Dual BSD/GPL");

MODULE_AUTHOR("TAXUE");

相关推荐