FreeRTOS 支持的定時器組,或者叫軟件定時器,又或者叫用戶定時器均可。軟件定時器的功能比較簡單,也容易掌握。被稱為定時器組是因為用戶可以創建多個定時器,創建的個數是可配置的。
定時器組介紹
FreeRTOS 軟件定時器組的時基是基于系統時鐘節拍實現的,之所以叫軟件定時器是因為它的實現不需要使用任何硬件定時器,而且可以創建很多個,綜合這些因素,這個功能就被稱之為軟件定時器組。
既然是定時器,那么它實現的功能與硬件定時器也是類似的。在硬件定時器中,我們是在定時器中斷中實現需要的功能,而使用軟件定時器時,我們是在創建軟件定時器時指定軟件定時器的回調函數,在回調函數中實現相應的功能。

單次模式和周期模式
FreeRTOS 提供的軟件定時器支持單次模式和周期性模式,單次模式就是用戶創建了定時器并啟動了定時器后,定時時間到將不再重新執行,這就是單次模式軟件定時器的含義。周期模式就是此定時器會按照設置的時間周期重復去執行,這就是周期模式軟件定時器的含義。另外就是單次模式或者周期模式的定時時間到后會調用定時器的回調函數,用戶可以回調函數中加入需要執行的工程代碼。
定時器任務(Daemon(守護進程) 任務)
為了更好的管理 FreeRTOS 的定時器組件, 專門創建了一個定時器任務, 或者稱之為 Daemon 任務。FreeRTOS 定時器組的大部分 API 函數都是通過消息隊列給定時器任務發消息,在定時器任務里面執行實際的操作。為了更好的說明這個問題,我們將官方在線版手冊中的這個截圖貼出來進行說明:

左側圖是用戶應用程序,右側是定時器任務。在用戶應用程序里面調用了定時器組API函xTimerReset,
這個函數會通過消息隊列給定時器任務發消息,在定時器任務里面執行實際操作。消息隊列在此處的作用有一個專門的名字:Timer command queue,即專門發送定時器組命令的隊列。
使用軟件定時器組注意事項
定時器回調函數是在定時器任務中執行的,實際應用中切不可在定時器回調函數中調用任何將定時器任務掛起的函數,比如vTaskDelay(),vTaskDelayUntil()以及非零延遲的消息隊列和信號量相關的函數。將定時器任務掛起,會導致定時器任務負責的相關功能都不能正確執行了。
定時器組 API 函數
使用如下 20 個函數可以實現 FreeRTOS 的定時器組:
? xTimerCreate()
? xTimerCreateStatic()
? xTimerIsTimerActive()
? xTimerStart()
? xTimerStop()
? xTimerChangePeriod()
? xTimerDelete()
? xTimerReset()
? xTimerStartFromISR()
? xTimerStopFromISR()
? xTimerChangePeriodFromISR()
? xTimerResetFromISR()
? pvTimerGetTimerID()
? vTimerSetTimerID()
? xTimerGetTimerDaemonTaskHandle()
? xTimerPendFunctionCall()
? xTimerPendFunctionCallFromISR()
? pcTimerGetName()
? xTimerGetPeriod()
? xTimerGetExpiryTime()
關于這 20 個函數的講解及其使用方法可以FreeRTOS 在線版手冊 。
這里我們重點的說以下 3 個函數:
? xTimerCreate()
? xTimerStart ()
? pvTimerGetTimerID ()
因為本章節配套的例子使用的是這 3 個函數。
函數 xTimerCreate
函數原型:

函數描述:
函數 xTimerCreate 用于創建軟件定時器。
? 第 1 個參數是定時器名字,用于調試目的,方便識別不同的定時器。
? 第 2 個參數是定時器周期,單位系統時鐘節拍。
? 第 3 個參數是選擇周期模式還是單次模式,若參數為 pdTRUE,則表示選擇周期模式,若參數為
pdFALSE,則表示選擇單次模式。
? 第 4 個參數是定時器 ID,當創建不同的定時器,但使用相同的回調函數時,在回調函數中通過不同的
ID 號來區分不同的定時器。
? 第 5 個參數是定時器回調函數。
? 返回值,創建成功返回定時器的句柄,由于 FreeRTOSCongfig.h 文件中 heap 空間不足,或者定時器周期設置為 0,會返回 NULL。
使用這個函數要注意以下問題:
1. 在 FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_TIMERS 1
函數 xTimerStart

