1. 程式人生 > >從0開始學FreeRTOS-(建立任務)-2

從0開始學FreeRTOS-(建立任務)-2

# 補充 開始今天的內容之前,先補充一下上篇文章[從微控制器到作業系統-1](https://jiejietop.gitee.io/freertos-1/)的一點點遺漏的知識點。 ```js BaseType_t xTaskCreate(       TaskFunction_t pvTaskCode,                              const char * const pcName,                              uint16_t usStackDepth,                              void *pvParameters,                              UBaseType_t uxPriority,                              TaskHandle_t *pvCreatedTask                          ); ``` 建立任務中的堆疊大小問題,在task.h中有這樣子的描述: ```js /** * @param usStackDepth The size of the task stack specified as the number of variables the stack * can hold - not the number of bytes.  For example, if the stack is 16 bits wide and   * usStackDepth is defined as 100, 200 byteswill be allocated for stack storage. */ ``` 當任務建立時,核心會分為每個任務分配屬於任務自己的唯一堆疊。usStackDepth 值用於告訴核心為它應該分配多大的棧空間。 這個值指定的是棧空間可以儲存多少個字(word) ,而不是多少個位元組(byte)。 文件也有說明,如果是16位寬度的話,假如usStackDepth = 100;那麼就是200個位元組(byte)。 當然,我用的是stm32,32位寬度的, usStackDepth=100;那麼就是400個位元組(byte)。 好啦,補充完畢。下面正式開始我們今天的主題。 --- 我自己學的是應用層的東西,很多底層的東西我也不懂,水平有限,出錯了還請多多包涵。 其實我自己寫文章的時候也去跟著火哥的書看著底層的東西啦,但是本身自己也是不懂,不敢亂寫。所以,這個《從微控制器到作業系統》系列的文章,我會講一點底層,更多的是應用層,主要是用的方面。 按照一般的寫程式碼的習慣,在main函式裡面各類初始化完畢了,並且建立任務成功了,那麼,可以開啟任務排程了。 ```js int main(void) {    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);//設定系統中斷優先順序分組4        Delay_Init();                       //延時函式初始化        Uart_Init(115200);                  //初始化串列埠    LED_Init();                     //初始化LED    KEY_Init();    //建立開始任務    xTaskCreate((TaskFunction_t )start_task,            //任務函式                (const char*    )"start_task",          //任務名稱                (uint16_t       )START_STK_SIZE,        //任務堆疊大小                (void*          )NULL,                  //傳遞給任務函式的引數                (UBaseType_t    )START_TASK_PRIO,       //任務優先順序                (TaskHandle_t*  )&StartTask_Handler);   //任務控制代碼                  vTaskStartScheduler();          //開啟任務排程 } ``` 來大概看看分析一下建立任務的過程,雖然說會用就行,但是也是要知道了解一下的。 注意:下面說的建立任務均為xTaskCreate(動態建立)而非靜態建立。 ```js pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */            if( pxStack != NULL )            {                /* Allocate space for the TCB. */                pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );                /*lint !e961 MISRA exception as the casts are only redundant for some paths. */                if( pxNewTCB != NULL )                {                    /* Store the stack location in the TCB. */                    pxNewTCB->pxStack = pxStack;                }                else                {                    /* The stack cannot be used as the TCB was not created.  Free                    it again. */                    vPortFree( pxStack );                }            }            else            {                pxNewTCB = NULL;            }        } ``` 首先是利用`pvPortMalloc`給任務的堆疊分配空間,`if( pxStack != NULL )`如果記憶體申請成功,就接著給任務控制塊申請記憶體。`pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) );`同樣是使用`pvPortMalloc();`如果任務控制塊記憶體申請失敗則釋放 之前已經申請成功的任務堆疊`的記憶體vPortFree( pxStack );` 然後就初始化任務相關的東西,並且將新初始化的任務控制塊新增到列表中`prvAddNewTaskToReadyList( pxNewTCB );`   最後返回任務的狀態,如果是成功了就是`pdPASS`,假如失敗了就是返回`errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;` ```js prvInitialiseNewTask(    pxTaskCode,                       pcName,                         ( uint32_t ) usStackDepth,                       pvParameters,                       uxPriority,                         pxCreatedTask,                       pxNewTCB,                         NULL );            prvAddNewTaskToReadyList( pxNewTCB );            xReturn = pdPASS;        }        else        {            xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY;        }        return xReturn;    } // 相關巨集定義 #define pdPASS            ( pdTRUE ) #define pdTRUE            ( ( BaseType_t ) 1 ) /* FreeRTOS error definitions. */ #define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY    ( -1 ) ``` 具體的`static void prvInitialiseNewTask()`實現請參考`FreeRTOS`的`tasks.c`檔案的`767`行程式碼。具體的`static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB )`實現請參考`FreeRTOS`的`tasks.c`檔案的`963`行程式碼。 因為這些是`tasks.c`中的`靜態的函式`,僅供xTaskCreate建立任務內部呼叫的,我們無需理會這些函式的實現過程,當然如果需要請自行了解。 建立完任務就開啟任務排程了: ```js vTaskStartScheduler();          //開啟任務排程 ``` 在任務排程裡面,會建立一個空閒任務(我們將的都是動態建立任務,靜態建立其實一樣的) ```js xReturn = xTaskCreate(    prvIdleTask,                          "IDLE", configMINIMAL_STACK_SIZE,                          ( void * ) NULL,                          ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ),                          &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */    } 相關巨集定義: #define tskIDLE_PRIORITY            ( ( UBaseType_t ) 0U ) #ifndef portPRIVILEGE_BIT    #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) #endif #define configUSE_TIMERS                        1                               //為1時啟用軟體定時器 ``` 從上面的程式碼我們可以看出,空閒任務的優先順序是tskIDLE_PRIORITY為0,也就是說空閒任務的優先順序最低。當CPU沒事幹的時候才執行空閒任務,以待隨時切換優先順序更高的任務。 如果使用了軟體定時器的話,我們還需要建立定時器任務,建立的函式是: ```js #if ( configUSE_TIMERS == 1 )    BaseType_t xTimerCreateTimerTask( void ) ``` 然後還要把中斷關一下 ```js portDISABLE_INTERRUPTS(); ``` 至於為什麼關中斷,也有說明: ```js /* Interrupts are turned off here, toensure a tick does not occur before or during the call toxPortStartScheduler().  The stacks of the created tasks contain a status wordwith interrupts switched on so interrupts will automatically getre-enabled when the first task starts to run. */ ``` 中斷在這裡被關閉,以確保不會發生滴答在呼叫xPortStartScheduler()之前或期間。堆疊建立的任務包含一個開啟中斷的狀態字因此中斷將在第一個任務時自動重新啟用開始執行。 那麼如何開啟中斷呢????這是個很重要的問題 別擔心,我們在SVC中斷服務函式裡面就會開啟中斷的 看程式碼: ```js __asm void vPortSVCHandler( void ) {         PRESERVE8         ldr    r3, =pxCurrentTCB  /* Restore the context. */         ldrr1, [r3]                            /* UsepxCurrentTCBConst to get the pxCurrentTCB address. */         ldrr0, [r1]                            /* Thefirst item in pxCurrentTCB is the task top of stack. */         ldmiar0!, {r4-r11}             /* Pop theregisters that are not automatically saved on exception entry and the criticalnesting count. */         msrpsp, r0                                   /*Restore the task stack pointer. */         isb         movr0, #0         msr  basepri, r0         orrr14, #0xd         bxr14 } ``` ```js msr  basepri, r0 ``` 就是它把中斷開啟的。看不懂沒所謂,我也不懂彙編,看得懂知道就好啦。 ```js xSchedulerRunning = pdTRUE; ``` 任務排程開始執行 ```js /* If configGENERATE_RUN_TIME_STATS isdefined then the following macro must be defined to configure thetimer/counter used to generate the run time counter time base. */ portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); ``` 如果`configGENERATE_RUN_TIME_STATS`使用時間統計功能,這個巨集為`1`,那麼使用者必須實現一個巨集`portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();`用來配置一個定時器或者計數器。 來到我們的重點了,開啟任務排程,那麼任務到這了就不會返回了。 ```js if( xPortStartScheduler() != pdFALSE ) {    /*Should not reach here as if the scheduler is running the    functionwill not return. */                   } ``` 然後就能開啟第一個任務了,感覺好難是吧,我一開始也是覺得的,但是寫了這篇文章,覺得還行吧,也不算太難,可能也是在檢視程式碼跟別人的書籍吧,寫東西其實還是蠻好的,能加深理解,寫過文章的人就知道,懂了不一定能寫出來,所以,我還是很希望朋友們能投稿的。傑傑隨時歡迎。。。 開始任務就按照套路模板新增自己的程式碼就好啦,很簡單的。 先建立任務: ```js xTaskCreate((TaskFunction_t )led0_task,                  (const char*    )"led0_task",                (uint16_t       )LED0_STK_SIZE,              (void*          )NULL,                                                   (UBaseType_t    )LED0_TASK_PRIO,                (TaskHandle_t*  )&LED0Task_Handler);     //建立LED1任務   xTaskCreate((TaskFunction_t )led1_task,                   (const char*    )"led1_task",                 (uint16_t       )LED1_STK_SIZE,             (void*          )NULL,               (UBaseType_t    )LED1_TASK_PRIO,              (TaskHandle_t*  )&LED1Task_Handler);       ``` 建立完任務就開啟任務排程: ```js 1vTaskStartScheduler();          //開啟任務排程 ``` 然後具體實現任務函式: ```js //LED0任務函式 void led0_task(void *pvParameters) {   while(1)    {       LED0=~LED0;       vTaskDelay(500);    } }   //LED1任務函式 void led1_task(void *pvParameters) {   while(1)    {       LED1=0;       vTaskDelay(200);       LED1=1;       vTaskDelay(800);    } } ``` 好啦,今天的介紹到這了為止,後面還會持續更新,敬請期待哦~ 歡迎大家一起來討論作業系統的知識 我們的群號是:783234154 ![歡迎關注我公眾號](https://img2018.cnblogs.com/blog/1834930/201910/1834930-20191015203637034-1803649238.jpg) 更多資料歡迎關注“物聯網IoT開發”公