FreeRTOS時間管理(上)-系統延時


轉自:https://www.cnblogs.com/yangguang-it/p/7181420.html

FreeRTOS 的時鍾節拍
任何操作系統都需要提供一個時鍾節拍,以供系統處理諸如延時、 超時等與時間相關的事件。
時鍾節拍是特定的周期性中斷,這個中斷可以看做是系統心跳。 中斷之間的時間間隔取決於不同的應
用,一般是 1ms – 100ms。時鍾的節拍中斷使得內核可以將任務延遲若干個時鍾節拍,以及當任務等待
事件發生時,提供等待超時等依據。時鍾節拍率越快,系統的額外開銷就越大。
對於 Cortex-M3 內核的 STM32F103 和 Cortex-M4 內核的 STM32F407 以及 F429,教程配套的例
子都是用滴答定時器來實現系統時鍾節拍的。
滴答定時器 Systick
SysTick 定時器被捆綁在 NVIC 中,用於產生 SysTick 異常(異常號: 15), 滴答定時器是一個 24 位
的遞減計數器,支持中斷。 使用比較簡單, 專門用於給操作系統提供時鍾節拍。
FreeRTOS 的系統時鍾節拍可以在配置文件 FreeRTOSConfig.h 里面設置:
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
如上所示的宏定義配置表示系統時鍾節拍是 1KHz,即 1ms。
時間延遲
FreeRTOS 中的時間延遲函數主要有以下兩個作用:
為周期性執行的任務提供延遲。
對於搶占式調度器,讓高優先級任務可以通過時間延遲函數釋放 CPU 使用權,從而讓低優先級任務可以得到執行。

下面我們通過如下的框圖來說明一下延遲函數對任務運行狀態的影響,讓大家有一個形象的認識。

運行條件:
僅對任務 Task1 的運行狀態做說明。
調度器支持時間片調度和搶占式調度。
運行過程描述如下:
起初任務 Task1 處於運行態,調用 vTaskDelay 函數后進入到阻塞狀態,也就是 blocked 狀態。
vTaskDelay 函數設置的延遲時間到,由於任務 Task1 不是當前就緒的最高優先級任務,所以不能進
入到運行狀態,只能進入到就緒狀態,也就是 ready 狀態。
一段時間后, 調度器發現任務 Task1 是當前就緒的最高優先級任務,從而任務從就緒態切換到運行態。
由於時間片調度,任務 Task1 由運行態切換到就緒態。

FreeRTOS 的時間相關函數
FreeRTOS 時間相關的函數主要有以下 4 個:
vTaskDelay ()
vTaskDelayUntil ()
xTaskGetTickCount()
xTaskGetTickCountFromISR() 

 

數 xTaskGetTickCount

函數原型:
volatile TickType_t xTaskGetTickCount( void );
函數描述:
函數 xTaskGetTickCount 用於獲取系統當前運行的時鍾節拍數。
使用這個函數要注意以下問題:
1. 此函數用於在任務代碼里面調用,如果在中斷服務程序里面調用的話,需要使用函數
xTaskGetTickCountFromISR,這兩個函數切不可混用。

eg:

數 xTaskGetTickCountFromISR

函數原型:
volatile TickType_t xTaskGetTickCountFromISR( void );
函數描述:
函數 xTaskGetTickCountFromISR 用於獲取系統當前運行的時鍾節拍數。
使用這個函數要注意以下問題:
1. 此函數用於在中斷服務程序里面調用, 如果在任務里面調用的話, 需要使用函數 xTaskGetTickCount,
這兩個函數切不可混用。

 

eg:

void TIM6_IRQHandler( void )
{
TickType_t xTickCount;
xTickCount = xTaskGetTickCountFromISR();

}

函數 vTaskDelay

 

函數原型:
void vTaskDelay(const TickType_t xTicksToDelay ); /* 延遲時間長度 */