函數 xTimerStart 用于啟動軟件定時器。
? 第 1 個參數是定時器句柄。
? 第 2 個參數是成功啟動定時器前的最大等待時間設置,單位系統時鐘節拍,定時器組的大部分 API函數不是直接運行的(見 上圖),而是通過消息隊列給定時器任務發消息來實現的,此參數設置的等待時間就是當消息隊列已經滿的情況下,等待消息隊列有空間時的最大等待時間。
? 返回值,返回 pdFAIL 表示此函數向消息隊列發送消息失敗,返回 pdPASS 表示此函數向消息隊列發送消息成功。定時器任務實際執行消息隊列發來的命令依賴于定時器任務的優先級,如果定時器任務是高優先級會及時得到執行,如果是低優先級,就要等待其余高優先級任務釋放 CPU 權才可以得到執行。
使用這個函數要注意以下問題:
1. 使用前一定要保證定時器組已經通過函數 xTimerCreate 創建了。
2. 在 FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_TIMERS 1
3. 對于已經被激活的定時器,即調用過函數 xTimerStart 進行啟動,再次調用此函數相當于調用了函數
xTimerReset 對定時器時間進行了復位。
4. 如果在啟動 FreeRTOS 調度器前調用了此函數, 定時器是不會立即執行的,需要等到啟動了 FreeRTOS調度器才會得到執行,即從此刻開始計時,達到 xTimerCreate 中設置的單次或者周期性延遲時間才會執行相應的回調函數。
函數 pvTimerGetTimerID
函數原型:
void *pvTimerGetTimerID( TimerHandle_t xTimer ); /* 定時器句柄 */
函數描述:
函數 pvTimerGetTimerID 用于返回使用函數 xTimerCreate 創建的軟件定時器 ID。
? 第 1 個參數是定時器句柄。
? 返回值,返回定時器 ID。
使用這個函數要注意以下問題:
1. 使用前一定要保證定時器組已經通過函數 xTimerCreate 創建了。
2. 在 FreeRTOSConfig.h 文件中使能宏定義:
#define configUSE_TIMERS 1
3. 創建不同的定時器時,可以對定時器使用相同的回調函數,在回調函數中通過此函數獲取是哪個定時器的時間到了,這個功能就是此函數的主要作用。
代碼操練場:
配置項:



實驗驗證:
使用軟件定時器,100ms一次實現led反轉,1000ms一次Beep翻轉。
主要展示定時器任務和回調函數:
定時器任務:
static void AppObjCreate (void){uint32_t i;const TickType_t xTimerPer[2] = {100, 1000};/*1. 創建定時器,如果在RTOS調度開始前初始化定時器,那么系統啟動后才會執行。2. 統一初始化兩個定時器,他們使用共同的回調函數,在回調函數中通過定時器ID來區分是那個定時器的時間到。當然,使用不同的回調函數也是沒問題的。*/for(i = 0; i < 2; i++){xTimers[i] = xTimerCreate("Timer", /* 定時器名字 */xTimerPer[i], /* 定時器周期,單位時鐘節拍 */pdTRUE, /* 周期性 */(void *) i, /* 定時器ID */vTimerCallback); /* 定時器回調函數 */if(xTimers[i] == NULL){/* 沒有創建成功,用戶可以在這里加入創建失敗的處理機制 */}else{/* 啟動定時器,系統啟動后才開始工作 */if(xTimerStart(xTimers[i], 100) != pdPASS)//等待延時100ms,其實設置成0在簡單任務下也是可以的,這個數值根據項目需求更改{/* 定時器還沒有進入激活狀態 */}}}}
回調函數:
static void vTimerCallback(xTimerHandle pxTimer){uint32_t ulTimerID;configASSERT(pxTimer);/* 獲取那個定時器時間到 */ulTimerID = (uint32_t)pvTimerGetTimerID(pxTimer);/* 處理定時器0任務 */if(ulTimerID == 0){LED2_TOGGLE;}/* 處理定時器1任務 */if(ulTimerID == 1){BEEP_TOGGLE;}}
通過ID不同,判斷是哪個定時器時間到,然后做相應的動作。