您的位置 首页 > 数码极客

软件中断如何触发 stm32软件触发中断 外部中断如何触发

设备与处理器之间的工作通常来说是异步,设备数据要传递给处理器通常来说有以下几种方法:轮询、等待和中断。

  • 轮询让CPU以固定的频率读取设备,看看数据是否准备好,准备好就读取;
  • 如果确定知道设备数据会在很短的未来准备好,也可以让CPU等待一段时间,之后读取数据;
  • 设备准备好数据后,拉中断信号,CPU在中断线程中进行读取操作;

让CPU进行轮询等待总是不能让人满意,所以通常都采用中断的形式,让设备来通知CPU读取数据。

中断处理相关的函数

2.6内核的函数参数与现在的参数有所区别,这里都主要介绍概念,具体实现方法需要结合具体的内核版本。

int request_irq(unsigned int irq, irqreturn_t (*handler) (int, void *, struct pt_regs *), unsigned long flags, const char *dev_name, void *dev_id); void free_irq(unsigned int IRQ, void *dev_id);

request_irq函数申请中断,返回0表示申请成功,其他返回值表示申请失败,其具体参数解释如下:

  • irq:申请的中断号,在第2小节介绍
  • handler:中断处理函数,第3小节介绍
  • flags:中断管理掩码,见后面
  • dev_name:中断名称,可以显示在/proc/interrupts中
  • dev_id:共享中断下必须指定,独占模式可以设为NULL,好的思路是用来指向设备的数据结构

flags 掩码可以使用以下几个:

掩码宏

解释

SA_INTERRUPT

当该位被设置时,表示是一个快速中断处理例程

SA_SHIRQ

表示中断在设备之间共享

SA_SAMPLE_RANDOM

产生的中断对/dev/random和/dev/urandom设备熵池做贡献,可以理解为增强随机性

快速和慢速处理例程:现代内核中基本没有这两个概念了,使用SA_INTERRUPT位后,当中断被执行时,当前处理器的其他中断都将被禁止。通常不要使用SA_INTERRUPT标志位,除非自己明确知道会发生什么。

共享中断:使用共享中断时,一方面要使用SA_SHIRQ位,另一个是request_irq中的dev_id必须是唯一的,不能为NULL。这个限制的原因是:内核为每个中断维护了一个共享处理例程的列表,例程中的dev_id各不相同,就像设备签名。如果dev_id相同,在卸载的时候引起混淆(卸载了另一个中断),当中断到达时会产生内核OOP消息。

共享中断需要满足以下一个条件才能申请成功:

  • 中断信号线空闲
  • 已经注册该中断信号线的处理例程都标识为共享中断

当不需要使用该中断时,需要使用free_irq释放中断。

通常我们会在模块加载的时候申请安装中断处理例程,但书中建议:在设备第一次打开的时候安装,在设备最后一次关闭的时候卸载。

如果要查看中断触发的次数,可以查看 /proc/interrupts 和 /proc/stat。

irq中断号获取

书中讲述了如何自动检测中断号,在嵌入式开发中通常都是查看原理图和datasheet来直接确定。

自动检测的原理如下:驱动程序通知设备产生中断,然后查看哪些中断信号线被触发了。Linux提供了以下方法来进行探测:

unsigned long probe_irq_on(void); int probe_irq_off(unsigned long);
  • prob_irq_on返回一个未分配中断的位掩码,该掩码需要传递给prob_irq_off。调用完probe_irq_on后就可以通知驱动设备产生中断(至少一次)。
  • probe_irq_off 会返回probe_irq_on之后发生了中断的中断编号(即我们所需要的irq中断号)。如果没有检测到发生了中断,则返回0(因此IRQ 0不能用),如果有多个中断编号发生了变化,则返回负值。

探测工作耗时较长,建议在模块加载的时候做。

中断处理函数

中断处理函数和普通函数其实差不多,唯一的区别是其运行的中断上下文中,在这个上下文中有以下注意事项:

  • 不能向用户空间发送或接收数据
  • 不能做任何会产生休眠的操作
  • 不能调用schedule函数

中断处理函数典型用法如下:

static irqreturn_t sample_interrut(int irq, void *dev_id, struct pt_regs *regs) { .... return IRQ_HANDLED; }

中断处理函数的参数和返回值含义如下:

  • irq:前面说到的中断号,可以用来区分当前中断线程
  • dev_id:申请是传递的参数dev_id
  • regs:保存了处理器进入中断代码前的处理器上下文快照,主要用于监控和调试,对驱动通常来说没什么作用。

返回值主要有两个:IRQ_NONE和IRQ_HANDLED。

  • 如果当前中断确实是自己需要处理的中断,则返回IRQ_HANDLED
  • 如果不是自己的中断,则返回IRQ_NONE

4. 中断的启用与禁用

对于中断我们是可以进行开启和关闭的,Linux中提供了以下函数操作单个中断的开关:

#include <asm; void disable_irq(int irq); void disable_irq_nosync(int irq); void enable_irq(int irq);

该方法可以在所有处理器上禁止或启用中断。

需要注意的是:

  • 上面的函数调用是可以嵌套的:就是调用了几次disable_irq则需要调用对应次数的enable_irq才能开启
  • disable_irq禁止中断时,如果当前中断正在执行,则会等待其执行完成,所以如果禁止的线程拥有中断线程所需的资源,则会造成思索。

如果要关闭当前处理器上所有的中断,则可以调用以下方法:

#include <asm; void local_irq_save(unsigned long flags); void local_irq_disable(void); void local_irq_restore(unsigned long flags); void local_irq_enable(void);

local_irq_save 会将中断状态保持到flags中,然后禁用处理器上的中断;如果明确知道中断没有在其他地方被禁用,则可以使用local_irq_disable,否则请使用local_irq_save。

locat_irq_restore 会根据上面获取到flags来恢复中断;local_irq_enable 会无条件打开所有中断。

5. 顶半部和底半部概念

在中断中需要做一些工作,如果工作内容太多,必然导致中断处理所需的时间过长;而中断处理又要求能够尽快完成,这样才不会影响正常的系统调度,这两个之间就产生了矛盾。

现在很多操作系统将中断分为两个部分来处理上面的矛盾:顶半部和底半部。

顶半部就是我们用request_irq来注册的中断处理函数,这个函数要求能够尽快结束,同时在其中调度底半部,让底半部在之后来进行后续的耗时工作。

顶半部就不再说明了,就是上面的中断处理函数,只是要求能够尽快处理完成并返回,不要处理耗时工作。

底半部通常使用tasklet或者工作队列来实现。

tasklet的特点和注意事项:

  • 运行在软件中断上下文中,需要注意中断上下文的事项;
  • 与调用它的函数运行在同一个CPU上;

工作队列的特点和注意事项:

  • 运行在进程上下文中,可以进行休眠等操作;
  • 工作者进程无法访问其他任何进程的地址空间;

责任编辑: 鲁达

1.内容基于多重复合算法人工智能语言模型创作,旨在以深度学习研究为目的传播信息知识,内容观点与本网站无关,反馈举报请
2.仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证;
3.本站属于非营利性站点无毒无广告,请读者放心使用!

“软件中断如何触发,stm32软件触发中断,AURIX软件触发中断,外部中断如何触发,如何做中断停止软件”边界阅读