FreeRTOS-内核控制函数
- FreeRTOS中有一些内核函数,一般来说这些内核函数在应用层不会使用,但是内核控制函数是理解FreeRTOS中断的基础。接下来我们逐一分析这些内核函数。
taskYIELD()
- 该函数的作用是进行任务切换,这是一个宏定义,实际上调用了portYIELD()。portYIELD函数定义如下:
- 该函数中实际进行任务切换的是portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT。我们深入来看一下portNVIC_INT_CTRL_REG 的定义:
-
该寄存器被称为中断可屏蔽器,作用是用于屏蔽优先级低于某个数值的中断,从而保证对时间要求严格的任务能够按照预期来完成任务。并且上表中的所有寄存器是CM3的特殊功能寄存器,只能由指令MSRMRS访问,所以代码中的msr指令可以理解为专门为这些特殊功能寄存器设置的赋值命令。那么如何正确的赋值才能达到我们预期的屏蔽效果呢是一个比较复杂的问题,下面一步一步来分析。
-
要解决这个问题,就必须要从中断优先级分组说起,CM3中划分为两种优先级,一种是抢占优先级,另一种是子优先级(或响应优先级)。CM3支持高达256级可编程优先级,相应地由一个8位的寄存器用于表达这256级的优先级。但是CM最多支持128级抢占优先级,而不是256级,这是因为内核在出厂时厂家就规定了表达抢占优先级的位只能在[7:1]这个区间内,因此抢占优先级最高为128级,相应优先级最高为256级,这是CM3中的优先级定义。下图是优先级分组表。
- 从上面可以看出我们只需要设置PRI_14和PRI_15这两个寄存器就可以设置其中断优先级。我们再看看代码中是不是这样设置的,找到这两个中断优先级的引用的地方,发现在启动任务调度器中有这两个语句:
portNVIC_SYSPRI2_REG定义如下:
- 发现它的对应地址是0xE000ED20,而PendSV对应的是0xE000ED22,SysTick对应的是0xE000ED23,似乎不对呀是不要忘了,portNVIC_PENDSV_PRI对应的宏定义是有2字节的移位的,这样经过移位之后刚好对应0xE000ED22,而且其中断优先级为15,即最低的。SysTick的优先级也是这么设置的。
- 到这里进入中断临界区的深入分析已经结束了,看似简单的调用和赋值其实蕴含了很多CM3核心的知识。
- 再总结一下过程:当要进入临界区的时候,会将系统所能管理的中断全部屏蔽掉,相应的触发任务切换的中断也会屏蔽掉,从而不会受到高优先级任务抢占和中断的影响,确保了对时间要求严格的任务如期进行。但是同样的,有一些更高优先级(不由FreeRTOS管理的)的中断触发的话是会影响到任务运行的。
taskEXIT_CRITICAL()
- 既然有进入临界区,那么必然也会有退出临界区,退出临界区和进入临界区背后的原理是相同的,下面是退出临界区的函数定义(实际上调用了vPortExitCritical()函数):
- 退出临界区的代码很简单,首先uxCriticalNesting自减1,uxCriticalNesting的作用就是记录进入临界区的嵌套次数,每进入一次就自加1,退出1次就自减1,知道uxCriticalNesting=0是恢复所有中断。下面来看一下portENABLE_INTERRUPTS()函数。
- 由此可见,只是将0赋值给basepri中断屏蔽寄存器,在CM3权威指南中提到,如果往basepri中写0,那么所有被屏蔽的中断都将被接触,从而完全退出了临界区,原理同进入一样,这里就不仔细分析了。
taskENTER_CRITICAL_FROM_ISR()
- 中断级进入临界区和任务级进入临界区非常相似,从下面的函数定义中就可以发现:
- 中断级的进入临界区只是将中断屏蔽寄存器的值取出来,作为函数返回值返回了,其他并没有区别。所以这里不再赘述了。
taskEXIT_CRITICAL_FROM_ISR()
- 原理与任务级相同,不再赘述了。
taskDISABLE_INTERRUPTS()
- 同样是调用vPortRaiseBASEPRI()函数,将FreeRTOS所能管理的中断全部屏蔽。
taskENABLE_INTERRUPTS()
- 同样是调用vPortSetBASEPRI( 0 )函数,将FreeRTOS所能管理的中断全部开启。
vTaskSupendAll()
- 该函数用于挂起任务调度器,当任务调度器挂起之后,任务调度器相当于暂停工作了,不会产生任务调度。任务调度器挂起操作很简单,函数定义如下:
- 代码仅有1行,就是将uxSchedulerSuspended这个全局变量自加1,任务挂起可以嵌套,当uxSchedulerSuspended=0时,才表示任务调度器解挂,这一点在讲述任务调度器解挂操作会分析。那么uxSchedulerSuspended变量自加1后会对任务切换等产生什么影响呢面是截取的相关函数:
BaseType_t xTaskIncrementTick( void ){ ··· traceTASK_INCREMENT_TICK( xTickCount ); if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) { /* Minor optimisation. The tick count cannot change in this block. */ const TickType_t xConstTickCount = xTickCount + 1; xTickCount = xConstTickCount; if( xConstTickCount == ( TickType_t ) 0U )
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!