前言
- 本博文基于FreeRTOS V9.0.0和MDK环境;
- 本博文属于野火学习笔记,如有不足之处还请多多指教;
任务的大三元素
先说几个基本概念:
任务: 相当于裸机大循环系统中的每个独立功能;例如LED和USART功能都可以作为一个单独的任务来说;
任务执行和裸机执行的区别: 裸机是顺序执行来完成总任务,而RTOS是通过任务的切换来完成;
①:任务函数 (独立函数,无限循环)
任务函数是最终实现功能的地方;
void task_entry (void *parg)
{
/* 任务主体,无限循环且不能返回 */
for (;;) {
/* 任务主体代码 */
}
}
②:任务控制块TCB
每一个任务都有一个TCB,在程序中,TCB是任务的身份证,TCB结构体内包含着任务的重要信息;
typedef struct tskTaskControlBlock
{
volatile StackType_t *pxTopOfStack; /*栈顶*/
ListItem_t xStateListItem; /*任务节点*/
StackType_t *pxStack; /*任务栈起始地址*/
char pcTaskName[ configMAX_TASK_NAME_LEN ]; /*任务名称,字符串形式*/
}tskTCB;
③任务栈(静态栈/动态栈)
每个任务都有一个独立的任务栈,用来临时存储截点信息,属于软件堆栈;
//静态栈
#define TASK1_STACK_SIZE 128
StackType_t Task1Stack[TASK1_STACK_SIZE];
//动态栈:暂无
任务的创建流程
代码如下:
TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, //任务函数名
const char * const pcName, //配置任务名
const uint32_t ulStackDepth, //任务堆栈尺寸(32位为单位)
void * const pvParameters, //任务参数;
UBaseType_t uxPriority, //任务优先级 (野火这里对源程序有改动)
StackType_t * const puxStackBuffer, //任务堆栈缓冲区起始地址
TCB_t * const pxTaskBuffer //任务控制块起始地址;(野火在这里对源程序有改动)
)
{
TCB_t *pxNewTCB;
TaskHandle_t xReturn; //建立一个任务句柄
if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer ) )
{
pxNewTCB = ( TCB_t * )pxTaskBuffer;
pxNewTCB->pxStack = ( StackType_t * )puxStackBuffer;
//建立新的任务
prvInitialiseNewTask( pxTaskCode,
pcName,
ulStackDepth,
pvParameters,
&xReturn,
pxNewTCB
);
}
else
xReturn = NULL;
//返回任务句柄,如果任务创建成功,此时XReturn应该指向任务控制块
return xReturn;
}
//新任务初始化函数
void prvInitialiseNewTask( TaskFunction_t pxTaskCode,
const char * const pcName,
const uint32_t ulStackDepth,
void *const pvParameters,
TaskHandle_t *const pxCreatedTask,
TCB_t *pxNewTCB
)
{
StackType_t *pxTopOfStack;
UBaseType_t x;
/**********初始化TCB*************/
//获取栈顶地址
pxTopOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t )1 );
//栈顶地址乡下做8字节对齐
pxTopOfStack = ( StackType_t * )( ( ( uint32_t )pxTopOfStack )&(~(( uint32_t )0x007)) );
//将任务的名字存储在TCB中
for( x = ( UBaseType_t )0; x < ( UBaseType_t )configMAX_TASK_NAME_LEN; x++ )
{
pxNewTCB->pcTaskName[ x ] = pcName[x];
if( pcName[x] == 0x00 )
break;
}
//任务名字长度不能超过configMAX_TASK_NAMW_LEN
pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN - 1] = '\0';
/************初始化任务栈****************/
pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack,
pxTaskCode,
pvParameters);
//让任务句柄指向任务控制块
if( ( void *)pxCreatedTask != NULL)
{
*pxCreatedTask = ( TaskHandle_t )pxNewTCB;
}
}
//定义任务栈初始初始化函数
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, //此时进来的栈顶指针已经是指向栈顶的8字节对齐指针;
TaskFunction_t pxCode, //任务函数名
void *pvParameters //任务参数
)
{
//异常发生时,自动加载到CPU寄存器的内容;
pxTopOfStack--;
*pxTopOfStack = portINITIAL_XPSR;
pxTopOfStack--;
*pxTopOfStack = ( ( StackType_t )pxCode )&portSTART_ADDRESS_MASK;
pxTopOfStack--;
*pxTopOfStack = ( StackType_t )prvTaskExitError;
pxTopOfStack -= 5;
*pxTopOfStack = ( StackType_t )pvParameters;
//异常发生时,手动加载CPU寄存器的内容;
pxTopOfStack -= 8;
//返回栈顶指针,此时pxTopOfStack指向空闲栈;
return pxTopOfStack;
}