函數描述:
函數 vTaskDelay 用於任務的延遲。
參數 xTicksToDelay 用於設置延遲的時鍾節拍個數,范圍 1- 0xFFFFFFFF。 延遲時間的最大值在
portmacro.h 文件里面有定義:
typedef uint32_t TickType_t;
#define portMAX_DELAY ( TickType_t )0xffffffffUL
即延遲時間的范圍是:1- 0xFFFFFFFF

函數 vTaskDelayUntil
函數原型:
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, /* 存儲任務上次處於非阻塞狀態時刻的變量地址 */
const TickType_t xTimeIncrement ); /* 周期性延遲時間 */
函數描述:
函數 vTaskDelayUntil 用於周期性延遲。
第 1 個參數,存儲任務上次處於非阻塞狀態時刻的變量地址。
第 2 個參數,周期性延遲時間。
使用這個函數要注意以下問題:
1. 使用此函數需要在 FreeRTOSConfig.h 配置文件中配置如下宏定義為 1
#define INCLUDE_vTaskDelayUntil 1
函數 vTaskDelay 和 vTaskDelayUntil 的區別
函數 vTaskDelayUntil 實現的是周期性延遲,而函數 vTaskDelay 實現的是相對性延遲,反映到實際
應用上有什么區別呢,下面就給大家舉一個簡單的例子。
運行條件:
有一個 bsp_KeyScan 函數,這個函數處理時間大概耗時 2ms。
有兩個任務,一個任務 Task1 是用的 vTaskDelay 延遲,延遲 10ms,另一個任務 Task2 是用的
vTaskDelayUntil 延遲,延遲 10ms。
不考慮任務被搶占而造成的影響

 

 eg1:絕對延時vTaskDelayUntil函數實現

 

復制代碼
static void vTaskLED(void *pvParameters) { TickType_t xLastWakeTime; const TickType_t xFrequency = 200; /* 獲取當前的系統時間 */ xLastWakeTime = xTaskGetTickCount(); while(1) { bsp_LedToggle(2); /* vTaskDelayUntil是絕對延遲,vTaskDelay是相對延遲。*/ vTaskDelayUntil(&xLastWakeTime, xFrequency); } }
復制代碼

eg2:絕對延時vTaskDelay函數實現

復制代碼
static void vTaskMsgPro(void *pvParameters) { TickType_t xDelay, xNextTime; const TickType_t xFrequency = 200; /* 獲取xFrequency個時鍾節拍后的時間 */ xNextTime = xTaskGetTickCount() + xFrequency; while(1) { bsp_LedToggle(3); /* 用vTaskDelay實現vTaskDelayUntil() */ xDelay = xNextTime - xTaskGetTickCount(); xNextTime += xFrequency; if(xDelay <= xFrequency) { vTaskDelay(xDelay); } } }
復制代碼

 要理解eg2需要破費心思。

想要用相對延時的方式實現絕對延時,我們首先獲取當前系統節拍,並且加上200個節拍,在我的測試例子中,一個節拍是1ms。這說明我是想要實現一個200ms的周期性任務。在執行完led翻轉之后,我獲取當前節拍數,用之前保存的想要延時數xNextTime減去當前節拍,得到的這個節拍就應該是我們用相對延時vTaskDelay去延時的時間。但是為什么程序實現中還要加上xNextTime += xFrequency;和一個if判斷if(xDelay <= xFrequency)呢?(當xNextTime 減去xTaskGetTickCount()減得夠的時候,if條件是肯定滿足的,而此時xNextTime += xFrequency是為了給下一個周期的nexttime賦值一個固定的時鍾節拍,這里是200).但是,有個問題需要注意,要是我的不是一個簡單的led任務,而是一段運行時間超過200ms的程序呢?這樣會導致 xNextTime - xTaskGetTickCount();得到一個小於0的數,但是定義的無符號類型,會發生有符號到無符號的轉換,這個時候,程序就會出錯(此時的delay函數不再准確),和其他任何定時器任務一樣,周期性任務設計的時候,應該確保在周期內完成任務工作。



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM