以下內容轉載自安富萊電子:http://forum.armfly.com/forum.php
1 、FreeRTOS 的 時鍾 節拍
任何操作系統都需要提供一個時鍾節拍,以供系統處理諸如延時、超時等與時間相關的事件。
時鍾節拍是特定的周期性中斷,這個中斷可以看做是系統心跳。中斷之間的時間間隔取決於不同的應用,一般是 1ms – 100ms。時鍾的節拍中斷使得內核可以將任務延遲若干個時鍾節拍,以及當任務等待事件發生時,提供等待超時等依據。時鍾節拍率越快,系統的額外開銷就越大。
FreeRTOS 的系統時鍾節拍可以在配置文件 FreeRTOSConfig.h 里面設置:
#define configTICK_RATE_HZ ( ( TickType_t ) 1000 )
如上所示的宏定義配置表示系統時鍾節拍是 1KHz,即 1ms。
2 、FreeRTOS 的 時 間 管 理
時間管理功能是 FreeRTOS 操作系統里面最基本的功能,同時也是必須要掌握好的。
2.1 時間延遲
FreeRTOS 中的時間延遲函數主要有以下兩個作用:
上面就是一個簡單的任務運行狀態的切換過程。
2.2 FreeRTOS 的時間相關函數
FreeRTOS 時間相關的函數主要有以下 4 個:
vTaskDelay ()
vTaskDelayUntil ()
xTaskGetTickCount()
xTaskGetTickCountFromISR()
下面我們對這 4 個函數依次進行說明:
2.3 函數 vTaskDelay
使用舉例:
2.4 函數 vTaskDelayUntil
函數原型:
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, /* 存儲任務上次處於非阻塞狀態時刻的變量地址 */
const TickType_t xTimeIncrement ); /* 周期性延遲時間 */
函數描述:
函數 vTaskDelayUntil 用於周期性延遲。
第 1 個參數,存儲任務上次處於非阻塞狀態時刻的變量地址。
第 2 個參數,周期性延遲時間。
使用這個函數要注意以下問題:
1. 使用此函數需要在 FreeRTOSConfig.h 配置文件中配置如下宏定義為 1
#define INCLUDE_vTaskDelayUntil 1
2. 用戶要注意此函數跟 vTaskDelay 的區別,2.7 小節詳細講解。
使用舉例:
2.5 函 數 xTaskGetTickCount
函數原型:
volatile TickType_t xTaskGetTickCount( void );
函數描述:
函數 xTaskGetTickCount 用於獲取系統當前運行的時鍾節拍數。
使用這個函數要注意以下問題:
1. 此函數用於在任務代碼里面調用,如果在中斷服務程序里面調用的話,需要使用函數xTaskGetTickCountFromISR,這兩個函數切不可混用。
使用舉例:
2.6 函 數 xTaskGetTickCountFromISR
函數原型:
volatile TickType_t xTaskGetTickCountFromISR( void );
函數描述:
函數 xTaskGetTickCountFromISR 用於獲取系統當前運行的時鍾節拍數。
使用這個函數要注意以下問題:
1. 此函數用於在中斷服務程序里面調用,如果在任務里面調用的話,需要使用函數 xTaskGetTickCount,
這兩個函數切不可混用。
使用舉例:
/* ********************************************************************************************************* * 函 數 名: TIM6_IRQHandler * 功能說明: TIM6 中斷服務程序。 * 形 參: 無 * 返 回 值: 無 ********************************************************************************************************* */ void TIM6_IRQHandler( void ) { TickType_t xTickCount; xTickCount = xTaskGetTickCountFromISR(); }
2.7 函數 vTaskDelay 和 vTaskDelayUntil 的區別
函數 vTaskDelayUntil 實現的是周期性延遲,而函數 vTaskDelay 實現的是相對性延遲,反映到實際
應用上有什么區別呢,下面就給大家舉一個簡單的例子。
運行條件:
有一個 bsp_KeyScan 函數,這個函數處理時間大概耗時 2ms。
有兩個任務,一個任務 Task1 是用的 vTaskDelay 延遲,延遲 10ms,另一個任務 Task2 是用的
vTaskDelayUntil 延遲,延遲 10ms。
不考慮任務被搶占而造成的影響。
實際運行過程效果:
Task1:
bsp_KeyScan+ vTaskDelay (10) ---> bsp_KeyScan + vTaskDelay (10)
|----2ms + 10ms 為一個周期------| |----2ms + 10ms 為一個周期----|
這個就是相對性的含義
Task2:
bsp_KeyScan + vTaskDelayUntil ---------> bsp_KeyScan + vTaskDelayUntil
|----10ms 為一個周期(2ms 包含在 10ms 內)---| |----10ms 為一個周期------|
這就是周期性的含義。
下面我們通過函數 vTaskDelay 來實現 vTaskDelayUntil,會有一個更加全面的認識:
/* ********************************************************************************************************* * 函 數 名: vTaskMsgPro * 功能說明: 消息處理,這里是用作 LED 閃爍 * 形 參: pvParameters 是在創建該任務時傳遞的形參 * 返 回 值: 無 * 優 先 級: 3 ********************************************************************************************************* */ 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); } } }
3、實驗 例程
實驗目的:
1. 學習 FreeRTOS 的周期性延遲和相對性延遲函數。
2. 注意相對性延遲函數 vTaskDelay 和周期性延遲函數 vTaskDelayUntil 的區別。
實驗內容:
1. K1 按鍵按下,串口打印任務執行情況(波特率 115200,數據位 8,奇偶校驗位無,停止位 1)。
2. K2 鍵按下,串口打印系統時鍾節拍數。
static void vTaskTaskUserIF(void *pvParameters) { uint8_t ucKeyCode; uint8_t pcWriteBuffer[500]; while(1) { ucKeyCode = bsp_GetKey(); if (ucKeyCode != KEY_NONE) { switch (ucKeyCode) { /* K1鍵按下 打印任務執行情況 */ case KEY_DOWN_K1: printf("=================================================\r\n"); printf("任務名 任務狀態 優先級 剩余棧 任務序號\r\n"); vTaskList((char *)&pcWriteBuffer); printf("%s\r\n", pcWriteBuffer); printf("\r\n任務名 運行計數 使用率\r\n"); vTaskGetRunTimeStats((char *)&pcWriteBuffer); printf("%s\r\n", pcWriteBuffer); break; /* K1鍵按下 打印系統時鍾節拍數 */ case KEY_DOWN_K2: printf("當前的系統時鍾節拍數 = %d\r\n", xTaskGetTickCount()); break; /* 其他的鍵值不處理 */ default: break; } } vTaskDelay(20); } }