09 在ZStack裡的定時器應用
阿新 • • 發佈:2019-02-15
ZStack裡的任務是基於事件來排程的, 裡面的定時器其實就是在約定的時間到後,設定指定任務的事件,從而讓設定定時的任務處理定時事件.
定時器封裝在工程目錄OSAL裡的OSAL_Timers.h標頭檔案裡:
extern uint8 osal_start_timerEx( uint8 task_id, uint16 event_id, uint16 timeout_value ); //設定timeout_value毫秒後,系統會設定task_id任務號的任務發生event_id事件. 此函式建立的定時器是一次性的.
extern uint8 osal_start_reload_timer( uint8 taskID, uint16 event_id, uint16 timeout_value ); //此定時器與上面的定時器相似,但可重複使用。
extern uint8 osal_stop_timerEx( uint8 task_id, uint16 event_id ); //停止定時器
extern uint32 osal_GetSystemClock( void ); //返回系統的時鐘,毫秒級別
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
此定時器功能是依賴SOC上的Timer2(MAC Timer)來實現的.
大概過程:
void osal_run_system( void ) //在系統任務的死迴圈檢查事件函式裡
{
uint8 idx = 0 ;
osalTimeUpdate(); //根據Timer2的計時來更新系統時間
Hal_ProcessPoll();
...
}
///////////////
void osalTimeUpdate( void )
{
...
if ( elapsedMSec )
{
osalClockUpdate( elapsedMSec );
osalTimerUpdate( elapsedMSec ); //更新系統裡定時器時間
}
}
///////////////////////////////////////////////////////////
//當呼叫osal_start_timerEx函式建立物件時:
uint8 osal_start_timerEx( uint8 taskID, uint16 event_id, uint16 timeout_value )
{
...
newTimer = osalAddTimer( taskID, event_id, timeout_value );
...
}
////////////////////////////
typedef struct
{
void *next;
uint16 timeout; //此值為0時表示定時器已超時
uint16 event_flag; //記錄定時器超時後給task_id任務設定什麼事件
uint8 task_id;
uint16 reloadTimeout; //重複性定時器用的初始計時值
} osalTimerRec_t;
osalTimerRec_t *timerHead; //timerHead是全域性指標變數,也就是定時器的連結串列頭節點
osalTimerRec_t * osalAddTimer( uint8 task_id, uint16 event_flag, uint16 timeout )
{
osalTimerRec_t *newTimer;
osalTimerRec_t *srchTimer;
// New Timer
newTimer = osal_mem_alloc( sizeof( osalTimerRec_t ) ); //動態建立一個定時器物件
if ( newTimer )
{
// 初始化定時器物件
newTimer->task_id = task_id;
newTimer->event_flag = event_flag;
newTimer->timeout = timeout;
newTimer->next = (void *)NULL;
newTimer->reloadTimeout = 0;
if ( timerHead == NULL ) //如果還沒有頭節點,則創建出來的定時器物件就成為頭節點
timerHead = newTimer;
else
{
srchTimer = timerHead;
while ( srchTimer->next ) //如果已經有頭節點了,則在連結串列的尾部加入新的定時器物件
srchTimer = srchTimer->next;
srchTimer->next = newTimer;
}
...
}
/////////////////////
//再回到定時器的計時更新函式裡:
void osalTimerUpdate( uint16 updateTime )
{
halIntState_t intState;
osalTimerRec_t *srchTimer;
osalTimerRec_t *prevTimer;
...
osal_systemClock += updateTime; //更新系統的計時時間
...
if ( timerHead != NULL ) //判斷定時器連結串列是否為空
{
srchTimer = timerHead;
prevTimer = (void *)NULL;
while ( srchTimer ) //遍歷定時器連結串列裡的每個定時器物件,檢查是否已超時
{
osalTimerRec_t *freeTimer = NULL;
if (srchTimer->timeout <= updateTime)
srchTimer->timeout = 0;
else
srchTimer->timeout = srchTimer->timeout - updateTime;
if ( (srchTimer->timeout == 0) && (srchTimer->reloadTimeout) && (srchTimer->event_flag) ) //重複性的定時器發生超時的操作
{
//下面這語句其實就是"tasksEvents[task_id] |= event_flag"
osal_set_event( srchTimer->task_id, srchTimer->event_flag );
srchTimer->timeout = srchTimer->reloadTimeout;//重新設定要計時的初始值
}
if ( srchTimer->timeout == 0 || srchTimer->event_flag == 0 ) //定時器超時,但沒有事件需要設定的操作
{
if ( prevTimer == NULL )
timerHead = srchTimer->next;
else
prevTimer->next = srchTimer->next;
freeTimer = srchTimer;
srchTimer = srchTimer->next;
}
else
{
prevTimer = srchTimer;
srchTimer = srchTimer->next; //移動到連結串列的下一個節點
}
...
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
定時2秒控制led燈的實現:
MyApp.c
#include "OnBoard.h"
#include "MyApp.h"
#include "hal_led.h"
#define TIME_OUT_EVENT 0x4
#define TIME_LEN 2000
uint8 mytask_id; //用於存放本身的任務號
void MyApp_Init(uint8 task_id )
{
mytask_id = task_id;
osal_start_reload_timer(task_id, TIME_OUT_EVENT, TIME_LEN);
}
uint16 MyApp_ProcessEvent(uint8 task_id, uint16 events )
{
if (events & TIME_OUT_EVENT)
{
HalLedSet(HAL_LED_ALL, HAL_LED_MODE_TOGGLE);
}
return 0;
}