嵌入式系统的中断处理的主要过程是啥?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了嵌入式系统的中断处理的主要过程是啥?相关的知识,希望对你有一定的参考价值。

嵌入式系统的中断处理的主要过程是什么?

嵌入式中断处理要根据不同的处理器有着不同的处理方法,不过原理都是一样的。我给你距举例下吧,现在最常用的处理器是ARM,它处理中断的流程如下:

当一个异常出现以后,ARM微处理器会执行以下几步操作: (这些是中断发生时,自动处理的)

1、将下一条指令的地址存入相应连接寄存器LR,以便程序在处理异常返回时能从正确的位置重新开始执行。若异常是从ARM状态进入,LR寄存器中保存的是下一条指令的地址(当前PC+4或PC+8,与异常的类型有关);若异常是从Thumb状态进入,则在LR寄存器中保存当前PC的偏移量,这样,异常处理程序就不需要确定异常是从何种状态进入的。例如:在软件中断异常SWI,指令MOV PC,R14_svc总是返回到下一条指令,不管SWI是在ARM状态执行,还是在Thumb状态执行。

2、将CPSR复制到相应的SPSR中。

3、根据异常类型,强制设置CPSR的运行模式位。

4、强制PC从相关的异常向量地址取下一条指令执行,从而跳转到相应的异常处理程序处。

还可以设置中断禁止位,以禁止中断发生。

如果异常发生时,处理器处于Thumb状态,则当异常向量地址加载入PC时,处理器自动切换到ARM状态

从异常返回
异常处理完毕之后,ARM微处理器会执行以下几步操作从异常返回:

1、将连接寄存器LR的值减去相应的偏移量后送到PC中。

2、将SPSR复制回CPSR中。

3、若在进入异常处理时设置了中断禁止位,要在此清除。

可以认为应用程序总是从复位异常处理程序开始执行的,因此复位异常处理程序不需要返回。

这些都是嵌入式的基础知识,你要多巩固下就好了。

关于如何学习嵌入式,我刚才看到一篇很不错的文章,是一个专科生介绍自己如何自学嵌入式,并找到嵌入式的工作,里面介绍了他的学习方法和学习过程,希望对你有帮助。

专科生学嵌入式到找到工作的前前后后

先做个自我介绍,我07年考上一所很烂专科民办的学校,学的是生物专业,具体的学校名称我就不说出来献丑了。09年我就辍学了,我在那样的学校,一年学费要1万多,但是根本没有人学习,我实在看不到希望,我就退学了。

退学后我也迷茫,大专都没有毕业,我真的不知道我能干什么,我在纠结着我能做什么。所以辍学后我一段时间,我想去找工作,因为我比较沉默寡言,不是很会说话,我不适合去应聘做业务。我想应聘做技术的,可是处处碰壁。

一次偶然的机会,我才听到嵌入式这个行业。那天我去新华书店,在计算机分类那边想找本书学习。后来有个女孩子走过来,问我是不是读计算机的,有没有兴趣学习嵌入式,然后给我介绍了一下嵌入式现在的火热情况,告诉我学嵌入式多么的有前景,给我了一份传单,嵌入式培训的广告。听了她的介绍,我心里痒痒的,确实我很想去学会一门自己的技术,靠自己的双手吃饭。

回家后,我就上网查了下嵌入式,确实是当今比较热门的行业,也是比较好找工作的,工资也是相对比较高。我就下决心想学嵌入式了。于是我去找嵌入式培训的相关信息,说真的,我也很迷茫,我不知道培训是否真的能像他们宣传的那样好,所以我就想了解一段时间再做打算。

后来,我在百度知道看到一篇让我很鼓舞的文章《如何学习嵌入式》,是一个嵌入式高手介绍没有基础的朋友怎么自学入门学嵌入式,文章写的很好,包含了如何学习,该怎么学习。他提到一个方法就是看视频,因为看书实在太枯燥和费解的,很多我们也看不懂。这点我真的很认同,我自己看书往往看不了几页。

我在想,为什么别人都能自学成才,我也可以的!我要相信自己,所以我就想自学,如果实在学不会我再去培训。

主意一定,我就去搜索嵌入式的视频,虽然零星找到一些嵌入式的视频,但是都不系统,我是想找一个能够告诉我该怎么学的视频,一套从入门到精通的视频,一个比较完整的资料,最好能有老师教,不懂可以请教的。

后来我又找到一份很好的视频,是在嵌入式学习网推出的一份视频《从零基础开始学嵌入式》,网址:http://www.BRIDALRINGSET.info/embedvideo.htm 里面的教程还不错,很完整,可以让我从基础的开始学起。视频不便宜啊,但是我也忍了,毕竟买几本书都要几百了,何况他们还有半年的技术咨询和服务,算值了。

