天天看点

FreeRTOS 从入门到精通8--中断管理

我们介绍了软件定时器的应用,在这一讲我们将介绍中断在FreeRTOS中的应用和注意事项。

什么是中断

CPU在执行某一事件A时,发生另外一个更重要紧急的事件B请求CPU去处理(产生了中断),于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理,CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A,这一过程统称为中断。

我在网上看到了一篇文章很形象地介绍了中断。

​​单片机中断系统_C语言中文网中断的产生背景 请设想这样一个场景:此刻我正在厨房用煤气烧一壶水,而烧开一壶水刚好需要 10 分钟,我是一个主体,烧水是一个目的,而且我只能时时刻刻在这里烧水,因为一旦水开了,溢出来浇灭煤气的话,有可能引发一场灾难。但就在这个时候呢,我又听到了http://c.biancheng.net/cpp/html/1888.html​​

 请设想这样一个场景:此刻我正在厨房用煤气烧一壶水,而烧开一壶水刚好需要 10 分钟,我是一个主体,烧水是一个目的,而且我只能时时刻刻在这里烧水,因为一旦水开了,溢出来浇灭煤气的话,有可能引发一场灾难。但就在这个时候呢,我又听到了电视里传来《天龙八部》的主题歌,马上就要开演了,我真想夺门而出,去看我最喜欢的电视剧。然而,听到这个水壶发出的“咕嘟”的声音,我清楚:除非等水烧开了,否则我是无法享受我喜欢的电视剧的。

这里边主体只有一个我,而我要做的有两件事情,一个是看电视,一个是烧水,而电视和烧水是两个独立的客体,它们是同时进行的。其中烧水需要 10 分钟,但不需要了解烧水的过程,只需要得到水烧开的这样一个结果就行了,提下水壶和关闭煤气只需要几秒的时间而已。所以我们采取的办法就是:烧水的时候,定上一个闹钟,定时 10 分钟,然后我就可以安心看电视了。当 10 分钟时间到了,闹钟响了,此刻水也烧开了,我就过去把煤气灭掉,然后继续回来看电视就可以了。

这个场景和单片机有什么关系呢?

在单片机的程序处理过程中也有很多类似的场景,当单片机正在专心致志的做一件事情(看电视)的时候,总会有一件或者多件紧迫或者不紧迫的事情发生,需要我们去关注,有一些需要我们停下手头的工作去马上去处理(比如水开了),只有处理完了,才能回头继续完成刚才的工作(看电视)。这种情况下单片机的中断系统就该发挥它的强大作用了,合理巧妙的利用中断,不仅可以使我们获得处理突发状况的能力,而且可以使单片机能够“同时”完成多项任务。

单片机通常拥有丰富的片内外设和很多中断源,中断程序相比于在主程序中使用循环语句轮询系统状态(Polling)能有效提高CPU的利用效率,同时能都够更加及时地响应外部事件。中断系统虽然强大,但中断系统的引进有时会产生一些意想不到的问题。在FreeRTOS中,任务程序由内核调度器统一调度和执行,程序的运行情况在一定程度上是稳定可预测的;中断相对而言是由外部环境决定是不可预测的,会对系统引入不确定性,如果使用不当的话会导致系统的失效和不稳定。所以在很多安全相关的程序中,通常为了稳定减少对中断的使用。总而言之,对中断的使用要根据实际需求和使用场景来决定,通过合理的配置和与FreeRTOS的中断管理(Interrupt Management)配合可以达到系统的高效能和高稳定性。

在FreeRTOS中的中断管理

FreeRTOS对于中断没有特别的处理程序,但提供一些特性和系统API函数方便用户可以简单地实现和维护中断管理。首先我们要明白,在FreeRTOS中中断的优先级和任务的优先级是有区别的。可以把任务类比是人间模式,而中断是上帝模式,拥有更高的权限。

  • 任务的优先级是由用户设置内核管理器管理的软件特性(software feature),与操作系统所在的硬件平台无关
  • 中断的优先级是由硬件平台相关的硬件特性(hardware feature),在中断代码运行的时候任务的代码将无法运行。即使是拥有最小优先级的中断也会打断拥有最高优先级的任务。

FreeRTOS对于一些系统API函数提供两种版本,一种是供任务调用的,一种是供中断调用的(Interrupt Safe API)。由中断调用的API函数后缀上会有“FromISR”。

在任务中系统API函数被调用后,如果有更高优先级的任务处于就绪状态的话,内核调度器会自动执行有更高优先级的任务。(需要在FreeRTOSConfig.h头文件中设置configUSE_PREEMPTION为1)。在中断处理代码中系统API函数被调用后,内核调度器不会自动切换到有更高优先级的任务,会把系统API函数中指针参数pxHigherPriorityTaskWoken设置为pdTRUE,然后由用户根据pxHigherPriorityTaskWoken的值自行决定是否切换任务。在中断处理代码中,切换任务的系统API函数如下

portYIELD_FROM_ISR( xHigherPriorityTaskWoken );      

如果xHigherPriorityTaskWoken的值是pdFALSE的话,任务切换不会发生;如果xHigherPriorityTaskWoken的值是pdTRUE的话,任务切换会发生。

中断处理程序中代码可以尽量简短,并把处理中断的代码放在一个中断任务里。这种方法称为延迟中断处理(deferred interrupt processing)。通过延迟中断处理用户可以把中断处理当成由调度器调度的一个任务并分配优先级,此时中断的优先级和任务的优先级是一致的。用户可以把中断任务的优先级设置为最高级,当中断发生在中断处理程序调用portYIELD_FROM_ISR()函数后中断任务将会优先执行。当然如果程序简单的话用户可以直接在中断处理程序中处理中断。

void InterruptHandling()
//模拟中断处理函数,中断处理函数是硬件平台相关,不属于FreeRTOS范畴
{
   RecordInterrupt();//记录一下中断的信息方便中断任务判断中断来源等
   ClearInterruptFlag();//清除中断标记,使能中断
   portYIELD_FROM_ISR(pdTRUE);//切换任务,如果中断任务的优先级设置为最高级将会优先执行
}
void vInterruptHandling(void const * argument)
//FreeRTOS建立的中断任务,处理中断
{
   ProcessInterrupt();
   //因为中断的来源有很多,需要通过对中断的记录判断中断来源并执行相应中断处理代码
}      

继续阅读