目录
本文只介绍动态创建流程。
使用方法(简述)
void task(void *parameter);
#define TASK_NAME "task"
#define TASK_SIEZ (64)
#define TASK_PRIORITY (1)
TaskHandle_t *TASK_HANDLE;
int main(void)
{
SysTick_init();//系统时钟初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//设置中断优先级分组
uart_init(115200);
xTaskCreate(task,
TASK_NAME,
TASK_SIEZ,
NULL,
TASK_PRIORITY,
TASK_HANDLE
);
vTaskStartScheduler();//启动调度器
while(1);
}
void task(void *parameter)
{
while(1)
{
printf("task create succeed\r\n");
vTaskDelay(500);
printf("delay 500\r\n");
vTaskDelay(500);
}
}
xTaskCreate函数
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, //任务函数
const char * const pcName, //函数名字,只在调试时使用(没什么用)
const uint16_t usStackDepth, //任务堆栈大小,以字为单位。
void * const pvParameters, //若函数有参,此参数传递参数
UBaseType_t uxPriority, //任务优先级
TaskHandle_t * const pxCreatedTask ) //任务句柄,用于对任务的各种操作
使用此函数会以动态分配内存的方式创建一个任务。
函数解析
下面是我认为有需要了解的删除了许多宏定义的创建任务函数。
/* 同样是简化删除了许多宏定义的结构体 */
struct xLIST_ITEM
{
/* 用于在某些列表中给任务排序 */
configLIST_VOLATILE TickType_t xItemValue;
/* 指向下一个任务 */
struct xLIST_ITEM * configLIST_VOLATILE pxNext;
/* 指向上一个任务 */
struct xLIST_ITEM * configLIST_VOLATILE pxPrevious;
/* 指向拥有此成员的任务控制块,用于通过列表项获取任务控制块 */
void * pvOwner;
/* 指向放置此列表项的列表 */
void * configLIST_VOLATILE pvContainer;
}
typedef struct xLIST_ITEM ListItem_t;
typedef struct tskTaskControlBlock
{
/* 此变量指向栈顶 */
volatile StackType_t *pxTopOfStack;
/* 用于挂载在各个状态的任务裂变中,就绪,阻塞,挂起 */
ListItem_t xStateListItem;
/* 供事件功能使用的列表 */
ListItem_t xEventListItem;
/* 存储任务优先级 */
UBaseType_t uxPriority;
/* 存储任务栈起始位置,可以用来计算栈使用情况 */
StackType_t *pxStack;
/* 记录任务名字,不能超过16个字符(最后一个时‘\0’) */
char pcTaskName[ configMAX_TASK_NAME_LEN ];
}
typedef struct xMINI_LIST_ITEM MiniListItem_t;
BaseType_t xTaskCreate( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint16_t usStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask )
{
TCB_t *pxNewTCB;
BaseType_t xReturn;
#else /* portSTACK_GROWTH */
{
StackType_t *pxStack;
/* 申请堆栈内存 */
pxStack = ( StackType_t * ) pvPortMalloc
( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) );
if( pxStack != NULL )
{
/* 为新任务控制块申请内存控件 */
pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );
if( pxNewTCB != NULL )
{
/* 申请成功, pxStack成员记录栈起始位置 */
pxNewTCB->pxStack = pxStack;
}
else
{
/* 申请失败,释放堆栈内存 */
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
#endif
/* 申请控制块成功 */
if( pxNewTCB != NULL )
{
/* 初始化新的任务控制块 */
prvInitialiseNewTask( pxTaskCode, pcName,
( uint32_t ) usStackDepth,
pvParameters,
uxPriority,
pxCreatedTask,
pxNewTCB, NULL );
/* 将新任务加入到就绪列表 */
prvAddNewTaskToReadyList( pxNewTCB );
/* 标记任务创建成功 */
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
prvInitialiseNewTask函数解析
/*
解析此函数时还是要了解一些FreeRTOS的各个列表的,组略介绍下。
这里我们只要指导FreeRTOS有阻塞队列,就绪队列,挂起队列
系统会在触发任务切换时从就绪列表中找出最高优先级任务。
这样我们能初步理解为什么要初始化列表项等成员就好了。
*/
static void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void * const pvParameters,
UBaseType_t uxPriority,
TaskHandle_t * const pxCreatedTask,
TCB_t *pxNewTCB,
const MemoryRegion_t * const xRegions ){
StackType_t *pxTopOfStack;
UBaseType_t x;
/* portSTACK_GROWTH用于设置堆栈是递减还是递增 ,这是递减的*/
#if( portSTACK_GROWTH < 0 )
{
/* 计算 栈顶位置 = 栈起始位置 + 任务栈大小(字为单位,以寄存器大小为单位) */
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
/* 使栈八字节对齐,栈顶地址 & ~(0x07)不难理解 二进制1000 等于 8 ,
不难想到只要前三位为0,该数为8的倍数 */
pxTopOfStack =
( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack )
& ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
}
/* 将任务名字初始化 configMAX_TASK_NAME_LEN = 16*/
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == 0x00 )
{
break;
}
}
/* 确保函数名最后一位是 '0'*/
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
/* configMAX_PRIORITIES为我们设置的最高的任务优先级,检查设置的优先级是否超出 */
if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES )
{
/* 超出,则设置为最高优先级 */
uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U;
}
/* 任务控制块存储任务优先级 */
pxNewTCB->uxPriority = uxPriority;
/* 初始化xStateListItem和xEventListItem 列表项 */
vListInitialiseItem( &( pxNewTCB->xStateListItem ) );
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
/* listSET_LIST_ITEM_OWNER宏定义 用于初始化OWNER,后续宏定义直接等价不写了
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB );
/*
/* 列表项pvowner 指向任务控制块,便于在列表中时找出任务控制块 */
&( pxNewTCB->xStateListItem ) ->pvowner = pxNewTCB;
/* 事件列表需要以优先级大小排序,设置xItemValue值 */
&( pxNewTCB->xEventListItem->xItemValue
= ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority;
&( pxNewTCB->xEventListItem->pvowner = pxNewTCB;
/* 初始化任务堆栈 */
pxNewTCB->pxTopOfStack
= pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
/* 任务句柄指向任务控制块,便于操作任务 */
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB;
}
void vListInitialiseItem( ListItem_t * const pxItem )
{
/* 此时任务项没有加入任何任务列表,所有初始指向NULL */
pxItem->pvContainer = NULL;
/*
listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem );
未置1 configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES时无用
/*
}
prvAddNewTaskToReadyList函数
static void prvInitialiseTaskLists( void )
{
UBaseType_t uxPriority;
/*初始化各个优先级的就绪任务列表*/
for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ )
{
vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) );
}
/* 初始化xDelayedTaskList1 xDelayedTaskList2 xPendingReadyList列表*/
vListInitialise( &xDelayedTaskList1 );
vListInitialise( &xDelayedTaskList2 );
vListInitialise( &xPendingReadyList );
/* 如果使能的删除任务功能的话,初始化xTasksWaitingTermination 此列表放置将要删除的任务,将在系统运行在空闲任务时删除 */
#if ( INCLUDE_vTaskDelete == 1 )
{
vListInitialise( &xTasksWaitingTermination );
}
#endif /* INCLUDE_vTaskDelete */
/* 使能了任务挂起功能,初始化xSuspendedTaskList 列表,该列表存储挂起的任务 */
#if ( INCLUDE_vTaskSuspend == 1 )
{
vListInitialise( &xSuspendedTaskList );
}
#endif /* INCLUDE_vTaskSuspend */
/* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList
using list2. */
pxDelayedTaskList = &xDelayedTaskList1;
pxOverflowDelayedTaskList = &xDelayedTaskList2;
}
/*
taskENTER_CRITICAL();进入临界区,就是屏蔽受系统控制的中断
taskEXIT_CRITICAL();退出临界区,下面是prvAddNewTaskToReadyList函数
*/
taskENTER_CRITICAL();
{
/* uxCurrentNumberOfTasks记录当前活跃任务数量 */
uxCurrentNumberOfTasks++;
/* 如果当前任务指针为空,第一次 */
if( pxCurrentTCB == NULL )
{
/* 直接切换到此任务 */
pxCurrentTCB = pxNewTCB;
/* 如果是第一次创建任务,则需要初始化各列表 */
if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 )
{
prvInitialiseTaskLists();
}
}
else
{
/* xSchedulerRunning 变量用于判断任务调度器是否允许 */
if( xSchedulerRunning == pdFALSE )
{
/* 调度器没有允许,如果新创建的任务比之前创建的任务优先级高则设置当前任务为新任务,确保第一个允许的任务时最高优先级任务 */
if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority )
{
pxCurrentTCB = pxNewTCB;
}
}
}
/* 将新任务加入到就绪列表中 */
prvAddTaskToReadyList( pxNewTCB );
}
taskEXIT_CRITICAL();
vTaskStartScheduler调度器启动函数
/* 函数完成的主要任务
1.创建空闲任务函数
2.初始化一些全局变量
3. 当使能软件定时器等功能时,还设置了定时器时基等。
*/
void vTaskStartScheduler( void )
{ //创建空闲任务。
#else
{
xReturn = xTaskCreate( prvIdleTask,
"IDLE", configMINIMAL_STACK_SIZE,
( void * ) NULL,
( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),
&xIdleTaskHandle );
}
#endif
//关中断
portDISABLE_INTERRUPTS();
/* xNextTaskUnblockTime 记录下一个任务阻塞事件,便于在事件到来时解除任务阻塞 */
xNextTaskUnblockTime = portMAX_DELAY;
/* xSchedulerRunning变量设为true表示调度器启用 */
xSchedulerRunning = pdTRUE;
/* xTickCount 记录调度器启动以来的系统节拍数 */
xTickCount = ( TickType_t ) 0U;
}
vTaskDelay函数
阻塞任务一定时间,xTicksToDelay参数为任务阻塞的节拍数。
void vTaskDelay( const TickType_t xTicksToDelay )
{
BaseType_t xAlreadyYielded = pdFALSE;
/* 任务阻塞节拍数如果不为0,为0切换一次函数不阻塞当前任务。*/
if( xTicksToDelay > ( TickType_t ) 0U )//节拍数不为0
{
/* 赞数关闭任务调度器,挂起所有任务 */
vTaskSuspendAll();
{
//将当前任务添加到阻塞任务中,阻塞xTicksToDelay秒
prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE );
}
//唤醒调度器
xAlreadyYielded = xTaskResumeAll();
}
else//节拍数为0
{
mtCOVERAGE_TEST_MARKER();
}
/* 唤醒失败,或者节拍数为0,开始一次任务切换 */
if( xAlreadyYielded == pdFALSE )
{
portYIELD_WITHIN_API();
}
}