0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux中断编程

嵌入式技术 来源:嵌入式技术 作者:嵌入式技术 2022-09-23 09:01 次阅读

Linux中断编程

中断:是指CPU在运行过程中,出现了某种异常事件,需要CPU先暂停当前工作,转而去处理新产生的异常事件,处理完后再返回暂停的事件继续往下执行。就例如我们正在使用手机进行微信视频聊天,这时突然有人打电话过来,这时手机的处理方式是手机来来电铃声响起,通知用户电话来了。

中断,就是来处理未来时间内可能会发生的事件, 中断事件也称为异常事件。有了中断处理,则可大大提高CPU处理效率。

单片机中,我们也常用中断方式来处理一些紧急事件,帮我们实现快速响应一些实时性的事件。因此我们在编写中断服务函数时都是代码尽可能简洁、一定不能处理死循环、若需要处理的事情比较多则应在中断中设定标志位,然后将逻辑代码放到主函数中去实现。

在Linux内核中,我们一般会将中断分为顶半部分和底半部分。顶半部分主要是处理耗时短的代码(像单片机中设置标志位),启动底半部分代码;底半部分主要是处理耗时比较长的代码,完成中断响应后的事件处理。

1. Linux下外部中断

  要使用外部中断,则需要完成中断三要素的配置:中断号(irq)、中断服务函数、中断触发方式(电平触发、边沿触发)。

1.1 相关接口函数

  • 获取中断号gpio_to_irq

  在Linux内核中提供了方便函数获取引脚中断号

int gpio_to_irq(unsigned gpio)
函数功能: 获取中断号
返回值: 成功返回对应GPIO的中断号irq
  • 注册中断request_irq
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)
函数功能: 注册中断
形 参: irq --中断号,gpio_to_irq函数返回值。
    handler --中断服务函数。
     服务函数原型:typedef irqreturn_t (*irq_handler_t)(int, void *);
    flags --中断触发方式。
     #define IRQF_TRIGGER_RISING 0x00000001 //上升沿
     #define IRQF_TRIGGER_FALLING 0x00000002 //下升沿
     #define IRQF_TRIGGER_HIGH 0x00000004//高电平
    #define IRQF_TRIGGER_LOW 0x00000008//低电平

    #define IRQF_SHARED 0x00000080 //共享中断
   name --中断注册标志。
   dev --传给中断服务函数的参数。
返回值: 成功返回0,失败返回其它值。
  • 中断服务函数
typedef irqreturn_t (*irq_handler_t)(int, void *);
函数功能: 中断服务函数
形 参: 第一个参数为中断号;第二个参数为注册函数传入的参数dev
返回值:
   enum irqreturn {
    IRQ_NONE = (0 << 0), //如果不是本中断的则返回这个值,只在共享中断中使用
    IRQ_HANDLED = (1 << 0), //正确执行中断程序返回这个值,常用
    IRQ_WAKE_THREAD = (1 << 1), //表示去唤醒中断处理者的线程
  };

  注意: irqreturn_t (*irq_handler_t)(int, void *);函数中不能出现带休眠的函数,如msleep函数;该函数必须要返回值。

  • 注销free_irq
free_irq(unsigned int irq, void *dev_id)
函数功能: 注销中断
形 参: irq --中断号,gpio_to_irq函数返回值。
   dev --传给中断服务函数的参数。需和注册时保持一致

2. 工作队列

  中断处理函数分为中断顶半部分和中断底半部分。顶半部分代码实现即为中断服务函数,而底半部分代码则可由工作队列完成。

2.1 工作队列简介

操作系统中,如果我们需要进行一项工作处理,往往需要创建一个任务来加入内核的调度队列。一个任务对应一个处理函数,如果要进行不同的事务处理,则需要创建多个不同的任务。任务作为CPU调度的基本单元,任务数量越大,则调度成本越高。工作队列(workqueue)机制简化了基础的任务创建和处理机制,一个workqueue对应一个实体task任务处理,工作队列中可以挂载多个工作实体,每一个工作都能对应不同的工作处理函数。即用户只需要创建一个workqueue,则可以完成多个挂接不同处理函数的工作队列。

工作队列还具有将工作推后执行机制,工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。

workqueue的处理依赖于task任务。一个workqueue队列会创建关联其对应的task任务,一个workqueue会挂载多个工作进行处理,每个工作都有工作处理函数。当workqueue得到调度,即其关联的task得到运行,在每次task的调度期间,都会从工作队列中按照先后顺序取出一个work来进行处理。workqueue模块在初始化时,会创建一个系统默认的工作队列,用户可根据需要将work添加到该队列中去执行。

2.2 工作相关函数接口

  • 工作结构体struct work_struct
