前言
- 本博文基於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;
}