前言
本文用来记录自己学习FreeRTOS的过程,目的是给自己学习增加一些动力和乐趣,主要是从使用的方向来学习FreeRTOS,因为之前已经学习了一些基础知识,可能学习的过程比较杂乱,想到哪里就学到哪一步部分,也就写到哪一部分。
第一节 简述
对于FreeRTOS应用到实际中,需要以下几个步骤:
第一个步骤:创建任务
创建任务可以看成两个部分。
第一部分,创建一个开始任务。
在code中应该先运行开始任务,在开始任务中再创建项目中要使用到的实际任务,开始任务只运行一次,运行完开始任务后就可以将开始任务删除已节省MCU RAM的使用。
第二部分,创建项目中要使用的所有任务。
第二部分中任务函数的特点分为以下几点:
a. 任务函数不能返回,是一个无限循环函数;
b. 每个任务都有自己的栈,在创建是就已经分配了任务自己的栈,在运行任务函数时局部变量放在自己的栈里,任务函数中内部所有栈的开销都是使用任务自己的栈。其中栈空间是保存多少个字(word),而不是多少个字节(byte)。
c. 任务函数中使用的全局变量、静态变量存放在内存的某个区域,所有任务都可以共用。(注:不过要防止使用冲突,所以尽量使用局部变量。)
第二节 创建任务函数与删除任务函数
使用创建任务函数xTaskCreate与删除任务函数vTaskDelete直接根据函数参数输入对应的变量就可以创建或删除相应的任务。
1.创建任务函数(xTaskCreate)
BaseType_t xTaskCreate(
TaskFunction_t pvTaskCode,
//任务函数。(函数名就是指向函数的指针)
const char * const pcNane,
//具有描述性的任务名字,一般不会使用它。
const uint16_t usStackDepth,
//任务堆栈的大小,
void * const pvParameters,
//传递给函数的参数
UBaseType_t uxPriority,
//任务运行时的优先级
xTaskHandle *pvCreatedTask
//用于传递任务的句柄,可以引用从而对任务进行其他操作。任务创建成功以后会返回此任务的任务句柄,句柄就是任务的堆栈,其他函数可能会使用到该任务的任务句柄。
)
例,
//创建开始任务
xTaskCreate((TaskFunction_t )start_task, //对应的开始任务函数
(const char* )"start_task", //开始任务的名称
(uint16_t ) 512, //任务堆栈大小
(void* )NULL, //没有参数
(UBaseType_t ) 1, //任务优先级为1
(TaskHandle_t* ) &StartTask_Handler); //任务的句柄
2.删除任务函数(vTaskDelete)
void vTaskDelete(
TaskHandle_t xTaskToDelete
//要删除的任务的任务句柄
)
例,vTaskDelete(StartTask_Handler); //删除开始任务
3.创建任务函数(xTaskCreate)详解
任务的创建有两种方法,一种是使用动态创建xTaskCreate,一种是使用静态创建xTaskCreateStatic。
函数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 )
{
TCB_t *pxNewTCB; //初始化任务控制块Task control block(TCB)
BaseType_t xReturn; //初始化一个任务句柄,用于对于创建任务的一个句柄返回
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 )
{
pxNewTCB->pxStack = pxStack;
}
else
{
vPortFree( pxStack );
}
}
else
{
pxNewTCB = NULL;
}
}
if( pxNewTCB != NULL )
{
prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); //使用此函数初始化新的任务
prvAddNewTaskToReadyList( pxNewTCB ); //将新创建的任务添加到Ready列表
xReturn = pdPASS;
}
else
{
xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;
}
return xReturn;
}
函数prvInitialiseNewTask
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 ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */
{
StackType_t *pxTopOfStack; //栈顶指针
UBaseType_t x;
/* 如果使能了堆栈溢出检测功能或追踪功能的话就使用一个定值来 填充任务堆栈*/
#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) )
{
/* Fill the stack with a known value to assist debugging. */
( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) );
}
#endif /* ( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) ) */
/* 计算栈顶地址。这取决于堆栈是从高内存开始向低内存,还是从低内存开始向高内存。 */
#if( portSTACK_GROWTH < 0 )
{
//获取栈顶地址,并向下做字节对齐
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) );
/* 检查对齐是否正确 */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
}
#else /* portSTACK_GROWTH */
{
pxTopOfStack = pxNewTCB->pxStack;
/* Check the alignment of the stack buffer is correct. */
configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) );
/* The other extreme of the stack space is required if stack checking is
performed. */
pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 );
}
#endif /* portSTACK_GROWTH */
/*向任务控制块中存储任务名字*/
for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[ x ];
if( pcName[ x ] == 0x00 )
{
break;
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}
pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0';
pxNewTCB->uxPriority = uxPriority; //任务控制块的优先级
vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); //初始化任务控制块所在的链表
vListInitialiseItem( &( pxNewTCB->xEventListItem ) );
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); //设置链表的拥有者,即TCB自身
listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); //初始化链表的其他内容
listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); //
//使用下面内容初始化任务所用的堆栈
#if( portUSING_MPU_WRAPPERS == 1 )
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged );
}
#else /* portUSING_MPU_WRAPPERS */
{
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters );
}
#endif /* portUSING_MPU_WRAPPERS */
if( ( void * ) pxCreatedTask != NULL )
{
*pxCreatedTask = ( TaskHandle_t ) pxNewTCB; //生成任务句柄并返回
}
else
{
mtCOVERAGE_TEST_MARKER();
}
}