下面介绍下我的学习流程,希望对和我一样完全没有基础的朋友有所帮助。

收到他们寄过来的光盘后,我就开始学习了,由于我没有什么基础,我就从最简单的C语言视频教程学起,话说简单,其实我还是很多不懂的,我只好请教他们,他们还是很热心的,都帮我解决了。C语言我差不多学了一个礼拜,接下来我就学了linux的基本命令,我在他们提供linux虚拟机上都有做练习,敲linux的基本命令,写简单的C语言代码,差不多也就三个礼拜。我每天都在不停的写一些简单的代码,这样一月后我基本掌握了C和linux的基本操作。

接下来我就去学习了人家的视频的培训教程,是整套的,和去参加培训没有多大的区别,这一看就是两个月,学习了ARM的基本原理,学习嵌入式系统的概念,也掌握了嵌入式的环境的一些搭建,对linux也有更深层次的理解了,明白了嵌入式应用到底是怎么做的,但是驱动我只是有一点点的了解,这个相对难一点,我想以后再慢慢啃。

这两个月,除了吃饭睡觉,我几乎都在学习。因为我知道几乎没有基础,比别人差劲,我只能坚持努力着,我不能放弃,我必要要靠自己来养活自己,必须学好这门技术,同时我不懂的就问,这里真的很感谢他们的技术客服对我的任何问题都是耐心的解答,每天都我几乎都有好几个问题问他们,然后我就把不懂的问题总结记下来,这样慢慢积累了一段时间,我发现自己真的有点入门了。

最后的一个月,我就去看关于实践部分的内容,了解嵌入式项目具体的开发流程,需要什么样的知识,我就开始准备这方面的知识,也就是学习这方面的视频,同时他们建议我去找了找一些嵌入式面试的题目,为自己以后找工作做准备。我就到网上找了很多嵌入式的题目,把他们理解的记下来,这样差不多准备了20天左右

我觉得自己差不多入门了,会做一些简单的东西了。我就想去找工作看看,于是我就到51job疯狂的投简历,因为我学历的问题,专科没有毕业,说真的,大公司没有人会要我,所以我投的都是民营的小公司,我希望自己的努力有所回报。没有想过几天过后,就有面试了,但是第一次面试我失败了,虽然我自认为笔试很好,因为我之前做了准备,但是他们的要求比较严格,需要有一年的项目经验,所以我没有被选中。

后来陆续面试了几家公司,终于功夫不负有心人。我终于面试上的,是在闵行的一家民营的企业,公司规模比较小,我的职务是嵌入式linux应用开发,做安防产品的应用的。我想我也比较幸运,经理很看重我的努力,就决定录用我,开的工资是3500一个月,虽然我知道在上海3500只能过温饱的生活,但是我想我足够了。我至少不用每天都要靠父母养,我自己也能养活自己的。我想只要我继续努力,我工资一定会翻倍的。

把本文写出来,希望能让和我一样的没有基础的朋友有信心,其实我们没有必要自卑,我们不比别人笨,只要我们肯努力,我们一样会成功。

最后祝愿所有想学嵌入式的朋友更早的入门!
参考技术A 看我的博文
http://hi.baidu.com/ti_tan/blog/item/66f1134f98d4b438afc3abad.html

Linux系统休眠和设备中断处理

一、设备IRQ的suspend和resume

本小节主要解决这样一个问题:在系统休眠过程中,如何suspend设备中断(IRQ)?在从休眠中唤醒的过程中,如何resume设备IRQ?

一般而言,在系统suspend过程的后期,各个设备的IRQ (interrupt request line)会被disable掉。具体的时间点是在各个设备的late suspend阶段之后。代码如下(删除了部分无关代码):

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

error = dpm_suspend_late(PMSG_SUSPEND);-----late suspend阶段

error = platform_suspend_prepare_late(state);

下面的代码中会disable各个设备的irq

error = dpm_suspend_noirq(PMSG_SUSPEND);----进入noirq的阶段

error = platform_suspend_prepare_noirq(state);

……

}

在dpm_suspend_noirq函数中,会针对系统中的每一个device,依次调用device_suspend_noirq来执行该设备noirq情况下的suspend callback函数,当然,在此之前会调用suspend_device_irqs函数来disable所有设备的irq。

