☰
FreeRTOS疑问解决1(关于任务调度器)
2026/10/25 1:14:36 网站建设 项目流程

疑问1.1:这是一段在函数static void prvAddNewTaskToReadyList( TCB_t * pxNewTCB )中的代码片段

我的疑问是:既然调度器未运行,将pxNewTCB赋值给pxCurrentTCB也无法执行任务切换啊?
if( xSchedulerRunning == pdFALSE )
{
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}

问题解决:xSchedulerRunning == pdFALSE意味着任务调度器还未启动!!你没看错,如果启动,那么xSchedulerRunning一定为pdTRUE,因此我的疑问就不太对,这个就谈不上是任务切换。即使调用函数vTaskSuspendAll()也无法使xSchedulerRunning == pdFALSE,该函数内部只会执行++uxSchedulerSuspended。因此,pxCurrentTCB = pxNewTCB只是选择任务优先级最高的任务,这个操作不算任务切换,这样可以方便调度器一启动直接运行优先级最高的任务。

这个函数很好的解决了我的疑问!从变量xReturn可看出调度器三种状态!其中第一个就是我说的任务调度器未启动的状态taskSCHEDULER_NOT_STARTED!然后需要注意的一点是,虽然uxSchedulerSuspended都是和pdFALSE作比较,但是它是一个数值变量!

BaseType_t xTaskGetSchedulerState( void )
{
BaseType_t xReturn;

if( xSchedulerRunning == pdFALSE )
{
xReturn = taskSCHEDULER_NOT_STARTED;
}
else
{
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
xReturn = taskSCHEDULER_RUNNING;
}
else
{
xReturn = taskSCHEDULER_SUSPENDED;
}
}

return xReturn;
}

疑问1.2:xSchedulerRunning != pdFALSE在调度器被挂起的时候也是满足该条件的,那么被挂起时,执行任务切换portYIELD_WITHIN_API();会发生什么??

if( xSchedulerRunning != pdFALSE )//调度器已经启动
{
if( pxTCB == pxCurrentTCB )
{
configASSERT( uxSchedulerSuspended == 0 );
portYIELD_WITHIN_API();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
(不完善)疑问解决: 先用xYieldPending 变量记录下来,等待任务调度器恢复

if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE )
{
/* The scheduler is currently suspended - do not allow a context
* switch. 调度程序当前挂起-不允许进行上下文切换*/
xYieldPending = pdTRUE;//所以给等待切换变量置一
}

疑问1.3:任务调度器是如何通过一个变量uxSchedulerSuspended的++或者--来实现PendSV任务切换中断的关闭与开启?

答:是通过函数void SysTick_Handler(void)中的xPortSysTickHandler()中

if( xTaskIncrementTick() != pdFALSE )

{

//这就是执行PendSV中断任务切换的核心代码,即:xTaskIncrementTick()返回值不为pdTRUE,

//将不会执行任务切换

portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;

}

来实现的。在函数xTaskIncrementTick()的内部,定义一个xSwitchRequired变量,默认为pdFALSE,如果变量uxSchedulerSuspended!=pdFALSE,那么只会执行++xPendedTicks,接着xSwitchRequired将会不作改变并且直接被return,因此达到目的。

疑问1.3.1:挂起任务调度器,Systick_Handler()还会每隔1ms默认触发一次吗?

答:会的,挂起任务调度器只是让变量uxSchedulerSuspended++,来间接的不让执行portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT这句代码而已,并没有粗暴的直接关闭优先级为15的中断。

疑问1.3.2:进入临界区(即关闭中断后),Systick_Handler()还会每隔1ms默认触发一次吗?如果不会的话,要是中断时间过长,岂不是会丢失滴答数吗?

答:猜测:这种情况Systick_Handler()就不会每1ms触发一次了,因为5-15优先级的中断都被关闭了。如果此时临界区代码量过大导致执行时间过长,会导致滴答定时器丢失滴答数。解决方式可能就是:使用vTaskSuspendAll()和xTaskResumeAll()来解决:临界区太长而不适合简单地关中断的代码段

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询