freertos提供了soft timer,需要爲softtimer掛接callback,當時間到達後,就會觸發callback執行。
callback在timer的服務任務中執行的,所以,不能使用可能阻塞任務的API。
一般使用SysTick作爲softtimer的基礎時鐘。
兩次觸發callback的時間間隔xTimerPeriodInTicks叫做定時週期。
freertos通過一個prvTimerTask任務(Daemon Task)管理softtimer。這個任務是在啓動調度器時,自動創建的。
Daemon Task 會在執行期間,輪詢軟件定時器,如果滿足條件,則調用callback。
#define configUSE_TIMERS 1
#define configSUPPORT_DYNAMIC_ALLOCATION 1
#define configTICK_RATE_HZ 1000
這兩個配置宏,使能了軟件定時器,並設置了TICK。
類似於linux中的jiffies,freertos中,也會維護一個全局變量xTickCount,作爲系統時間。
當用戶創建一個softtimer時,freertos會根據用戶設置的定時數,在當前jiffies上後延設置的定時數,並填充到softtimer的喚醒時間中。然後將softtimer的控制塊插入軟件定時器列表。
freertos維護了兩個TIMERLIST。
pxCurrentTimerList, pxOverflowTimerList。
新創建的SoftTimer,會以jiffies升序的順序,插入pxCurrentTimerList.
DaemonTask在運行時,會按順序掃描pxCurrentTimerList,如果首元超時,則調用首元對應的Callback,如果首元都沒有超時,那麼其他定時器也一定沒有超時,所以這時,DaemonTask掛起。
pxOverflowTimerList作用與之一致。
編寫軟件定時器的callback時,應該快進快出,不允許阻塞DaemonTask,更不允許死循環。
DaemonTask的任務優先級應該儘量高,從而獲得更好的時間響應度。
單次軟件定時器的Callback被執行完並返回後,系統會自動刪除TIMER,並回收資源。
用戶程序通過Timer command queue和 Daemon Task進行通信。
在用戶程序中,使用TIMER API,freertos負責將一個command發送到Timer command queue中,當DaemonTask運行時,從command queue中獲取命令。
#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1)
#define configTIMER_QUEUE_LENGTH
#define configTIMER_STACK_DEPTH
這幾個宏,用來配置DaemonTask。
來看看TIMER的控制塊。
typedef struct tmrTimerControl{
const char* pcTimerName;
ListItem_t xTimerListItem;
TickType_t xTimerPeriodInTicks;
UBaseType_t uxAutoReload;
TimerCallbackFunction_t pxCallbackFunction;
void* pvTimerID;
}xTIMER;
typedef xTIMER Timer_t;
在xTimerListItem的value中,存放喚醒時間jiffies。
來看看TIMER API。
1)創建。
xTimerCreate(),動態創建一個TIMER對象,並返回句柄。
TimerHandle_t
xTimerCreate(
const char* const pcTimerName,
const TickType_t xTimerPeriodInTicks,
void * const pvTimerID,
TimerCallbackFunction_t pxCallbackFunction
);
2)啓動。
xTimerStart,將TIMER啓動,並添加到ACTIVELIST中。
BaseType_t xTimerStart(TimerHandle_t xTimer, TickType_t xBlockTime);
BaseType_t
xTimerStartFromISR(
TimerHandle_t xTimer,
BaseType_t* pxHigherPriorityTaskWoken
);
3)停止。
xTimerStop,將TIMER停止,並從ACTIVELIST中移除。
BaseType_t xTimerStop(TimerHandle_t xTimer, TickType_t xBlockTime);
BaseType_t
xTimerStopFromISR(
TimerHandle_t xTimer,
BaseType_t* pxHigherPriorityTaskWoken
);
4)復位。
xTimerReset,用來複位TIMER。
BaseType_t xTimerReset(TimerHandle_t xTimer, TickType_t xTicksToWait);
BaseType_t
xTimerResetFromISR(
TimerHandle_t xTimer,
BaseType_t* pxHigherPriorityTaskWoken
);
5)刪除。
BaseType_t xTimerDelete(TimerHandle_t xTimer, TickType_t xTicksToWait);
來看一個具體的例子。
static TimerHandle_t Swtmr1_Handle = NULL;
static TmerHandle_t Swtmr2_Handle = NULL;
// in apptask
Swtmr1_Handle = xTimerCreate(
(const char*)"AutoReloadTimer",
(TickType_t)1000,
(UBaseType_t)pdTRUE,
(void*)1,
(TimerCallbackFunction_t)Swtmr1_Callback
);
if(Swtmr1_Handle != NULL){
xTimerStart(Swtmr1_Handle, 0);
}
Swtmr2_Handle = xTimerCreate(
(const char*)"OneShotTimer",
(TickType_t)5000,
(UBaseType_t)pdFALSE,
(void*)2,
(TimerCallbackFunction_t)Swtmr2_Callback
);
if(Swtmr2_Handle != NULL){
xTimerStart(Swtmr2_Handle, 0);
}
static void Swtmr1_Callback(void* parameter)
{
TmrCb_Count1++;
return;
}
static void Swtmr2_Callback(void* parameter)
{
TmrCb_Count2++;
return;
}
對於系統中,一些基於時間事件驅動的操作和行爲,用TIMER是比較合適的。
系統負責管理時間點,開發者只需要關注於TIMEEVENT ACTION。