之所以这么做,其思路是这样的:在各个设备驱动完成了late suspend之后,按理说这些已经被suspend的设备不应该再触发中断了。如果还有一些设备没有被正确的suspend,那么我们最好的策略是mask该设备的irq,从而阻止中断的递交。此外,在过去的代码中(指interrupt handler),我们对设备共享IRQ的情况处理的不是很好,存在这样的问题:在共享IRQ的设备们完成suspend之后,如果有中断触发,这时候设备驱动的interrupt handler并没有准备好。在有些场景下,interrupt handler会访问已经suspend设备的IO地址空间,从而导致不可预知的issue。这些issue很难debug,因此,我们引入了suspend_device_irqs()以及设备noirq阶段的callback函数。

系统resume过程中,在各个设备的early resume过程之前,各个设备的IRQ会被重新打开,具体代码如下(删除了部分无关代码):

static int suspend_enter(suspend_state_t state, bool *wakeup)

{……

platform_resume_noirq(state);----首先执行noirq阶段的resume

dpm_resume_noirq(PMSG_RESUME);------在这里会恢复irq,然后进入early resume阶段

platform_resume_early(state);

dpm_resume_early(PMSG_RESUME);

……}

在dpm_resume_noirq函数中,会调用各个设备驱动的noirq callback,在此之后,调用resume_device_irqs函数,完成各个设备irq的enable。

 

二、关于IRQF_NO_SUSPEND Flag

当然,有些中断需要在整个系统的suspend-resume过程中(包括在noirq阶段,包括将nonboot CPU推送到offline状态以及系统resume后,将其重新设置为online的阶段)保持能够触发的状态。一个简单的例子就是timer中断,此外IPI以及一些特殊目的设备中断也需要如此。

在中断申请的时候,IRQF_NO_SUSPEND flag可以用来告知IRQ subsystem,这个中断就是上一段文字中描述的那种中断:需要在系统的suspend-resume过程中保持enable状态。有了这个flag,suspend_device_irqs并不会disable该IRQ,从而让该中断在随后的suspend和resume过程中,保持中断开启。当然,这并不能保证该中断可以将系统唤醒。如果想要达到唤醒的目的,请调用enable_irq_wake。

需要注意的是:IRQF_NO_SUSPEND flag影响使用该IRQ的所有外设(一个IRQ可以被多个外设共享,不过ARM中不会这么用)。如果一个IRQ被多个外设共享,并且各个外设都注册了对应的interrupt handler,如果其一在申请中断的时候使用了IRQF_NO_SUSPEND flag,那么在系统suspend的时候(指suspend_device_irqs之后,按理说各个IRQ已经被disable了),所有该IRQ上的各个设备的interrupt handler都可以被正常的被触发执行,即便是有些设备在调用request_irq(或者其他中断注册函数)的时候没有设定IRQF_NO_SUSPEND flag。正因为如此,我们应该尽可能的避免同时使用IRQF_NO_SUSPEND 和IRQF_SHARED这两个flag。

 

三、系统中断唤醒接口:enable_irq_wake() 和 disable_irq_wake()

有些中断可以将系统从睡眠状态中唤醒,我们称之“可以唤醒系统的中断”,当然,“可以唤醒系统的中断”需要配置才能启动唤醒系统这样的功能。这样的中断一般在工作状态的时候就是作为普通I/O interrupt出现,只要在准备使能唤醒系统功能的时候,才会发起一些特别的配置和设定。

这样的配置和设定有可能是和硬件系统(例如SOC)上的信号处理逻辑相关的,我们可以考虑下面的HW block图:

技术分享图片

外设的中断信号被送到“通用的中断信号处理模块”和“特定中断信号接收模块”。正常工作的时候,我们会turn on“通用的中断信号处理模块”的处理逻辑,而turn off“特定中断信号接收模块” 的处理逻辑。但是,在系统进入睡眠状态的时候,有可能“通用的中断信号处理模块”已经off了,这时候,我们需要启动“特定中断信号接收模块”来接收中断信号,从而让系统suspend-resume模块(它往往是suspend状态时候唯一能够工作的HW block了)可以正常的被该中断信号唤醒。一旦唤醒,我们最好是turn off“特定中断信号接收模块”,让外设的中断处理回到正常的工作模式,同时,也避免了系统suspend-resume模块收到不必要的干扰。

IRQ子系统提供了两个接口函数来完成这个功能:enable_irq_wake()函数用来打开该外设中断线通往系统电源管理模块(也就是上面的suspend-resume模块)之路,另外一个接口是disable_irq_wake(),用来关闭该外设中断线通往系统电源管理模块路径上的各种HW block。

调用了enable_irq_wake会影响系统suspend过程中的suspend_device_irqs处理,代码如下:

static bool suspend_device_irq(struct irq_desc *desc)

{

……

if (irqd_is_wakeup_set(&desc->irq_data)) {

irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);

return true;

}

省略Disable 中断的代码

}

