函数预览
函数 | 描述 |
---|---|
taskYIELD() | 任务切换 |
taskENTER_CRITICAL() | 进入临界区,用于任务中。 |
taskEXIT_CRITICAL() | 退出临界区,用于任务中。 |
taskENTER_CRITICAL_FROM_ISR() | 进入临界区,用于中断服务函数中。 |
taskEXIT_CRITICAL_FROM_ISR() | 退出临界区,用于中断服务函数中。 |
taskDISABLE_INTERRUPTS() | 关闭中断。 |
taskENABLE_INTERRUPTS() | 打开中断。 |
vTaskStartScheduler() | 开启任务调度器。 |
vTaskEndScheduler() | 关闭任务调度器。 |
vTaskSuspendAll() | 挂起任务调度器。 |
xTaskResumeAll() | 恢复任务调度器。 |
vTaskStepTick() | 设置系统节拍值。 |
函数详解
这里只介绍部分函数,剩下的一些都是很好理解的,就不过多介绍。
关闭任务调度器
此函数在文件 tasks.c 中有如下定义:
void vTaskEndScheduler( void )
{
portDISABLE_INTERRUPTS(); //关闭中断
xSchedulerRunning = pdFALSE; //标记任务调度器停止运行
vPortEndScheduler(); //调用硬件层关闭中断的处理函数
}
函数 vPortEndScheduler()在 port.c 中有定义,这个函数在移植 FreeRTOS 的时候要根据实际使用的处理器来编写,此处没有实现这个函数,只是简单的加了一行断言,函数如下:
void vPortEndScheduler( void )
{
configASSERT( uxCriticalNesting == 1000UL );
}
挂起任务调度器
挂起任务调度器,调用此函数不需要关闭可屏蔽中断即可挂起任务调度器,此函数在文件tasks.c 中有如下定义:
void vTaskSuspendAll( void )
{
++uxSchedulerSuspended;
}
此函数只是简单的将变量 uxSchedulerSuspended 加一,uxSchedulerSuspended 是挂起嵌套计数器,调度器挂起是支持嵌套的。使用函数 xTaskResumeAll()可以恢复任务调度器,调用了几次 vTaskSuspendAll()挂起调度器,同样的也得调用几次 xTaskResumeAll()才会最终恢复任务调度器。
假设现在有这样一种情况,任务 1 的优先级为 10,此时任务 1 由于等待队列(关于队列的知识后面会有专门的章节讲)TestQueue 而处于阻塞态。但是有段其他的代码调用函数vTaskSuspendAll()挂起了任务调度器,在还没有调用 xTaskResumeAll()恢复任务调度器之前,有个在外部中断发生了,在中断服务程序里面调用函数 xQueueSendFromISR()向任务 1 发送了队列 TestQueue。如果任务调度器没有阻塞的话函数 xQueueSendFromISR()会使任务 1 进入就绪态,也就是将任务 1 添加到优先级 10 对应的就绪列表 pxReadyTasksLists[10]中,这样当任务切换的时候任务 1 就会运行。但是现在任务调度器由于函数 vTaskSuspendAll()而挂起,这个时候任务 1 就不是添加到任务就绪列表 pxReadyTasksLists[10]中了,而是添加到另一个叫做xPendingReadyList 的列表中,xPendingReadyList 是个全局变量,在文件 tasks.c 中有定义。当调用函数 xTaskResumeAll()恢复调度器的时候就会将挂到列表 xPendingReadyList 中的任务重新移动到它们所对应的就绪列表 pxReadyTasksLists 中。
恢复任务调度器
BaseType_t xTaskResumeAll( void )
{
TCB_t *pxTCB = NULL;
BaseType_t xAlreadyYielded = pdFALSE;
configASSERT( uxSchedulerSuspended );
//进入临界区
taskENTER_CRITICAL();
{
//调度器挂起嵌套计数器 uxSchedulerSuspended 减一
--uxSchedulerSuspended;
//如果 uxSchedulerSuspended 为 0 说明所有的挂起都已经解除,调度器可以开始运行了。
if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE )
{
if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U )
{
/* while()循环处理列表 xPendingReadyList,只要列表 xPendingReadyList 不为空,说明还
* 有任务挂到了列表 xPendingReadyList 上,这里需要将这些任务从列表 xPendingReadyList 上移
* 除并添加到这些任务所对应的就绪列表中。
* /
while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE )
{
//遍历列表 xPendingReadyList,获取挂到列表 xPendingReadyList 上的任务对应的任务控制块。
pxTCB = ( TCB_t * ) listGET_OWNER_OF_HEAD_ENTRY\ ( ( &xPendingReadyList ) );
//将任务从事件列表上删除。
( void ) uxListRemove( &( pxTCB->xEventListItem ) );
//将任务从壮态列表上移除。
( void ) uxListRemove( &( pxTCB->xStateListItem ) );
//调用函数 prvAddTaskToReadyList()将任务添加到就绪列表中。
prvAddTaskToReadyList( pxTCB );
//判断任务的优先级是否高于当前正在运行的任务,如果是的话需要将 xYieldPending 标记为 pdTRUE,表示需要进行任务切换。
if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) {
xYieldPending = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
/************************************************************************/
/****************************省略部分代码********************************/
/************************************************************************/
//进行任务切换。
if( xYieldPending != pdFALSE )
{
#if( configUSE_PREEMPTION != 0 )
{
//标记在函数 xTaskResumeAll()中进行了任务切换,变量 xAlreadyYielded 用于标记在函数 xTaskResumeAll()中是否有进行任务切换。
xAlreadyYielded = pdTRUE;
}
#endif
//调用函数 taskYIELD_IF_USING_PREEMPTION()进行任务切换,此函数本质上是一个宏,其实最终调用是通过调用函数 portYIELD()来完成任务切换的。
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
//退出临界区
taskEXIT_CRITICAL();
//返回变量 xAlreadyYielded,如果为 pdTRUE 的话表示在函数 xTaskResumeAll()中进行了任务切换,如果为 pdFALSE 的话表示没有进行任务切换。
return xAlreadyYielded;
}
设置系统节拍数
此 函 数 在 使 用 FreeRTOS 的 低 功 耗 tickless 模 式 的 时 候 会 用 到 , 即 宏configUSE_TICKLESS_IDLE 为 1。当使能低功耗 tickless 模式以后在执行空闲任务的时候系统时钟节拍中断就会停止运行,系统时钟中断停止运行的这段时间必须得补上,这个工作就是由函数 vTaskStepTick()来完成的,此函数在文件 tasks.c 中有如下定义:
void vTaskStepTick( const TickType_t xTicksToJump )
{
configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
//函数参数 xTicksToJump 是要加上的时间值,系统节拍计数器 xTickCount 加上这个时间值得到新的系统时间
xTickCount += xTicksToJump;
traceINCREASE_TICK_COUNT( xTicksToJump );
}