#include 
struct work_struct {
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;  /*工作处理函数*/
#ifdef CONFIG_LOCKDEP
	struct lockdep_map lockdep_map;
#endif
};

  在工作结构体体中,我们需要关心的成员是工作处理函数:work_func_t func,简单来说即一个工作会对应有一个处理函数。工作处理函数原型如下:

#include 
typedef void (*work_func_t)(struct work_struct *work);
  • 初始化工作INIT_WORK
#define INIT_WORK(_work, _func)
函数功能: 初始化工作,以宏的方式实现
形 参: _work --工作结构体体指针
   _func --工作处理函数
  • 工作调度schedule_work
int schedule_work(struct work_struct *work)

2.3工作队列使用步骤

  1. 定义工作结构体struct work_struct,初始化工作INIT_WORK;
  2. 编写工作处理函数void (*work_func_t)(struct work_struct *work);
  3. 在合适的地方调调度工作(一般在中断顶半部分);

2.4工作队列使用示例

  下面以按键为例,实现中断方式按键检测,通过工作队列处理底半部分代码,杂项设备框架实现设备注册。

poYBAGMsVOqAM5YjAAC9181SmfI038.png#pic_centerpYYBAGMsVOuAZpP4AAGtQ8N96qc548.png#pic_center
  • K1 – GPX3_2
  • K2 --GPX3_3
  • K3 --GPX3_4
  • K4 --GPX3_5
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 
#include 
struct work_struct key_work;/*工作结构体*/
struct _KEY
{
	unsigned int gpio;/*按键引脚*/
	char name[20];/*按键名*/
	int irq;/*中断号*/
	int key_num;/*按键编号*/
};
static struct _KEY KEY_GPIO_PIN[]=
{
	{EXYNOS4_GPX3(2),"key1",0,1},
	{EXYNOS4_GPX3(3),"key2",0,2},
	{EXYNOS4_GPX3(4),"key3",0,3},	
	{EXYNOS4_GPX3(5),"key4",0,4},	
};
static struct _KEY *key_p;
static unsigned int key_val;
/*工作服务函数*/
void key_work_func(struct work_struct *work)
{
	msleep(30);/*按键消抖*/
	if(gpio_get_value(key_p->gpio)==0)
	{
		printk(" key%d 按下n",key_p->key_num);
	}
	else 
	{
		printk(" key%d 松开n",key_p->key_num);
	}
	key_val=key_p->key_num;
}
/*中断服务函数*/
static irqreturn_t key_irq_handler(int irq, void *dev)
{
	key_p=(struct _KEY *)dev;
	schedule_work(&key_work);/*调度工作*/
	return IRQ_HANDLED;/*中断正常处理*/
}
static int key_open(struct inode *inode, struct file *file)
{
	printk("设备打开成功n");
	return 0;
}
static ssize_t key_read(struct file *file, char __user *buf, size_t cnt, loff_t *seek)
{
	int res=copy_to_user(buf,&key_val, 4);
	key_val=0;
	return 4-res;
}
static int key_release(struct inode *inode, struct file *file)
{
	printk("设备关闭成功n");
	return 0;
}
/*文件操作集合*/
static struct file_operations key_fops=
{
	.owner= THIS_MODULE, /*当前模块文件操作集合所有者*/
	.open=key_open,/*open函数接口*/
	.read=key_read,/*read函数接口*/
	.release=key_release,/*close函数接口*/
};
/*
字符设备注册:主设备+次设备号
主设备  --用来区分类(杂项设备、输入设备)
次设备号  --对应哪个设备
杂项设备的主设备号固定为:10
*/
static struct miscdevice key_miscdev = {
	.minor	= MISC_DYNAMIC_MINOR,/*次设备号255由内核分配*/
	.name	= "tiny4412_key",/*设备节点名字,会在/dev下生成*/
	.fops	= &key_fops,/**/
};