也就是说,一旦调用enable_irq_wake设定了该设备的中断作为系统suspend的唤醒源,那么在该外设的中断不会被disable,只是被标记一个IRQD_WAKEUP_ARMED的标记。对于那些不是wakeup source的中断,在suspend_device_irq 函数中会标记IRQS_SUSPENDED并disable该设备的irq。在系统唤醒过程中(resume_device_irqs),被diable的中断会重新enable。

当然,如果在suspend的过程中发生了某些事件(例如wakeup source产生了有效信号),从而导致本次suspend abort,那么这个abort事件也会通知到PM core模块。事件并不需要被立刻通知到PM core模块,一般而言,suspend thread会在某些点上去检查pending的wakeup event。

在系统suspend的过程中,每一个来自wakeup source的中断都会终止suspend过程或者将系统唤醒(如果系统已经进入suspend状态)。但是,在执行了suspend_device_irqs之后,普通的中断被屏蔽了,这时候,即便HW触发了中断信号也无法执行其interrupt handler。作为wakeup source的IRQ会怎样呢?虽然它的中断没有被mask掉,但是其interrupt handler也不会执行(这时候的HW Signal只是用来唤醒系统)。唯一有机会执行的interrupt handler是那些标记IRQF_NO_SUSPEND flag的IRQ,因为它们的中断始终是enable的。当然,这些中断不应该调用enable_irq_wake进行唤醒源的设定。

 

四、Interrupts and Suspend-to-Idle

Suspend-to-idle (也被称为"freeze" 状态)是一个相对比较新的系统电源管理状态,相关代码如下:

static int suspend_enter(suspend_state_t state, bool *wakeup)

{

……

各个设备的late suspend阶段

各个设备的noirq suspend阶段

if (state == PM_SUSPEND_FREEZE) {

freeze_enter();

goto Platform_wake;

}

……

}

Freeze和suspend的前面的操作基本是一样的:首先冻结系统中的进程,然后是suspend系统中的形形色色的device,不一样的地方在noirq suspend完成之后,freeze不会disable那些non-BSP的处理器和syscore suspend阶段,而是调用freeze_enter函数,把所有的处理器推送到idle状态。这时候,任何的enable的中断都可以将系统唤醒。而这也就意味着那些标记IRQF_NO_SUSPEND(其IRQ没有在suspend_device_irqs过程中被mask掉)是有能力将处理器从idle状态中唤醒(不过,需要注意的是:这种信号并不会触发一个系统唤醒信号),而普通中断由于其IRQ被disable了,因此无法唤醒idle状态中的处理器。

那些能够唤醒系统的wakeup interrupt呢?由于其中断没有被mask掉,因此也可以将系统从suspend-to-idle状态中唤醒。整个过程和将系统从suspend状态中唤醒一样,唯一不同的是:将系统从freeze状态唤醒走的中断处理路径,而将系统从suspend状态唤醒走的唤醒处理路径,需要电源管理HW BLOCK中特别的中断处理逻辑的参与。

 

五、IRQF_NO_SUSPEND 标志和enable_irq_wake函数不能同时使用

针对一个设备,在申请中断的时候使用IRQF_NO_SUSPEND flag,又同时调用enable_irq_wake设定唤醒源是不合理的,主要原因如下:

1、如果IRQ没有共享,使用IRQF_NO_SUSPEND flag说明你想要在整个系统的suspend-resume过程中(包括suspend_device_irqs之后的阶段)保持中断打开以便正常的调用其interrupt handler。而调用enable_irq_wake函数则说明你想要将该设备的irq信号设定为中断源,因此并不期望调用其interrupt handler。而这两个需求明显是互斥的。

2、IRQF_NO_SUSPEND 标志和enable_irq_wake函数都不是针对一个interrupt handler的,而是针对该IRQ上的所有注册的handler的。在一个IRQ上共享唤醒源以及no suspend中断源是比较荒谬的。

不过,在非常特殊的场合下,一个IRQ可以被设定为wakeup source,同时也设定IRQF_NO_SUSPEND 标志。为了代码逻辑正确,该设备的驱动代码需要满足一些特别的需求。

 

参考文献

1、内核文档power/suspend-and-interrupts.txt

以上是关于嵌入式系统的中断处理的主要过程是啥?的主要内容,如果未能解决你的问题,请参考以下文章

嵌入式--中断系统学习(中断系统+NVIC+SYSCFG+优先级配置+中断嵌套+中断架构)

嵌入式系统是啥?有啥用?

嵌入式中pattern是啥

ESP-Arduino玩转外设中断基本概念及Timer中断

2019-举例跟踪分析Linux内核5.0系统调用处理过程

( )1.嵌入式系统调试通常可以分为硬件级调试和在线仿真器调试。 ( )2.在ARM处理器中,快速中断(FIQ)的