文章目錄
- 任務創建
- 任務的內容
- 推薦閱讀
任務創建
prvIdleTask
任務,是由任務調度函數vTaskStartScheduler
創建的,任務優先級0,任務堆棧深度由配置選項configMINIMAL_STACK_SIZE
定義。
void vTaskStartScheduler(void)
{/* 其他代碼*//* Add the idle task at the lowest priority. */xReturn = xTaskCreate(prvIdleTask,configIDLE_TASK_NAME,configMINIMAL_STACK_SIZE,(void *) NULL,portPRIVILEGE_BIT, &xIdleTaskHandle); /* 其他代碼*/
}
任務的內容
這里貼上源碼,然后對源碼做解析。
static portTASK_FUNCTION(prvIdleTask, pvParameters)
{/* Stop warnings. */(void) pvParameters;/** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE* SCHEDULER IS STARTED. **//* In case a task that has a secure context deletes itself, in which case* the idle task is responsible for deleting the task's secure context, if* any. */portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);for(; ;){/* See if any tasks have deleted themselves - if so then the idle task* is responsible for freeing the deleted task's TCB and stack. */prvCheckTasksWaitingTermination();#if (configUSE_PREEMPTION == 0){/* If we are not using preemption we keep forcing a task switch to* see if any other task has become available. If we are using* preemption we don't need to do this as any task becoming available* will automatically get the processor anyway. */taskYIELD();}#endif /* configUSE_PREEMPTION */#if ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1)){/* When using preemption tasks of equal priority will be* timesliced. If a task that is sharing the idle priority is ready* to run then the idle task should yield before the end of the* timeslice.** A critical region is not required here as we are just reading from* the list, and an occasional incorrect value will not matter. If* the ready list at the idle priority contains more than one task* then a task other than the idle task is ready to execute. */if(listCURRENT_LIST_LENGTH(&(pxReadyTasksLists[ tskIDLE_PRIORITY ])) > (UBaseType_t) 1){taskYIELD();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* ((configUSE_PREEMPTION == 1) && (configIDLE_SHOULD_YIELD == 1)) */#if (configUSE_IDLE_HOOK == 1){extern void vApplicationIdleHook(void);/* Call the user defined function from within the idle task. This* allows the application designer to add background functionality* without the overhead of a separate task.* NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES,* CALL A FUNCTION THAT MIGHT BLOCK. */vApplicationIdleHook();}#endif /* configUSE_IDLE_HOOK *//* This conditional compilation should use inequality to 0, not equality* to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when* user defined low power mode implementations require* configUSE_TICKLESS_IDLE to be set to a value other than 1. */#if (configUSE_TICKLESS_IDLE != 0){TickType_t xExpectedIdleTime;/* It is not desirable to suspend then resume the scheduler on* each iteration of the idle task. Therefore, a preliminary* test of the expected idle time is performed without the* scheduler suspended. The result here is not necessarily* valid. */xExpectedIdleTime = prvGetExpectedIdleTime();if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP){vTaskSuspendAll();{/* Now the scheduler is suspended, the expected idle* time can be sampled again, and this time its value can* be used. */configASSERT(xNextTaskUnblockTime >= xTickCount);xExpectedIdleTime = prvGetExpectedIdleTime();/* Define the following macro to set xExpectedIdleTime to 0* if the application does not want* portSUPPRESS_TICKS_AND_SLEEP() to be called. */configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING(xExpectedIdleTime);if(xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP){traceLOW_POWER_IDLE_BEGIN();portSUPPRESS_TICKS_AND_SLEEP(xExpectedIdleTime);traceLOW_POWER_IDLE_END();}else{mtCOVERAGE_TEST_MARKER();}}(void) xTaskResumeAll();}else{mtCOVERAGE_TEST_MARKER();}}#endif /* configUSE_TICKLESS_IDLE */}
}
-
portTASK_FUNCTION
是一個宏定義,專門用來定義任務函數,如下圖。
-
portALLOCATE_SECURE_CONTEXT(configMINIMAL_SECURE_STACK_SIZE);
portALLOCATE_SECURE_CONTEXT
是一個用于在 FreeRTOS 中分配安全上下文的函數,一般用在使用了TrustZone 技術的系統中。通過分配和初始化安全堆棧,它為非安全任務提供了必要的支持,以便在需要時與安全區域進行交互。TrustZone技術是由ARM公司開發的一種硬件安全技術,旨在通過將處理器和系統資源分為安全和非安全兩部分來增強系統的安全性。它廣泛應用于需要高安全性的嵌入式系統中,如智能手機、物聯網設備和支付系統。
-
prvCheckTasksWaitingTermination();
檢查是否有需要釋放內存的被刪除任務,當有任務調用函數vTaskDelete()
刪除自身的話,此任務就會添加到列表xTasksWaitingTermination
中。函數prvCheckTasksWaitingTermination()
會檢查列表xTasksWaitingTermination
是否為空,如果不為空的話就依次將列表中所有任務對應的內存釋放掉(任務控制塊 TCB 和任務堆棧的內存)。 -
使用搶占式內核并且 configIDLE_SHOULD_YIELD 為 1,說明空閑任務需要讓出時間片給同優先級的其他就緒任務。
-
檢查優先級為
tskIDLE_PRIORITY
(空閑任務優先級)的就緒任務列表是否為空,如果不為空的話就調用函數taskYIELD()
進行一次任務切換。 -
如果使能了空閑任務鉤子函數的話就執行這個鉤子函數,空閑任務鉤子函數的函數名為
vApplicationIdleHook()
,這個函數需要用戶自行編寫!在編寫這個這個鉤子函數的時候一定不能調用任何可以阻塞空閑任務的 API 函數。通常,使用這個空閑鉤子函數設置CPU進入低功耗模式。 -
剩下的都是Tickless模式的代碼
configUSE_TICKLESS_IDLE
不為 0,說明使能了 FreeRTOS 的低功耗 Tickless 模式。一般而言,在RTOS中為了降低功耗。運行空閑任務時需要進入低功耗模式,在處理應用層任務時需要將處理器從低功耗模式中喚醒。一般會在空閑任務的鉤子函數中執行低功耗相關處理,比如設置處理器進入低功耗模式、關閉其他外設時鐘、降低系統主頻等。
但FreeRTOS中的時鐘是由滴答定時器(一般頻率為1000Hz)來提供的,這會導致當處理器進入低功耗模式時會被滴答定時器中斷頻繁喚醒,導致低功耗模式作用大大降低。因此引入了Tickless模式——當處理器進入空閑任務周期后,就關閉滴答定時器中斷,只有當其他中斷發生或者其他任務需要處理的時候,處理器才會被從低功耗模式中喚醒。
但這將面臨兩個問題: 其一是關閉系統節拍中斷會導致系統節拍計數器停止,系統時鐘就會停止。 FreeRTOS 的系統時鐘是依賴于系統節拍中斷(滴答定時器中斷)的,如果關閉了系統節拍中斷的話就會導致系統時鐘停止運行,這是絕對不允許的!解決方法為–記錄下系統節拍中斷的關閉時間,當系統節拍中斷再次開啟運行的時候補上這段時間,因此這段時間也稱為補償時間。這時候就需要另外一個定時器來記錄這段補償時間,而F103是繼續采用滴答定時器來完成這個功能。
其二是當處理器進入睡眠模式之后,除了及時響應中斷外,還需要響應應用層的就緒任務,但是中斷可以喚醒睡眠模式,應用層任務卻不能主動將處理器從睡眠中喚醒。為了解決上述問題,采用記錄處理器在進入低功耗模式之前的待運行任務阻塞時間(也稱為低功耗持續時間),FreeRTOS中提供了
prvGetExpectedIdleTime()
函數來獲取這段時間。
推薦閱讀
空閑任務與鉤子函數