static int __init tiny4412_key_module_init(void)
{
	int i=0;
	int res;
    printk("hello,驱动注册成功n");
	/*初始化工作*/
	INIT_WORK(&key_work,key_work_func);
	/*注册中断*/
	for(i=0;i(key_gpio_pin)>
poYBAGMsVOuAfzGRAAEtRbFUGc8924.png#pic_center

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    87

    文章

    10988

    浏览量

    206724
  • 编程
    +关注

    关注

    88

    文章

    3439

    浏览量

    92375
  • 中断
    +关注

    关注

    5

    文章

    884

    浏览量

    41021
收藏 人收藏

    评论

    相关推荐

    Linux内核中断设计与实现

    裸机编程中使用中断比较麻烦,需要配置寄存器、使能IRQ等等。而在Linux驱动编程中,内核提供了完善的终端框架,只需要申请中断,然后注册
    发表于 07-29 08:57 634次阅读

    芯灵思Sinlinx A33开发板 Linux中断编程原理说明

    。step2 内核关于 CPU 的中断linux 中断注册函数中的 irq 中断号并不是芯片物理上的编号,而是由芯片商在移植 Linux
    发表于 01-31 17:24

    芯灵思Sinlinx A33开发板 Linux中断编程 2:程序框架

    根据上一个帖子的分析,想要实现按键中断,首先得知道引脚对应的中断号,LRADC0对应的中断编号#define SUNXI_IRQ_LRADC (SUNXI_GIC_START + 30)/* 62
    发表于 02-01 16:28

    芯灵思Sinlinx A33开发板Linux中断编程 3-应用程序

    应用程序代码参考#include #include #include #include #include #include #include #include #include #include#defineDEV_NAME "/dev/mybtn"int main(int argc, char *args[]){ int fd = 0; int ret = 0; unsigned char recv_buf[1] = {"0"}; fd = open(DEV_NAME, O_RDONLY); //fd = open(DEV_NAME, O_RDONLY|O_NONBLOCK); if(fd < 0) {perror("open"); } while(1) {strcpy(recv_buf, "0000");//读取按键数据ret = read(fd, recv_buf, 1);if((ret < 0) && (errno != EAGAIN)) {perror("read");exit(-1);}//输出按键状态printf("%s\r\n", recv_buf); } return 0;}
    发表于 02-11 16:45

    芯灵思SinlinxA33开发板Linux中断编程4-最终代码(1)

    = IRQ_TYPE_EDGE_BOTH; irq = gpio_to_irq( GPIOL(14) );//发生中断号为irq的中断会执行key_isr函数。注册成功会在/proc/irq号/KEY文件夹出现或 cat
    发表于 02-13 16:09

    面向嵌入式Linux系统的软中断设计与实现

    本文在分析标准Linux 内核的软中断机制的演化以及实现原理的基础上,提出并实现了一个面前嵌入式Linux 系统的软中断技术。该技术为嵌入式系统开发提供一个统一的
    发表于 08-03 11:20 16次下载

    Linux 2.6 中断处理原理简介

    Linux 2.6 中断处理原理简介 中断描述符表(Interrupt Descriptor Table,IDT)是一个系统表,它与每一个中断或异常向量相联系,每一个向量在表中存放的
    发表于 02-05 10:52 769次阅读

    嵌入式Linux设备驱动开发之中断编程详解

    11.5 中断编程 前面所讲述的驱动程序中都没有涉及中断处理,而实际上,有很多Linux的驱动都是通过中断的方式来进行内核和硬件的交互。
    发表于 10-18 17:33 0次下载

    linux 下C编程

    linux 下C编程
    发表于 10-25 08:54 9次下载
    <b class='flag-5'>linux</b> 下C<b class='flag-5'>编程</b>

    嵌入式Linux中断驱动

    用过STM32的大概都知道,基本每个GPIO管脚都支持中断模式,这样在检测外部插入一个硬件设备时,通过GPIO管脚电平中断就非常方便。那么AM3354的片子是否支持GPIO管脚电平中断呢?答案是肯定
    发表于 11-01 16:57 4次下载
    嵌入式<b class='flag-5'>Linux</b><b class='flag-5'>中断</b>驱动

    STM32中断编程步骤

    介绍stm32f10x中断的概念,已经中断向量表,还有中断编程的一下要点,涉及的一下寄存器
    发表于 11-23 17:51 30次下载
    STM32<b class='flag-5'>中断</b><b class='flag-5'>编程</b>步骤

    STM32中断与DMA通信编程

    ,256级可编程中断设置。STM32使用了其中一部分,16个内核中断,107系列有68个可屏蔽中断(103系列只有60个),16级可编程
    发表于 12-06 20:36 0次下载
    STM32<b class='flag-5'>中断</b>与DMA通信<b class='flag-5'>编程</b>

    STM32中断与DMA通信编程

    ,256级可编程中断设置。STM32使用了其中一部分,16个内核中断,107系列有68个可屏蔽中断(103系列只有60个),16级可编程
    发表于 12-07 10:51 7次下载
    STM32<b class='flag-5'>中断</b>与DMA通信<b class='flag-5'>编程</b>

    STM32中断与DMA通信编程

    ,256级可编程中断设置。STM32使用了其中一部分,16个内核中断,107系列有68个可屏蔽中断(103系列只有60个),16级可编程
    发表于 01-14 15:37 1次下载
    STM32<b class='flag-5'>中断</b>与DMA通信<b class='flag-5'>编程</b>

    Linux中断情景分析

    在一个系统中,中断时常发生,而且线程调度也是由一个硬件定时器时时刻刻发出中断来支撑的。可以说中断就是linux系统的灵魂。
    发表于 06-23 14:22 360次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>中断</b>情景分析