本文是《ALIENTEK STM32F429 FreeRTOS 开发教程》第十一章学习笔记
第一章笔记–FreeRTOS简介与源码下载
第二章笔记–FreeRTOS在STM32F4上移植
第三章笔记-FreeRTOS系统配置
第四章笔记-FreeRTOS中断分析
第四章笔记补充-FreeRTOS临界段代码
第五章笔记-FreeRTOS任务基础
第六章笔记-FreeRTOS任务API函数的使用
第七章笔记-FreeRTOS列表和列表项
第八章笔记-1-FreeRTOS任务创建
第八章笔记-2-FreeRTOS任务调度器开启
第九章笔记-FreeRTOS任务切换
第十章笔记-FreeRTOS系统内核控制函数
查询某个任务的优先级
UBaseType_t uxTaskPriorityGet( TaskHandle_t xTask )
{
TCB_t *pxTCB;
UBaseType_t uxReturn;
taskENTER_CRITICAL();
{
pxTCB = prvGetTCBFromHandle( xTask );
uxReturn = pxTCB->uxPriority;
}
taskEXIT_CRITICAL();
return uxReturn;
}
xTask:查找任务的任务句柄
taskENTER_CRITICA()和taskEXIT_CRITICAL():屏蔽中断和打开中断即进入和退出临界区
prvGetTCBFromHandle( xTask ):返回任务的TCB的指针,原型为:
#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? ( TCB_t * ) pxCurrentTCB : ( TCB_t * ) ( pxHandle ) )
这个宏定义可以检查,参数是否为空,和返回相应的TCB指针。
uxReturn = pxTCB->uxPriority:将TCB块中的任务优先级取出赋值用来作为函数返回值
改变某个任务的任务优先级
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
{
TCB_t *pxTCB;
UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry;
BaseType_t xYieldRequired = pdFALSE;
configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) );
if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
taskENTER_CRITICAL();
{
pxTCB = prvGetTCBFromHandle( xTask );
traceTASK_PRIORITY_SET( pxTCB, uxNewPriority );
#if ( configUSE_MUTEXES == 1 )
{
uxCurrentBasePriority = pxTCB->uxBasePriority;
}
#else
{
uxCurrentBasePriority = pxTCB->uxPriority;
}
#endif
if( uxCurrentBasePriority != uxNewPriority )
{
if( uxNewPriority > uxCurrentBasePriority )
{
if( pxTCB != pxCurrentTCB )
{
if( uxNewPriority >= pxCurrentTCB->uxPriority )
{
xYieldRequired = pdTRUE;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
else
{
}
}
else if( pxTCB == pxCurrentTCB )
{
xYieldRequired = pdTRUE;
}
else
{
}
uxPriorityUsedOnEntry = pxTCB->uxPriority;
#if ( configUSE_MUTEXES == 1 )
{
if( pxTCB->uxBasePriority == pxTCB->uxPriority )
{
pxTCB->uxPriority = uxNewPriority;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
pxTCB->uxBasePriority = uxNewPriority;
}
#else
{
pxTCB->uxPriority = uxNewPriority;
}
#endif
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL )
{
listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) )
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE )
{
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 )
{
portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
prvAddTaskToReadyList( pxTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
if( xYieldRequired != pdFALSE )
{
taskYIELD_IF_USING_PREEMPTION();
}
else
{
mtCOVERAGE_TEST_MARKER();
}
( void ) uxPriorityUsedOnEntry;
}
}
taskEXIT_CRITICAL();
}
xTask:改变的任务的任务句柄
uxNewPriority:任务要使用的新的优先级,范围是0~configMAX_PRIORITIES-1
configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ):若使用的新的优先级大于最大优先级则断言输出这一情况
if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ){…}: 确保新的优先级有效,如果大于最大优先级,则 “ uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U ” 将新的优先级赋值为最大优先级
#if ( configUSE_MUTEXES == 1 ){…}#else{…}#endif:若使用互斥信号量时,uxCurrentBasePriority = pxTCB->uxBasePriority;使用基础优先级,不使用互斥信号量时,uxCurrentBasePriority = pxTCB->uxPriority;
if( uxCurrentBasePriority != uxNewPriority ):如果任务优先级不等于要改变的优先级,则进行改变的相关操作
if( uxNewPriority > uxCurrentBasePriority ) { if( pxTCB != pxCurrentTCB ) { if( uxNewPriority >= pxCurrentTCB->uxPriority ):修改优先级高于现在优先级时,而且要修改任务不是当前执行任务,重要的是修改优先级的值大于现在执行任务的优先值,要执行xYieldRequired = pdTRUE,目的是要在后面执行任务切换,因为改变了优先级后,最高优先级任务就发生变化,处理器要去执行高优先级任务
else{}: 相对于if( pxTCB != pxCurrentTCB ),因为正在运行任务是要改变优先级的任务,而正在运行的任务必然是最高优先级的任务,所以改变优先级后不需要进行任务切换
else if( pxTCB == pxCurrentTCB ):相对于if( uxNewPriority > uxCurrentBasePriority )这条语句,即如果修改优先级的值小于现在的优先级,但是要修改的任务是现在正在执行的任务,则有可能修改后优先值不是最高优先级,需要进行任务切换,所以xYieldRequired = pdTRUE;
else{}:相对于if( uxNewPriority > uxCurrentBasePriority )和else if( pxTCB == pxCurrentTCB ),即要修改成的优先级值小于修改前优先级,并且修改任务不是当前执行任务,所以还是会执行最高优先级任务
uxPriorityUsedOnEntry = pxTCB->uxPriority:将改变前的优先级值记下,要在后面改变任务的就绪列表位置里用到
#if ( configUSE_MUTEXES == 1 ){ if( pxTCB->uxBasePriority == pxTCB->uxPriority ):当使用互斥信号量,基础优先级等于任务优先级时,即不使用继承优先级,执行pxTCB->uxPriority = uxNewPriority;pxTCB->uxBasePriority = uxNewPriority;将要更新的优先级值赋值到TCB的任务优先级和基础优先级上
if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ):如果该事件列表项的值未用于其它操作,则执行listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) );重置该事件列表项值
if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ):判断要改变任务是不是在就绪列表中,如果是在就绪列表中则要将其放在新优先级的列表里,如果是在延迟或者阻塞列表,则只需要改变优先级变量即可。
//检查列表项是否在列表中
#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( BaseType_t ) ( ( pxListItem )->pvContainer == ( void * ) ( pxList ) ) )
if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ):将任务绪前先将其从就绪列表删除
portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ):优先级复位宏调用
#define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) )
if( xYieldRequired != pdFALSE ) { taskYIELD_IF_USING_PREEMPTION(): 如果之前代码中判断需要进行一次任务切换,则执行taskYIELD_IF_USING_PREEMPTION()
#define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API()
#define portYIELD_WITHIN_API portYIELD
#define portYIELD()
{
portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT;
__dsb( portSY_FULL_READ_WRITE );
__isb( portSY_FULL_READ_WRITE );
}
开启PendSV中断,并用dsb和isb指令 完成数据同步隔离和指令同步隔离
( void ) uxPriorityUsedOnEntry:有可能没有使用到这一变量,这里是为了消除编译器的警告。
此函数用于获取系统中所有任务的任务状态,每个任务的状态信息保存在一个TaskStatus_t类型的结构体里面,这个结构体里面包含了任务的任务句柄、任务名字、堆栈、优先级等信息
要使用此函数则要将宏configUSE_TRACE_FACILITY设置为1
UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime )
{
UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES;
vTaskSuspendAll();
{
if( uxArraySize >= uxCurrentNumberOfTasks )
{
do
{
uxQueue--;
uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady );
} while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY );
uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked );
uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked );
#if( INCLUDE_vTaskDelete == 1 )
{
uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted );
}
#endif
#if ( INCLUDE_vTaskSuspend == 1 )
{
uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended );
}
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1)
{
if( pulTotalRunTime != NULL )
{
#ifdef portALT_GET_RUN_TIME_COUNTER_VALUE
portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) );
#else
*pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE();
#endif
}
}
#else
{
if( pulTotalRunTime != NULL )
{
*pulTotalRunTime = 0;
}
}
#endif
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
( void ) xTaskResumeAll();
return uxTask;
}
pxTaskStatusArray:指向TaskStatus_t结构体类型的数组首地址,每个任务至少需要一个TaskStaus_t结构体,任务的数量可以使用函数uxTaskGetNumberOfTasks()
结构体TaskStatus_t:
typedef struct xTASK_STATUS
{
TaskHandle_t xHandle; //任务句柄
const char *pcTaskName; //任务名字
UBaseType_t xTaskNumber; //任务编号
eTaskState eCurrentState; //当前任务状态
UBaseType_t uxCurrentPriority; //任务当前优先级
UBaseType_t uxBasePriority; //任务基础优先级
uint32_t ulRunTimeCounter; //任务运行的总时间
StackType_t *pxStackBase; //堆栈基地址
uint16_t usStackHighWaterMark; //从任务创建以来任务堆栈剩余的最小大小,此值太小说明堆栈有溢出的风险
} TaskStatus_t;
uxArraySize: 保存任务状态数组的数组的大小
pulTotalRunTime:如果configGENERATE_RUN_TIME_STATS为1的话此参数用来保存系统总的运行时间
返回值:统计到的任务状态的个数,即填写到数组pxTaskStatusArray中的个数,此值等于函数uxTaskGetNumberOfTasks()的返回值。
UBaseType_t uxTaskGetNumberOfTasks( void )
{
return uxCurrentNumberOfTasks;
}
vTaskSuspendAll():挂起调度器
prvListTasksWithinSingleList():
填充taskstatus_t结构对每个任务的信息是从pxlist列表引用(这可能是一个就绪列表,延迟列表,阻塞列表,等
static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState )
{
volatile TCB_t *pxNextTCB, *pxFirstTCB;
UBaseType_t uxTask = 0;
if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 )
{
listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList );
do
{
listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList );
vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState );
uxTask++;
} while( pxNextTCB != pxFirstTCB );
}
else
{
mtCOVERAGE_TEST_MARKER();
}
return uxTask;
}
遍历pxlist列表通过函数vTaskGetInfo()并把每个任务信息赋值到pxTaskStatusArray列表中
uxTask += …..: 分别将各列表中任务信息取出放在pxTaskStatusArray列表里,返回值加到pxTask即总共任务个数
函数用来获取单个指定的任务状态,任务的状态信息放入参数pxTaskStatus里
使用此函数将宏configUSE_TRACE_FACILITY设置为1
void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState )
{
TCB_t *pxTCB;
pxTCB = prvGetTCBFromHandle( xTask );
pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB;
pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName [ 0 ] );
pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority;
pxTaskStatus->pxStackBase = pxTCB->pxStack;
pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber;
#if ( INCLUDE_vTaskSuspend == 1 )
{
if( pxTaskStatus->eCurrentState == eSuspended )
{
vTaskSuspendAll();
{
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL )
{
pxTaskStatus->eCurrentState = eBlocked;
}
}
xTaskResumeAll();
}
}
#endif /* INCLUDE_vTaskSuspend */
#if ( configUSE_MUTEXES == 1 )
{
pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority;
}
#else
{
pxTaskStatus->uxBasePriority = 0;
}
#endif
#if ( configGENERATE_RUN_TIME_STATS == 1 )
{
pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter;
}
#else
{
pxTaskStatus->ulRunTimeCounter = 0;
}
#endif
if( eState != eInvalid )
{
pxTaskStatus->eCurrentState = eState;
}
else
{
pxTaskStatus->eCurrentState = eTaskGetState( xTask );
}
if( xGetFreeStackSpace != pdFALSE )
{
#if ( portSTACK_GROWTH > 0 )
{
pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack );
}
#else
{
pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack );
}
#endif
}
else
{
pxTaskStatus->usStackHighWaterMark = 0;
}
}
xTask:要查找的任务句柄
pxTaskStatus: 指向类型为TaskStatus_t的结构体
xGetFreeStackSpace:结构体TaskStatus_t中有个字段usStackHighWaterMark来保存任务运行以来任务堆栈剩余的历史最小大小,计算要花费时间,xGetFreeStackSpace设置为pdFALSE可以跳过这一步骤
eState: TaskStatus_t中的字段eCurrentState用来保存任务运行状态,这个字段是eTaskState类型
eTaskState:
typedef enum
{
eRunning = 0, //运行状态
eReady, //就绪态
eBlocked, //阻塞态
eSuspended, //挂起态
eDeleted, //任务被删除
eInvalid //无效
} eTaskState;
pxTCB = prvGetTCBFromHandle( xTask ):通过任务句柄获取任务的TCB
pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB;
pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName [ 0 ] );
pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority;
pxTaskStatus->pxStackBase = pxTCB->pxStack;
pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber;
把TCB中的相关状态信息,放入pxTaskStatus里
if( pxTaskStatus->eCurrentState == eSuspended ):如果任务在暂停列表中,那么它实际上有可能无限期地被阻塞——所以实际上它应该被报告为处于阻塞状态。
if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ){pxTaskStatus->eCurrentState = eBlocked:如果TCB的事件列表中有列表项则将状态变为阻塞
#if ( configUSE_MUTEXES == 1 );#if ( configGENERATE_RUN_TIME_STATS == 1 ) :用来条件编译是否获取基础优先级和任务运行总时间
if( eState != eInvalid ):如果传入任务运行状态参数不是无效,则放入pxTaskStatus里pxTaskStatus->eCurrentState = eState; 如果参数是无效则调用函数获得运行状态放入pxTaskStatus->eCurrentState = eTaskGetState( xTask );
if( xGetFreeStackSpace != pdFALSE ):如果xGetFreeStackSpace参数不是pdFALSE则用usStackHighWaterMark记录堆栈历史剩余最小大小
prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack );prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ): 用来计算递增堆栈和递减堆栈的历史剩余最小大小